diff options
37 files changed, 4784 insertions, 372 deletions
diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index ec3f8b4..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "tl-parser"] - path = tl-parser - url = https://github.com/vysheng/tl-parser diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..86a87c5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: c + +compiler: + - gcc + - clang + +script: + - git submodule update --init --recursive + - ./configure + - make diff --git a/Makefile.android b/Makefile.android new file mode 100644 index 0000000..1dcb9fe --- /dev/null +++ b/Makefile.android @@ -0,0 +1,134 @@ +srcdir=. + +# ANDROID Config ------------------------------------------------------------- +ANDROID_NDK= +ANDROID_API=android-18 +ARCH=arm +MACHINE=armv7-a + +CROSS_COMPILE_CC=${ANDROID_NDK}/toolchains/${ARCH}-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/${ARCH}-linux-androideabi-gcc +CROSS_COMPILE_AR=${ANDROID_NDK}/toolchains/${ARCH}-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/${ARCH}-linux-androideabi-ar +CROSS_COMPILE_LD=${ANDROID_NDK}/toolchains/${ARCH}-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/${ARCH}-linux-androideabi-ld + +TOOLCHAIN_CFLAGS=--sysroot=${ANDROID_NDK}/platforms/${ANDROID_API}/arch-${ARCH} -mandroid -march=${MACHINE} -Wa,--noexecstack + +TOOLCHAIN_LFLAGS=--sysroot=${ANDROID_NDK}/platforms/${ANDROID_API}/arch-${ARCH} -nostdlib +#-------------------------------------------------------------------- + +# OpenSSL -------------------------------------------------- +OPENSSL_ROOT= +OPENSSL_INCLUDE=-I${OPENSSL_ROOT}/include +OPENSSL_LIBS=-L${OPENSSL_ROOT}/android +#---------------------------------------------------------- + +#Cross Compiler Configs ------------------------------------------------------------- +CROSS_COMPILE_CFLAGS=-g -O2 +CROSS_COMPILE_CPPFLAGS= ${TOOLCHAIN_CFLAGS} ${OPENSSL_INCLUDE} +CROSS_COMPILE_DEFS=-DHAVE_CONFIG_H +CROSS_COMPILE_FLAGS=${CROSS_COMPILE_CFLAGS} ${CROSS_COMPILE_CPPFLAGS} ${CROSS_COMPILE_DEFS} -Wall -Wextra -Werror -Wno-deprecated-declarations -fno-strict-aliasing -fno-omit-frame-pointer -ggdb -Wno-unused-parameter -fPIC + +CROSS_COMPILE_LDFLAGS= ${TOOLCHAIN_LFLAGS} ${OPENSSL_LIBS} +CROSS_COMPILE_EXTRA_LIBS=-lm -lssl -lcrypto +CROSS_COMPILE_LOCAL_LDFLAGS=-rdynamic -ggdb ${CROSS_COMPILE_EXTRA_LIBS} +CROSS_COMPILE_LINK_FLAGS=${CROSS_COMPILE_LDFLAGS} ${CROSS_COMPILE_LOCAL_LDFLAGS} +# ----------------------------------------------------------------------------------- + +CFLAGS=-g -O2 +LDFLAGS= -L/usr/local/lib -L/usr/lib -L/usr/lib +CPPFLAGS= -I/usr/local/include -I/usr/include -I/usr/include +DEFS=-DHAVE_CONFIG_H +COMPILE_FLAGS=${CFLAGS} ${CPFLAGS} ${CPPFLAGS} ${DEFS} -Wall -Wextra -Werror -Wno-deprecated-declarations -fno-strict-aliasing -fno-omit-frame-pointer -ggdb -Wno-unused-parameter -fPIC + +EXTRA_LIBS=-lz -lm -lssl -lcrypto +LOCAL_LDFLAGS=-rdynamic -ggdb ${EXTRA_LIBS} +LINK_FLAGS=${LDFLAGS} ${LOCAL_LDFLAGS} + +DEP=dep +AUTO=auto +OBJ=objs +CROSS_OBJ=${OBJ}/cross_objs +LIB=libs +EXE=bin +DIR_LIST=${DEP} ${AUTO} ${EXE} ${OBJ} ${CROSS_OBJ} ${LIB} ${DEP}/auto ${CROSS_OBJ}/auto + +LIB_LIST=${LIB}/libtgl.a ${LIB}/libtgl.so + +TGL_OBJECTS=${CROSS_OBJ}/mtproto-common.o ${CROSS_OBJ}/mtproto-client.o ${CROSS_OBJ}/queries.o ${CROSS_OBJ}/structures.o ${CROSS_OBJ}/binlog.o ${CROSS_OBJ}/tgl.o ${CROSS_OBJ}/updates.o ${CROSS_OBJ}/tg-mime-types.o ${CROSS_OBJ}/mtproto-utils.o +TGL_OBJECTS_AUTO=${CROSS_OBJ}/auto/auto-skip.o ${CROSS_OBJ}/auto/auto-fetch.o ${CROSS_OBJ}/auto/auto-store.o ${CROSS_OBJ}/auto/auto-autocomplete.o ${CROSS_OBJ}/auto/auto-types.o ${CROSS_OBJ}/auto/auto-fetch-ds.o ${CROSS_OBJ}/auto/auto-free-ds.o ${CROSS_OBJ}/auto/auto-store-ds.o ${CROSS_OBJ}/auto/auto-print-ds.o +TLD_OBJECTS=${OBJ}/dump-tl-file.o +GENERATE_OBJECTS=${OBJ}/generate.o +COMMON_OBJECTS=${OBJ}/tools.o +CROSS_COMMON_OBJECTS=${CROSS_OBJ}/tools.o +OBJ_C=${GENERATE_OBJECTS} ${COMMON_OBJECTS} ${TLD_OBJECTS} +TGL_OBJECTS_C=${CROSS_COMMON_OBJECTS} ${TGL_OBJECTS} + +DEPENDENCE=$(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${OBJ_C})) +DEPENDENCE_LIST=${DEPENDENCE} + +INCLUDE=-I. -I${srcdir} +CC=gcc + +.SUFFIXES: + +.SUFFIXES: .c .h .o + +all: ${LIB_LIST} +create_dirs_and_headers: ${DIR_LIST} ${AUTO}/auto-skip.h ${AUTO}/auto-fetch.h ${AUTO}/auto-store.h ${AUTO}/auto-autocomplete.h ${AUTO}/auto-types.h +create_dirs: ${DIR_LIST} +dump-tl: ${EXE}/dump-tl-file + +include ${srcdir}/Makefile.tl-parser + +${DIR_LIST}: + @test -d $@ || mkdir -p $@ + +-include ${DEPENDENCE_LIST} + +${TGL_OBJECTS}: ${AUTO}/constants.h ${AUTO}/auto-skip.h ${AUTO}/auto-fetch.h ${AUTO}/auto-store.h ${AUTO}/auto-autocomplete.h ${AUTO}/auto-types.h ${AUTO}/auto-fetch-ds.h ${AUTO}/auto-free-ds.h ${AUTO}/auto-store-ds.h ${AUTO}/auto-print-ds.h + +${OBJ_C}: ${OBJ}/%.o: ${srcdir}/%.c | create_dirs + ${CC} ${INCLUDE} ${COMPILE_FLAGS} -c -MP -MD -MF ${DEP}/$*.d -MQ ${OBJ}/$*.o -o $@ $< + +${TGL_OBJECTS_C}: ${CROSS_OBJ}/%.o: ${srcdir}/%.c | create_dirs + ${CROSS_COMPILE_CC} ${INCLUDE} ${CROSS_COMPILE_FLAGS} -c -MP -MD -MF ${DEP}/$*.d -MQ ${CROSS_OBJ}/$*.o -o $@ $< + +${TGL_OBJECTS_AUTO}: ${CROSS_OBJ}/auto/%.o: ${AUTO}/%.c | create_dirs + ${CROSS_COMPILE_CC} ${INCLUDE} ${CROSS_COMPILE_FLAGS} -iquote ${srcdir}/tgl -c -MP -MD -MF ${DEP}/$*.d -MQ ${CROSS_OBJ}/$*.o -o $@ $< + +${LIB}/libtgl.a: ${TGL_OBJECTS} ${CROSS_COMMON_OBJECTS} ${TGL_OBJECTS_AUTO} + rm -f $@ && ${CROSS_COMPILE_AR} ruv $@ ${TGL_OBJECTS} ${CROSS_COMMON_OBJECTS} + +${LIB}/libtgl.so: ${TGL_OBJECTS} ${CROSS_COMMON_OBJECTS} ${TGL_OBJECTS_AUTO} + export LD=${CROSS_COMPILE_LD} && ${CROSS_COMPILE_CC} -shared -o $@ ${TGL_OBJECTS} ${CROSS_COMMON_OBJECTS} ${CROSS_COMPILE_LINK_FLAGS} + +${EXE}/generate: ${GENERATE_OBJECTS} ${COMMON_OBJECTS} + ${CC} ${GENERATE_OBJECTS} ${COMMON_OBJECTS} ${LINK_FLAGS} -o $@ + +${AUTO}/scheme.tlo: ${AUTO}/scheme.tl ${EXE}/tl-parser + ${EXE}/tl-parser -e $@ ${AUTO}/scheme.tl + +${AUTO}/scheme.tl: ${srcdir}/scheme.tl ${srcdir}/encrypted_scheme.tl ${srcdir}/binlog.tl ${srcdir}/mtproto.tl ${srcdir}/append.tl | ${AUTO} + cat $^ > $@ + +${AUTO}/scheme2.tl: ${AUTO}/scheme.tl ${EXE}/tl-parser + ${EXE}/tl-parser -E ${AUTO}/scheme.tl 2> $@ || ( cat $@ && rm $@ && false ) + +${AUTO}/auto.c: ${AUTO}/scheme.tlo ${EXE}/generate + ${EXE}/generate ${AUTO}/scheme.tlo > $@ + +${AUTO}/auto-%.c: ${AUTO}/scheme.tlo ${EXE}/generate auto/constants.h ${AUTO}/auto-%.h | create_dirs_and_headers + ${EXE}/generate -g $(patsubst ${AUTO}/auto-%.c,%,$@) ${AUTO}/scheme.tlo > $@ || rm $@ + +${AUTO}/auto-%.h: ${AUTO}/scheme.tlo ${EXE}/generate + ${EXE}/generate -g $(patsubst ${AUTO}/auto-%.h,%-header,$@) ${AUTO}/scheme.tlo > $@ || rm $@ + + +${AUTO}/constants.h: ${AUTO}/scheme2.tl ${srcdir}/gen_constants_h.awk + awk -f ${srcdir}/gen_constants_h.awk < $< > $@ + +${EXE}/dump-tl-file: ${OBJ}/auto/auto.o ${TLD_OBJECTS} + ${CC} ${OBJ}/auto/auto.o ${TLD_OBJECTS} ${LINK_FLAGS} -o $@ + +clean: + rm -rf ${DIR_LIST} config.log config.status > /dev/null || echo "all clean" + diff --git a/Makefile.in b/Makefile.in index f8b5154..c41fcdf 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,7 +4,7 @@ CFLAGS=@CFLAGS@ LDFLAGS=@LDFLAGS@ @OPENSSL_LDFLAGS@ CPPFLAGS=@CPPFLAGS@ @OPENSSL_INCLUDES@ DEFS=@DEFS@ -COMPILE_FLAGS=${CFLAGS} ${CPFLAGS} ${CPPFLAGS} ${DEFS} -Wall -Wextra -Werror -Wno-deprecated-declarations -fno-strict-aliasing -fno-omit-frame-pointer -ggdb -Wno-unused-parameter -fPIC +COMPILE_FLAGS=${CFLAGS} ${CPFLAGS} ${CPPFLAGS} ${DEFS} -Wall -Wextra -Wno-deprecated-declarations -fno-strict-aliasing -fno-omit-frame-pointer -ggdb -Wno-unused-parameter -fPIC -D_FORTIFY_SOURCE=2 EXTRA_LIBS=@LIBS@ @EXTRA_LIBS@ @OPENSSL_LIBS@ LOCAL_LDFLAGS=-rdynamic -ggdb ${EXTRA_LIBS} @@ -31,6 +31,13 @@ DEPENDENCE_LIST=${DEPENDENCE} INCLUDE=-I. -I${srcdir} CC=@CC@ +AR=@AR@ + +# Check if -fstack-protector-strong is supported before enabling it +SPUNSUPPORTED = $(shell $(CC) -fstack-protector-strong 2>&1 | grep -c 'stack-protector-strong') +ifeq "$(SPUNSUPPORTED)" "0" + COMPILE_FLAGS += -fstack-protector-strong +endif .SUFFIXES: @@ -57,7 +64,7 @@ ${TGL_OBJECTS_AUTO}: ${OBJ}/auto/%.o: ${AUTO}/%.c | create_dirs ${CC} ${INCLUDE} ${COMPILE_FLAGS} -iquote ${srcdir}/tgl -c -MP -MD -MF ${DEP}/$*.d -MQ ${OBJ}/$*.o -o $@ $< ${LIB}/libtgl.a: ${TGL_OBJECTS} ${COMMON_OBJECTS} ${TGL_OBJECTS_AUTO} - rm -f $@ && ar ruv $@ $^ + rm -f $@ && ${AR} ruv $@ $^ ${LIB}/libtgl.so: ${TGL_OBJECTS} ${COMMON_OBJECTS} ${TGL_OBJECTS_AUTO} ${CC} -shared -o $@ $^ ${LINK_FLAGS} @@ -97,6 +104,10 @@ distclean: rm -rf ${DIR_LIST} config.h config.log config.status mime-types.c: mime.types - @echo "// DO NOT EDIT! SEE MAKE-TARGET 'gen-mime-types'" > mime-types.c -# xxd is usually packaged with vim-common - xxd -i mime.types >> mime-types.c + @echo xxd -i mime.types > mime-types.c + @if command -v xxd >/dev/null 2>&1 ; then \ + xxd -i mime.types > mime-types.c ; \ + else \ + echo "# No xxd installed! Trying to skip ..." ; \ + test -r $@ ; \ + fi @@ -29,6 +29,18 @@ Then, ./configure make +#### Android +Install libs: openssl, openssl(android), zlib +if you want to use provided net/timers then install libevent and add --enable-libevent key to configure + +Then, + + export ANDROID_NDK=<Path_to_Android_NDK> + export OPENSSL_ROOT=<Path_to_Android_version_of_OpenSSL_root_dir> + ./configure + make -f Makefile.android + + ### Contacts If you would like to ask a question, you can write to my telegram or to the github (or both). To contact me via telegram, you should use import_card method with argument 000653bf:0738ca5d:5521fbac:29246815:a27d0cda diff --git a/auto-static-autocomplete.c b/auto-static-autocomplete.c index 71feb38..495fb1e 100644 --- a/auto-static-autocomplete.c +++ b/auto-static-autocomplete.c @@ -43,6 +43,7 @@ int tglf_extf_autocomplete (struct tgl_state *TLS, const char *text, int text_le #ifdef DISABLE_EXTF (void) free_vars_to_be_freed; assert (0); + return 0; #else if (index == -1) { buffer_pos = data; diff --git a/auto-static-fetch.c b/auto-static-fetch.c index 437fa1b..86d99cb 100644 --- a/auto-static-fetch.c +++ b/auto-static-fetch.c @@ -106,6 +106,7 @@ static void print_offset (void) { char *tglf_extf_fetch (struct tgl_state *TLS, struct paramed_type *T) { #ifdef DISABLE_EXTF assert (0); + return 0; #else out_buf_pos = 0; if (fetch_type_any (T) < 0) { return 0; } diff --git a/auto-static-skip.c b/auto-static-skip.c deleted file mode 100644 index e69de29..0000000 --- a/auto-static-skip.c +++ /dev/null diff --git a/auto-static-store.c b/auto-static-store.c index 616c49b..f70f077 100644 --- a/auto-static-store.c +++ b/auto-static-store.c @@ -253,6 +253,7 @@ void tgl_paramed_type_free (struct paramed_type *P); struct paramed_type *tglf_extf_store (struct tgl_state *TLS, const char *data, int data_len) { #ifdef DISABLE_EXTF assert (0); + return 0; #else buffer_pos = (char *)data; buffer_end = (char *)(data + data_len); @@ -105,7 +105,8 @@ void bl_do_set_auth_key (struct tgl_state *TLS, int num, unsigned char *buf) /* assert (num > 0 && num <= MAX_DC_ID); assert (TLS->DC_list[num]); - memcpy (TLS->DC_list[num]->auth_key, buf, 256); + if (TLS->DC_list[num]->auth_key != (char *)buf) + memcpy (TLS->DC_list[num]->auth_key, buf, 256); static unsigned char sha1_buffer[20]; TGLC_sha1 ((void *)TLS->DC_list[num]->auth_key, 256, sha1_buffer); diff --git a/config.h.in b/config.h.in index b242e93..ff0bf7d 100644 --- a/config.h.in +++ b/config.h.in @@ -39,8 +39,7 @@ /* Define to 1 if you have the <mach/mach.h> header file. */ #undef HAVE_MACH_MACH_H -/* Define to 1 if your system has a GNU libc compatible `malloc' function, and - to 0 otherwise. */ +/* Define to 1 if you have the `malloc' function. */ #undef HAVE_MALLOC /* Define to 1 if you have the <malloc.h> header file. */ @@ -64,8 +63,7 @@ /* Define to 1 if you have the <netinet/in.h> header file. */ #undef HAVE_NETINET_IN_H -/* Define to 1 if your system has a GNU libc compatible `realloc' function, - and to 0 otherwise. */ +/* Define to 1 if you have the `realloc' function. */ #undef HAVE_REALLOC /* Define to 1 if you have the `select' function. */ @@ -113,9 +111,6 @@ /* Define to 1 if you have the <unistd.h> header file. */ #undef HAVE_UNISTD_H -/* Define to 1 if the system has the `__builtin_bswap32' built-in function */ -#undef HAVE___BUILTIN_BSWAP32 - /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT @@ -153,12 +148,6 @@ #undef inline #endif -/* Define to rpl_malloc if the replacement function should be used. */ -#undef malloc - -/* Define to rpl_realloc if the replacement function should be used. */ -#undef realloc - /* Define to `unsigned int' if <sys/types.h> does not define. */ #undef size_t @@ -620,9 +620,9 @@ ac_includes_default="\ #endif" ac_subst_vars='LTLIBOBJS +LIBOBJS EXTRA_OBJECTS EXTRA_LIBS -LIBOBJS EGREP GREP CPP @@ -630,6 +630,7 @@ OPENSSL_LDFLAGS OPENSSL_LIBS OPENSSL_INCLUDES PKG_CONFIG +AR OBJEXT EXEEXT ac_ct_CC @@ -3099,6 +3100,98 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_AR" = x; then + AR="as_fn_error $? "No ar found. Huh?" "$LINENO" 5" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +else + AR="$ac_cv_prog_AR" +fi + # BSD locations for headers and libraries from packages, Linux locations for self-compiled stuff. CPPFLAGS="$CPPFLAGS -I/usr/local/include" @@ -4348,141 +4441,7 @@ esac # Checks for library functions. -for ac_header in stdlib.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" -if test "x$ac_cv_header_stdlib_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STDLIB_H 1 -_ACEOF - -fi - -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 -$as_echo_n "checking for GNU libc compatible malloc... " >&6; } -if ${ac_cv_func_malloc_0_nonnull+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - ac_cv_func_malloc_0_nonnull=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#if defined STDC_HEADERS || defined HAVE_STDLIB_H -# include <stdlib.h> -#else -char *malloc (); -#endif - -int -main () -{ -return ! malloc (0); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_malloc_0_nonnull=yes -else - ac_cv_func_malloc_0_nonnull=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5 -$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; } -if test $ac_cv_func_malloc_0_nonnull = yes; then : - -$as_echo "#define HAVE_MALLOC 1" >>confdefs.h - -else - $as_echo "#define HAVE_MALLOC 0" >>confdefs.h - - case " $LIBOBJS " in - *" malloc.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS malloc.$ac_objext" - ;; -esac - - -$as_echo "#define malloc rpl_malloc" >>confdefs.h - -fi - - -for ac_header in stdlib.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" -if test "x$ac_cv_header_stdlib_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STDLIB_H 1 -_ACEOF - -fi - -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible realloc" >&5 -$as_echo_n "checking for GNU libc compatible realloc... " >&6; } -if ${ac_cv_func_realloc_0_nonnull+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - ac_cv_func_realloc_0_nonnull=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#if defined STDC_HEADERS || defined HAVE_STDLIB_H -# include <stdlib.h> -#else -char *realloc (); -#endif - -int -main () -{ -return ! realloc (0, 0); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_realloc_0_nonnull=yes -else - ac_cv_func_realloc_0_nonnull=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_realloc_0_nonnull" >&5 -$as_echo "$ac_cv_func_realloc_0_nonnull" >&6; } -if test $ac_cv_func_realloc_0_nonnull = yes; then : - -$as_echo "#define HAVE_REALLOC 1" >>confdefs.h - -else - $as_echo "#define HAVE_REALLOC 0" >>confdefs.h - - case " $LIBOBJS " in - *" realloc.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS realloc.$ac_objext" - ;; -esac - - -$as_echo "#define realloc rpl_realloc" >>confdefs.h - -fi - - -for ac_func in alarm endpwent memset memmove mkdir select socket strdup strndup uname +for ac_func in alarm endpwent malloc memset memmove mkdir realloc select socket strdup strndup uname do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/configure.ac b/configure.ac index dab47aa..416c550 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,7 @@ m4_include([m4_ax_check_zlib.m4]) # Checks for programs. AC_PROG_CC +AC_CHECK_TOOL(AR, ar, [AC_MSG_ERROR([No ar found. Huh?])]) # BSD locations for headers and libraries from packages, Linux locations for self-compiled stuff. CPPFLAGS="$CPPFLAGS -I/usr/local/include" @@ -83,9 +84,7 @@ AC_TYPE_UID_T AC_C_INLINE # Checks for library functions. -AC_FUNC_MALLOC -AC_FUNC_REALLOC -AC_CHECK_FUNCS([alarm endpwent memset memmove mkdir select socket strdup strndup uname]) +AC_CHECK_FUNCS([alarm endpwent malloc memset memmove mkdir realloc select socket strdup strndup uname]) AC_SUBST(EXTRA_LIBS) AC_SUBST(EXTRA_OBJECTS) diff --git a/crypto/err.h b/crypto/err.h index ec6c6f6..efee156 100644 --- a/crypto/err.h +++ b/crypto/err.h @@ -25,4 +25,10 @@ void TGLC_err_print_errors_fp (FILE *fp); +// Don't want to include tgl.h just for this +struct tgl_state; + +// Init crypto backend, log to TLS +int TGLC_init (struct tgl_state *TLS); + #endif diff --git a/crypto/err_altern.c b/crypto/err_altern.c index 8921543..d8ade14 100644 --- a/crypto/err_altern.c +++ b/crypto/err_altern.c @@ -24,6 +24,8 @@ #include <gcrypt.h> +#include "../tgl.h" +#include "../tgl-inner.h" #include "err.h" void TGLC_err_print_errors_fp (FILE *fp) { @@ -31,4 +33,43 @@ void TGLC_err_print_errors_fp (FILE *fp) { (void) fp; } +int TGLC_init (struct tgl_state *TLS) { + vlogprintf (E_NOTICE, "Init gcrypt\n"); + // https://gnupg.org/documentation/manuals/gcrypt/Initializing-the-library.html + // https://lists.gnupg.org/pipermail/gcrypt-devel/2003-August/000458.html + + if (gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) { + // Someone else already *completed* it. + vlogprintf (E_NOTICE, "Init gcrypt: already initialized -- good\n"); + return 0; + } + + if (gcry_control (GCRYCTL_ANY_INITIALIZATION_P)) { + // Someone else already *started* it without *completing*. + vlogprintf (E_WARNING, "Init gcrypt: already started *but not completed* by third party -- bad\n"); + vlogprintf (E_WARNING, "Init gcrypt: ... not trying to init gcrypt then.\n"); + return 0; + } + + if (!gcry_check_version (GCRYPT_VERSION)) { + vlogprintf (E_ERROR, "Init gcrypt: version mismatch!\n"); + return -1; + } + + gcry_error_t err = gcry_control (GCRYCTL_DISABLE_SECMEM, NULL, 0); + if (err != GPG_ERR_NO_ERROR) { + vlogprintf (E_ERROR, "Init gcrypt: secmem failed?!\n"); + return -1; + } + + /* Tell Libgcrypt that initialization has completed. */ + err = gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + if (err != GPG_ERR_NO_ERROR) { + vlogprintf (E_ERROR, "Init gcrypt: init failed?!\n"); + return -1; + } + + return 0; +} + #endif diff --git a/crypto/err_openssl.c b/crypto/err_openssl.c index 563f6a8..b0b2271 100644 --- a/crypto/err_openssl.c +++ b/crypto/err_openssl.c @@ -24,10 +24,18 @@ #include <openssl/err.h> +#include "../tgl.h" +#include "../tgl-inner.h" #include "err.h" void TGLC_err_print_errors_fp (FILE *fp) { ERR_print_errors_fp (fp); } +int TGLC_init (struct tgl_state *TLS) { + // Doesn't seem to need any initialization. + vlogprintf (6, "Init OpenSSL (no-op)\n"); + return 0; +} + #endif diff --git a/crypto/rsa_pem_openssl.c b/crypto/rsa_pem_openssl.c index 32752d3..be657b3 100644 --- a/crypto/rsa_pem_openssl.c +++ b/crypto/rsa_pem_openssl.c @@ -31,6 +31,47 @@ #include "meta.h" #include "rsa_pem.h" +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2070000fL)) + +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + /* If the fields n and e in r are NULL, the corresponding input + * parameters MUST be non-NULL for n and e. d may be + * left NULL (in case only the public key is used). + */ + if ((r->n == NULL && n == NULL) + || (r->e == NULL && e == NULL)) + return 0; + + if (n != NULL) { + BN_free(r->n); + r->n = n; + } + if (e != NULL) { + BN_free(r->e); + r->e = e; + } + if (d != NULL) { + BN_free(r->d); + r->d = d; + } + + return 1; +} + +void RSA_get0_key(const RSA *r, + const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + if (n != NULL) + *n = r->n; + if (e != NULL) + *e = r->e; + if (d != NULL) + *d = r->d; +} + +#endif + TGLC_WRAPPER_ASSOC(rsa,RSA) // TODO: Refactor crucial struct-identity into its own header. @@ -38,19 +79,23 @@ TGLC_WRAPPER_ASSOC(bn,BIGNUM) TGLC_rsa *TGLC_rsa_new (unsigned long e, int n_bytes, const unsigned char *n) { RSA *ret = RSA_new (); - ret->e = unwrap_bn (TGLC_bn_new ()); - TGLC_bn_set_word (wrap_bn (ret->e), e); - ret->n = unwrap_bn (TGLC_bn_bin2bn (n, n_bytes, NULL)); + TGLC_bn* e_tglcbn = TGLC_bn_new (); + TGLC_bn_set_word (e_tglcbn, e); + RSA_set0_key(ret, unwrap_bn (TGLC_bn_bin2bn (n, n_bytes, NULL)), unwrap_bn(e_tglcbn), NULL); return wrap_rsa (ret); } -#define RSA_GETTER(M) \ - TGLC_bn *TGLC_rsa_ ## M (TGLC_rsa *key) { \ - return wrap_bn (unwrap_rsa (key)->M); \ - } \ +TGLC_bn *TGLC_rsa_n (TGLC_rsa * key) { + const BIGNUM *n; + RSA_get0_key( unwrap_rsa(key), &n, NULL, NULL); + return wrap_bn(n); +} -RSA_GETTER(n); -RSA_GETTER(e); +TGLC_bn *TGLC_rsa_e (TGLC_rsa * key) { + const BIGNUM *e; + RSA_get0_key( unwrap_rsa(key), NULL, &e, NULL); + return wrap_bn(e); +} void TGLC_rsa_free (TGLC_rsa *p) { RSA_free (unwrap_rsa (p)); @@ -2307,7 +2307,6 @@ void gen_skip_source (void) { printf ("#include <assert.h>\n"); printf ("#include \"auto/auto-skip.h\"\n"); - printf ("#include \"auto-static-skip.c\"\n"); printf ("#include \"mtproto-common.h\"\n"); int i, j; diff --git a/mtproto-client.c b/mtproto-client.c index 30e36a5..c4be958 100644 --- a/mtproto-client.c +++ b/mtproto-client.c @@ -678,11 +678,12 @@ static double get_server_time (struct tgl_dc *DC) { static long long generate_next_msg_id (struct tgl_state *TLS, struct tgl_dc *DC, struct tgl_session *S) { long long next_id = (long long) (get_server_time (DC) * (1LL << 32)) & -4; - if (next_id <= S->last_msg_id) { - next_id = S->last_msg_id += 4; + if (next_id <= TLS->last_msg_id) { + next_id = TLS->last_msg_id += 4; } else { - S->last_msg_id = next_id; + TLS->last_msg_id = next_id; } + S->last_msg_id = next_id; // See tglmp_encrypt_send_message return next_id; } @@ -754,7 +755,7 @@ long long tglmp_encrypt_send_message (struct tgl_state *TLS, struct connection * assert (l > 0); rpc_send_message (TLS, c, &enc_msg, l + UNENCSZ); - return S->last_msg_id; + return S->last_msg_id; // Pray that this was set by generate_next_msg_id somehow } int tglmp_encrypt_inner_temp (struct tgl_state *TLS, struct connection *c, int *msg, int msg_ints, int useful, void *data, long long msg_id) { diff --git a/mtproto-utils.c b/mtproto-utils.c index 0948bc8..1fea566 100644 --- a/mtproto-utils.c +++ b/mtproto-utils.c @@ -98,9 +98,7 @@ static unsigned long long BN2ull (TGLC_bn *b) { if (sizeof (unsigned long) == 8) { return TGLC_bn_get_word (b); } else if (sizeof (unsigned long long) == 8) { - assert (0); // As long as nobody ever uses this code, assume it is broken. unsigned long long tmp; - /* Here be dragons, but it should be okay due to be64toh */ TGLC_bn_bn2bin (b, (unsigned char *) &tmp); return be64toh (tmp); } else { @@ -112,9 +110,7 @@ static void ull2BN (TGLC_bn *b, unsigned long long val) { if (sizeof (unsigned long) == 8 || val < (1ll << 32)) { TGLC_bn_set_word (b, val); } else if (sizeof (unsigned long long) == 8) { - assert (0); // As long as nobody ever uses this code, assume it is broken. - htobe64(val); - /* Here be dragons, but it should be okay due to htobe64 */ + val = htobe64(val); TGLC_bn_bin2bn ((unsigned char *) &val, 8, b); } else { assert (0); @@ -31,6 +31,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <wordexp.h> #ifndef WIN32 #include <sys/utsname.h> #endif @@ -314,7 +315,7 @@ int tglq_query_error (struct tgl_state *TLS, long long id) { char *error = fetch_str (error_len); struct query *q = tglq_query_get (TLS, id); if (!q) { - vlogprintf (E_WARNING, "error for query '%s' #%" INT64_PRINTF_MODIFIER "d: #%d :%.*s\n", q->methods->name, id, error_code, error_len, error); + vlogprintf (E_WARNING, "error for query ?! #%" INT64_PRINTF_MODIFIER "d: #%d :%.*s\n", id, error_code, error_len, error); vlogprintf (E_WARNING, "No such query\n"); } else { if (!(q->flags & QUERY_ACK_RECEIVED)) { @@ -598,6 +599,20 @@ int utf8_len (const char *s, int len) { return r; } +static inline char ascii_char_norm (char c) { + return (c >= 0x41 && c <= 0x5A) ? c + 32 : c; +} + +static int ascii_cmp_nocase (const char *what, const char *with, size_t num) { + size_t i; + for (i = 0; i < num; i ++) { + if (ascii_char_norm (what[i]) != ascii_char_norm (with[i])) { + return 1; + } + } + return 0; +} + static char *process_html_text (struct tgl_state *TLS, const char *text, int text_len, int *ent_size, int **ent) { char *new_text = talloc (2 * text_len + 1); int stpos[100]; @@ -619,7 +634,7 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex return NULL; } int old_p = *ent_size; - if (text_len - p >= 3 && !memcmp (text + p, "<b>", 3)) { + if (text_len - p >= 3 && !ascii_cmp_nocase (text + p, "<b>", 3)) { increase_ent (ent_size, ent, 3); total ++; (*ent)[old_p] = CODE_message_entity_bold; @@ -630,7 +645,7 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex p += 2; continue; } - if (text_len - p >= 4 && !memcmp (text + p, "</b>", 4)) { + if (text_len - p >= 4 && !ascii_cmp_nocase (text + p, "</b>", 4)) { if (stp == 0 || sttype[stp - 1] != 0) { tgl_set_query_error (TLS, EINVAL, "Invalid tag nest"); tfree (new_text, 2 * text_len + 1); @@ -641,7 +656,7 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex p += 3; continue; } - if (text_len - p >= 3 && !memcmp (text + p, "<i>", 3)) { + if (text_len - p >= 3 && !ascii_cmp_nocase (text + p, "<i>", 3)) { increase_ent (ent_size, ent, 3); total ++; (*ent)[old_p] = CODE_message_entity_italic; @@ -652,7 +667,7 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex p += 2; continue; } - if (text_len - p >= 4 && !memcmp (text + p, "</i>", 4)) { + if (text_len - p >= 4 && !ascii_cmp_nocase (text + p, "</i>", 4)) { if (stp == 0 || sttype[stp - 1] != 1) { tgl_set_query_error (TLS, EINVAL, "Invalid tag nest"); tfree (new_text, 2 * text_len + 1); @@ -663,7 +678,7 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex p += 3; continue; } - if (text_len - p >= 6 && !memcmp (text + p, "<code>", 6)) { + if (text_len - p >= 6 && !ascii_cmp_nocase (text + p, "<code>", 6)) { increase_ent (ent_size, ent, 3); total ++; (*ent)[old_p] = CODE_message_entity_code; @@ -674,7 +689,7 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex p += 5; continue; } - if (text_len - p >= 7 && !memcmp (text + p, "</code>", 7)) { + if (text_len - p >= 7 && !ascii_cmp_nocase (text + p, "</code>", 7)) { if (stp == 0 || sttype[stp - 1] != 2) { tgl_set_query_error (TLS, EINVAL, "Invalid tag nest"); tfree (new_text, 2 * text_len + 1); @@ -685,12 +700,12 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex p += 6; continue; } - if (text_len - p >= 9 && !memcmp (text + p, "<a href=\"", 9)) { + if (text_len - p >= 9 && !ascii_cmp_nocase (text + p, "<a href=\"", 9)) { int pp = p + 9; while (pp < text_len && text[pp] != '"') { pp ++; } - if (pp == text_len || pp == text_len - 1 || text[pp + 1] != '>') { + if (pp == text_len || pp == text_len - 1) { tgl_set_query_error (TLS, EINVAL, "<a> tag did not close"); tfree (new_text, 2 * text_len + 1); return NULL; @@ -716,10 +731,13 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex memcpy (r + 1, text + p + 9, len); memset (r + 1 + len, 0, (-len-1) & 3); - p = pp + 1; + while (pp < text_len && text[pp] != '>') { + pp ++; + } + p = pp; continue; } - if (text_len - p >= 4 && !memcmp (text + p, "</a>", 4)) { + if (text_len - p >= 4 && !ascii_cmp_nocase (text + p, "</a>", 4)) { if (stp == 0 || sttype[stp - 1] != 3) { tgl_set_query_error (TLS, EINVAL, "Invalid tag nest"); tfree (new_text, 2 * text_len + 1); @@ -730,7 +748,7 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex p += 3; continue; } - if (text_len - p >= 4 && !memcmp (text + p, "<br>", 4)) { + if (text_len - p >= 4 && !ascii_cmp_nocase (text + p, "<br>", 4)) { new_text[cur_p ++] = '\n'; p += 3; continue; @@ -738,16 +756,19 @@ static char *process_html_text (struct tgl_state *TLS, const char *text, int tex tgl_set_query_error (TLS, EINVAL, "Unknown tag"); tfree (new_text, 2 * text_len + 1); return NULL; - } else if (text_len - p >= 4 && !memcmp (text + p, ">", 4)) { + } else if (text_len - p >= 4 && !ascii_cmp_nocase (text + p, ">", 4)) { p += 3; new_text[cur_p ++] = '>'; - } else if (text_len - p >= 4 && !memcmp (text + p, "<", 4)) { + } else if (text_len - p >= 4 && !ascii_cmp_nocase (text + p, "<", 4)) { p += 3; new_text[cur_p ++] = '<'; - } else if (text_len - p >= 5 && !memcmp (text + p, "&", 5)) { + } else if (text_len - p >= 5 && !ascii_cmp_nocase (text + p, "&", 5)) { p += 4; new_text[cur_p ++] = '&'; - } else if (text_len - p >= 6 && !memcmp (text + p, " ", 6)) { + } else if (text_len - p >= 6 && !ascii_cmp_nocase (text + p, """, 6)) { + p += 5; + new_text[cur_p ++] = '"'; + } else if (text_len - p >= 6 && !ascii_cmp_nocase (text + p, " ", 6)) { p += 5; new_text[cur_p ++] = 0xc2; new_text[cur_p ++] = 0xa0; @@ -1097,6 +1118,7 @@ void tgl_do_send_msg (struct tgl_state *TLS, struct tgl_message *M, void (*callb } } else { out_int (CODE_reply_keyboard_hide); + out_int (M->reply_markup->flags); } } @@ -1449,7 +1471,9 @@ struct get_history_extra { tgl_peer_id_t id; int limit; int offset; - int max_id; + int offset_id; + int min_id; + int is_range; }; static void _tgl_do_get_history (struct tgl_state *TLS, struct get_history_extra *E, void (*callback)(struct tgl_state *TLS,void *callback_extra, int success, int size, struct tgl_message *list[]), void *callback_extra); @@ -1493,10 +1517,16 @@ static int get_history_on_answer (struct tgl_state *TLS, struct query *q, void * E->limit = count - E->offset; if (E->limit < 0) { E->limit = 0; } } - assert (E->limit >= 0); + if (E->is_range > 0) { + if (n <= 0) { + E->limit = 0; // no messages left in the range + } else if (E->ML[E->list_offset - 1] && E->ML[E->list_offset - 1]->permanent_id.id <= E->min_id + 1) { + E->limit = 0; // offset_id lower than min_id + } + } - if (E->limit <= 0 || DS_MM->magic == CODE_messages_messages || DS_MM->magic == CODE_messages_channel_messages) { + if (E->limit <= 0 || DS_MM->magic == CODE_messages_messages) { if (q->callback) { ((void (*)(struct tgl_state *TLS, void *, int, int, struct tgl_message **))q->callback) (TLS, q->callback_extra, 1, E->list_offset, E->ML); } @@ -1507,8 +1537,9 @@ static int get_history_on_answer (struct tgl_state *TLS, struct query *q, void * tfree (E->ML, sizeof (void *) * E->list_size); tfree (E, sizeof (*E)); } else { + assert (E->list_offset > 0); E->offset = 0; - E->max_id = E->ML[E->list_offset - 1]->permanent_id.id; + E->offset_id = E->ML[E->list_offset - 1]->permanent_id.id; _tgl_do_get_history (TLS, E, q->callback, q->callback_extra); } return 0; @@ -1587,11 +1618,11 @@ static void _tgl_do_get_history (struct tgl_state *TLS, struct get_history_extra out_int (tgl_get_peer_id (E->id)); out_long (E->id.access_hash); } - out_int (E->max_id); + out_int (E->offset_id); out_int (E->offset); out_int (E->limit); out_int (0); - out_int (0); + out_int (E->min_id); tglq_send_query (TLS, TLS->DC_working, packet_ptr - packet_buffer, packet_buffer, &get_history_methods, E, callback, callback_extra); } @@ -1607,6 +1638,18 @@ void tgl_do_get_history (struct tgl_state *TLS, tgl_peer_id_t id, int offset, in E->offset = offset; _tgl_do_get_history (TLS, E, callback, callback_extra); } + +void tgl_do_get_history_range (struct tgl_state *TLS, tgl_peer_id_t id, int min_id, int max_id, int limit, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success, int size, struct tgl_message *list[]), void *callback_extra) { + + struct get_history_extra *E = talloc0 (sizeof (*E)); + E->id = id; + E->limit = limit; + E->offset_id = max_id; + E->min_id = min_id; + E->is_range = 1; + + _tgl_do_get_history (TLS, E, callback, callback_extra); +} /* }}} */ /* {{{ Get dialogs */ @@ -1863,7 +1906,6 @@ static void send_avatar_end (struct tgl_state *TLS, struct send_file *f, void *c break; case TGL_PEER_USER: out_int (CODE_photos_upload_profile_photo); - out_int (CODE_photos_upload_profile_photo); break; case TGL_PEER_CHANNEL: out_int (CODE_channels_edit_photo); @@ -2079,7 +2121,12 @@ static void send_file_thumb (struct tgl_state *TLS, struct send_file *f, const v static void _tgl_do_send_photo (struct tgl_state *TLS, tgl_peer_id_t to_id, const char *file_name, tgl_peer_id_t avatar, int w, int h, int duration, const void *thumb_data, int thumb_len, const char *caption, int caption_len, unsigned long long flags, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success, struct tgl_message *M), void *callback_extra) { - int fd = open (file_name, O_RDONLY | O_BINARY); + wordexp_t wrdexp; + int status = wordexp(file_name,&wrdexp,0); + int fd=-1; + if(status == 0) + fd = open (wrdexp.we_wordv[0], O_RDONLY | O_BINARY); + wordfree(&wrdexp); if (fd < 0) { tgl_set_query_error (TLS, EBADF, "Can not open file: %s", strerror(errno)); if (!avatar.peer_id) { @@ -2088,7 +2135,7 @@ static void _tgl_do_send_photo (struct tgl_state *TLS, tgl_peer_id_t to_id, cons } } else { if (callback) { - ((void (*)(struct tgl_state *, void *, int))callback) (TLS, callback_extra, 0); + ((void (*)(struct tgl_state *, void *, int, struct tgl_message*))callback) (TLS, callback_extra, 0, NULL); } } return; @@ -2105,7 +2152,7 @@ static void _tgl_do_send_photo (struct tgl_state *TLS, tgl_peer_id_t to_id, cons } } else { if (callback) { - ((void (*)(struct tgl_state *, void *, int))callback) (TLS, callback_extra, 0); + ((void (*)(struct tgl_state *, void *, int, struct tgl_message*))callback) (TLS, callback_extra, 0, NULL); } } return; @@ -2138,7 +2185,7 @@ static void _tgl_do_send_photo (struct tgl_state *TLS, tgl_peer_id_t to_id, cons } } else { if (callback) { - ((void (*)(struct tgl_state *, void *, int))callback) (TLS, callback_extra, 0); + ((void (*)(struct tgl_state *, void *, int, struct tgl_message*))callback) (TLS, callback_extra, 0, NULL); } } return; @@ -2528,7 +2575,7 @@ void tgl_do_send_contact (struct tgl_state *TLS, tgl_peer_id_t id, const char *p out_cstring (last_name, last_name_len); struct messages_send_extra *E = talloc0 (sizeof (*E)); - tglt_secure_random (&E->id, 8); + E->id = tgl_peer_id_to_random_msg_id (id); out_long (E->id.id); tglq_send_query (TLS, TLS->DC_working, packet_ptr - packet_buffer, packet_buffer, &send_msgs_methods, E, callback, callback_extra); @@ -2899,6 +2946,9 @@ void _tgl_do_channel_get_members (struct tgl_state *TLS, struct channel_get_mem case 3: out_int (CODE_channel_participants_kicked); break; + case 4: + out_int (CODE_channel_participants_bots); + break; default: out_int (CODE_channel_participants_recent); break; @@ -3205,7 +3255,7 @@ static void load_next_part (struct tgl_state *TLS, struct download *D, void *cal l = tsnprintf (buf, sizeof (buf), "%s/download_%" INT64_PRINTF_MODIFIER "d_%d.jpg", TLS->downloads_directory, D->volume, D->local_id); } else { if (D->ext) { - l = tsnprintf (buf, sizeof (buf), "%s/download_%" INT64_PRINTF_MODIFIER "d.%s", TLS->downloads_directory, D->id, D->ext); + l = tsnprintf (buf, sizeof (buf), "%s/%s" , TLS->downloads_directory, D->name); } else { l = tsnprintf (buf, sizeof (buf), "%s/download_%" INT64_PRINTF_MODIFIER "d", TLS->downloads_directory, D->id); } @@ -3301,6 +3351,13 @@ void tgl_do_load_file_location (struct tgl_state *TLS, struct tgl_file_location } void tgl_do_load_photo (struct tgl_state *TLS, struct tgl_photo *photo, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success, const char *filename), void *callback_extra) { + if (!photo) { + tgl_set_query_error (TLS, EINVAL, "Bad photo (invalid)"); + if (callback) { + callback (TLS, callback_extra, 0, 0); + } + return; + } if (!photo->sizes_num) { tgl_set_query_error (TLS, EINVAL, "Bad photo (no photo sizes"); if (callback) { @@ -3331,7 +3388,7 @@ static void _tgl_do_load_document (struct tgl_state *TLS, struct tgl_document *V D->id = V->id; D->access_hash = V->access_hash; D->dc = V->dc_id; - D->name = 0; + D->name = V->caption; D->fd = -1; if (V->mime_type) { @@ -3743,13 +3800,14 @@ static int get_difference_on_answer (struct tgl_state *TLS, struct query *q, voi } for (i = 0; i < ml_pos; i++) { + // Ignore invalid messages, would cause a null ptr deref otherwise + if (ML[i] == NULL) continue; bl_do_msg_update (TLS, &ML[i]->permanent_id); } for (i = 0; i < el_pos; i++) { // messages to secret chats that no longer exist are not initialized and NULL - if (EL[i]) { - bl_do_msg_update (TLS, &EL[i]->permanent_id); - } + if (EL[i] == NULL) continue; + bl_do_msg_update (TLS, &EL[i]->permanent_id); } tfree (ML, ml_pos * sizeof (void *)); @@ -451,7 +451,7 @@ chatInviteEmpty#69df3769 = ExportedChatInvite; chatInviteExported#fc2e05bc link:string = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; -chatInvite#93e99b60 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string = ChatInvite; +chatInvite#93e99b60 flags:# channel:flags.0?true broadcast:flags.1?true public_:flags.2?true megagroup:flags.3?true title:string = ChatInvite; inputStickerSetEmpty#ffb62b95 = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; @@ -720,4 +720,4 @@ channels.leaveChannel#f836aa95 channel:InputChannel = Updates; channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates; channels.kickFromChannel#a672de14 channel:InputChannel user_id:InputUser kicked:Bool = Updates; channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite; -channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; +channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
\ No newline at end of file diff --git a/structures.c b/structures.c index 3ccdfc0..290cc18 100644 --- a/structures.c +++ b/structures.c @@ -1,4 +1,4 @@ -/* +/* This file is part of tgl-library This library is free software; you can redistribute it and/or @@ -173,7 +173,7 @@ tgl_peer_id_t tglf_fetch_peer_id (struct tgl_state *TLS, struct tl_ds_peer *DS_P return TGL_MK_CHAT (DS_LVAL (DS_P->chat_id)); case CODE_peer_channel: return TGL_MK_CHANNEL (DS_LVAL (DS_P->channel_id)); - default: + default: assert (0); exit (2); } @@ -275,11 +275,11 @@ struct tgl_user *tglf_fetch_alloc_user (struct tgl_state *TLS, struct tl_ds_user if (!DS_U) { return 0; } if (DS_U->magic == CODE_user_empty) { return 0; - } - - tgl_peer_id_t user_id = TGL_MK_USER (DS_LVAL (DS_U->id)); + } + + tgl_peer_id_t user_id = TGL_MK_USER (DS_LVAL (DS_U->id)); user_id.access_hash = DS_LVAL (DS_U->access_hash); - + struct tgl_user *U = (struct tgl_user *)tgl_peer_get (TLS, user_id); if (!U) { TLS->users_allocated ++; @@ -289,7 +289,7 @@ struct tgl_user *tglf_fetch_alloc_user (struct tgl_state *TLS, struct tl_ds_user increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = (tgl_peer_t *)U; } - + int flags = U->flags; if (DS_LVAL (DS_U->flags) & (1 << 10)) { @@ -298,34 +298,36 @@ struct tgl_user *tglf_fetch_alloc_user (struct tgl_state *TLS, struct tl_ds_user } else { flags &= ~TGLUF_SELF; } - + if (DS_LVAL (DS_U->flags) & (1 << 11)) { flags |= TGLUF_CONTACT; } else { flags &= ~TGLUF_CONTACT; } - + if (DS_LVAL (DS_U->flags) & (1 << 12)) { flags |= TGLUF_MUTUAL_CONTACT; } else { flags &= ~TGLUF_MUTUAL_CONTACT; } - - + + if (DS_LVAL (DS_U->flags) & (1 << 14)) { flags |= TGLUF_BOT; + U->bot = 1; } else { flags &= ~TGLUF_BOT; + U->bot = 0; } /* if (DS_LVAL (DS_U->flags) & (1 << 15)) { flags |= TGLUF_BOT_FULL_ACCESS; } - + if (DS_LVAL (DS_U->flags) & (1 << 16)) { flags |= TGLUF_BOT_NO_GROUPS; }*/ - + if (DS_LVAL (DS_U->flags) & (1 << 17)) { flags |= TGLUF_OFFICIAL; } else { @@ -336,10 +338,10 @@ struct tgl_user *tglf_fetch_alloc_user (struct tgl_state *TLS, struct tl_ds_user flags |= TGLUF_CREATE | TGLUF_CREATED; } - bl_do_user (TLS, tgl_get_peer_id (U->id), + bl_do_user (TLS, tgl_get_peer_id (U->id), DS_U->access_hash, - DS_STR (DS_U->first_name), - DS_STR (DS_U->last_name), + DS_STR (DS_U->first_name), + DS_STR (DS_U->last_name), DS_STR (DS_U->phone), DS_STR (DS_U->username), NULL, @@ -348,17 +350,17 @@ struct tgl_user *tglf_fetch_alloc_user (struct tgl_state *TLS, struct tl_ds_user NULL, flags ); - + if (DS_U->status) { assert (tglf_fetch_user_status (TLS, &U->status, U, DS_U->status) >= 0); } - + if (DS_LVAL (DS_U->flags) & (1 << 13)) { if (!(U->flags & TGLUF_DELETED)) { bl_do_peer_delete (TLS, U->id); } } - + return U; } @@ -369,16 +371,16 @@ struct tgl_user *tglf_fetch_alloc_user_full (struct tgl_state *TLS, struct tl_ds if (!U) { return NULL; } int flags = U->flags; - + if (DS_BVAL (DS_UF->blocked)) { flags |= TGLUF_BLOCKED; } else { flags &= ~TGLUF_BLOCKED; } - bl_do_user (TLS, tgl_get_peer_id (U->id), + bl_do_user (TLS, tgl_get_peer_id (U->id), NULL, - NULL, 0, + NULL, 0, NULL, 0, NULL, 0, NULL, 0, @@ -416,9 +418,9 @@ struct tgl_secret_chat *tglf_fetch_alloc_encrypted_chat (struct tgl_state *TLS, return NULL; } - tgl_peer_id_t chat_id = TGL_MK_ENCR_CHAT (DS_LVAL (DS_EC->id)); + tgl_peer_id_t chat_id = TGL_MK_ENCR_CHAT (DS_LVAL (DS_EC->id)); chat_id.access_hash = DS_LVAL (DS_EC->access_hash); - + struct tgl_secret_chat *U = (void *)tgl_peer_get (TLS, chat_id); if (!U) { TLS->encr_chats_allocated ++; @@ -428,9 +430,9 @@ struct tgl_secret_chat *tglf_fetch_alloc_encrypted_chat (struct tgl_state *TLS, increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = (tgl_peer_t *)U; } - + int new = !(U->flags & TGLPF_CREATED); - + if (DS_EC->magic == CODE_encrypted_chat_discarded) { if (new) { vlogprintf (E_WARNING, "Unknown chat in deleted state. May be we forgot something...\n"); @@ -449,57 +451,57 @@ struct tgl_secret_chat *tglf_fetch_alloc_encrypted_chat (struct tgl_state *TLS, } str_to_256 (g_key, DS_STR (DS_EC->g_a)); - + int user_id = DS_LVAL (DS_EC->participant_id) + DS_LVAL (DS_EC->admin_id) - tgl_get_peer_id (TLS->our_id); int r = sc_request; - bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), + bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), DS_EC->access_hash, DS_EC->date, DS_EC->admin_id, &user_id, - NULL, + NULL, (void *)g_key, NULL, - &r, - NULL, NULL, NULL, NULL, NULL, - NULL, + &r, + NULL, NULL, NULL, NULL, NULL, + NULL, TGLECF_CREATE | TGLECF_CREATED, NULL, 0 ); } else { if (DS_EC->magic == CODE_encrypted_chat_waiting) { int r = sc_waiting; - bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), + bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), DS_EC->access_hash, DS_EC->date, NULL, NULL, - NULL, NULL, NULL, - &r, - NULL, NULL, NULL, NULL, NULL, - NULL, + NULL, + &r, + NULL, NULL, NULL, NULL, NULL, + NULL, TGL_FLAGS_UNCHANGED, NULL, 0 ); return U; // We needed only access hash from here } - + str_to_256 (g_key, DS_STR (DS_EC->g_a_or_b)); - + //write_secret_chat_file (); int r = sc_ok; - bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), + bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), DS_EC->access_hash, DS_EC->date, NULL, NULL, - NULL, + NULL, g_key, NULL, - &r, - NULL, NULL, NULL, NULL, NULL, + &r, + NULL, NULL, NULL, NULL, NULL, DS_EC->key_fingerprint, TGL_FLAGS_UNCHANGED, NULL, 0 @@ -511,15 +513,15 @@ struct tgl_secret_chat *tglf_fetch_alloc_encrypted_chat (struct tgl_state *TLS, struct tgl_chat *tglf_fetch_alloc_chat (struct tgl_state *TLS, struct tl_ds_chat *DS_C) { if (!DS_C) { return NULL; } - if (DS_C->magic == CODE_chat_empty) { + if (DS_C->magic == CODE_chat_empty) { return NULL; } if (DS_C->magic == CODE_channel || DS_C->magic == CODE_channel_forbidden) { return (void *)tglf_fetch_alloc_channel (TLS, DS_C); } - tgl_peer_id_t chat_id = TGL_MK_CHAT (DS_LVAL (DS_C->id)); + tgl_peer_id_t chat_id = TGL_MK_CHAT (DS_LVAL (DS_C->id)); chat_id.access_hash = 0; // chats don't have access hash - + struct tgl_chat *C = (void *)tgl_peer_get (TLS, chat_id); if (!C) { TLS->chats_allocated ++; @@ -529,9 +531,9 @@ struct tgl_chat *tglf_fetch_alloc_chat (struct tgl_state *TLS, struct tl_ds_chat increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = (tgl_peer_t *)C; } - + C->id = chat_id; - + int flags = C->flags; if (!(flags & TGLCF_CREATED)) { flags |= TGLCF_CREATE | TGLCF_CREATED; @@ -575,7 +577,7 @@ struct tgl_chat *tglf_fetch_alloc_chat (struct tgl_state *TLS, struct tl_ds_chat bl_do_chat (TLS, tgl_get_peer_id (C->id), DS_STR (DS_C->title), - DS_C->participants_count, + DS_C->participants_count, DS_C->date, NULL, NULL, @@ -593,7 +595,7 @@ struct tgl_chat *tglf_fetch_alloc_chat_full (struct tgl_state *TLS, struct tl_ds if (DS_MCF->full_chat->magic == CODE_channel_full) { return (void *)tglf_fetch_alloc_channel_full (TLS, DS_MCF); } - + if (DS_MCF->users) { int i; for (i = 0; i < DS_LVAL (DS_MCF->users->cnt); i++) { @@ -607,7 +609,7 @@ struct tgl_chat *tglf_fetch_alloc_chat_full (struct tgl_state *TLS, struct tl_ds tglf_fetch_alloc_chat (TLS, DS_MCF->chats->data[i]); } } - + struct tl_ds_chat_full *DS_CF = DS_MCF->full_chat; if (DS_CF->bot_info) { @@ -618,9 +620,9 @@ struct tgl_chat *tglf_fetch_alloc_chat_full (struct tgl_state *TLS, struct tl_ds tgl_peer_t *P = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_BI->user_id))); if (P && (P->flags & TGLCF_CREATED)) { - bl_do_user (TLS, tgl_get_peer_id (P->id), + bl_do_user (TLS, tgl_get_peer_id (P->id), NULL, - NULL, 0, + NULL, 0, NULL, 0, NULL, 0, NULL, 0, @@ -634,13 +636,13 @@ struct tgl_chat *tglf_fetch_alloc_chat_full (struct tgl_state *TLS, struct tl_ds } } - tgl_peer_id_t chat_id = TGL_MK_CHAT (DS_LVAL (DS_CF->id)); + tgl_peer_id_t chat_id = TGL_MK_CHAT (DS_LVAL (DS_CF->id)); struct tgl_chat *C = (void *)tgl_peer_get (TLS, chat_id); assert (C); bl_do_chat (TLS, tgl_get_peer_id (C->id), NULL, 0, - NULL, + NULL, NULL, DS_CF->participants->version, (struct tl_ds_vector *)DS_CF->participants->participants, @@ -657,9 +659,9 @@ struct tgl_chat *tglf_fetch_alloc_chat_full (struct tgl_state *TLS, struct tl_ds struct tgl_channel *tglf_fetch_alloc_channel (struct tgl_state *TLS, struct tl_ds_chat *DS_C) { if (!DS_C) { return NULL; } - - tgl_peer_id_t chat_id = TGL_MK_CHANNEL (DS_LVAL (DS_C->id)); - chat_id.access_hash = DS_LVAL (DS_C->access_hash); + + tgl_peer_id_t chat_id = TGL_MK_CHANNEL (DS_LVAL (DS_C->id)); + chat_id.access_hash = DS_LVAL (DS_C->access_hash); struct tgl_channel *C = (void *)tgl_peer_get (TLS, chat_id); if (!C) { @@ -670,14 +672,14 @@ struct tgl_channel *tglf_fetch_alloc_channel (struct tgl_state *TLS, struct tl_d increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = (tgl_peer_t *)C; } - + C->id = chat_id; - + int flags = C->flags; if (!(flags & TGLCHF_CREATED)) { flags |= TGLCHF_CREATE | TGLCHF_CREATED; } - + if (DS_LVAL (DS_C->flags) & 1) { flags |= TGLCHF_CREATOR; } else { @@ -744,7 +746,7 @@ struct tgl_channel *tglf_fetch_alloc_channel (struct tgl_state *TLS, struct tl_d struct tgl_channel *tglf_fetch_alloc_channel_full (struct tgl_state *TLS, struct tl_ds_messages_chat_full *DS_MCF) { if (!DS_MCF) { return NULL; } - + if (DS_MCF->users) { int i; for (i = 0; i < DS_LVAL (DS_MCF->users->cnt); i++) { @@ -795,7 +797,7 @@ void tglf_fetch_photo_size (struct tgl_state *TLS, struct tgl_photo_size *S, str S->size = DS_PS->bytes->len; } - tglf_fetch_file_location (TLS, &S->loc, DS_PS->location); + tglf_fetch_file_location (TLS, &S->loc, DS_PS->location); } void tglf_fetch_geo (struct tgl_state *TLS, struct tgl_geo *G, struct tl_ds_geo_point *DS_GP) { @@ -806,7 +808,7 @@ void tglf_fetch_geo (struct tgl_state *TLS, struct tgl_geo *G, struct tl_ds_geo_ struct tgl_photo *tglf_fetch_alloc_photo (struct tgl_state *TLS, struct tl_ds_photo *DS_P) { if (!DS_P) { return NULL; } if (DS_P->magic == CODE_photo_empty) { return NULL; } - + struct tgl_photo *P = tgl_photo_get (TLS, DS_LVAL (DS_P->id)); if (P) { P->refcnt ++; @@ -817,7 +819,7 @@ struct tgl_photo *tglf_fetch_alloc_photo (struct tgl_state *TLS, struct tl_ds_ph P = talloc0 (sizeof (*P)); P->id = DS_LVAL (DS_P->id); P->refcnt = 1; - + tgl_photo_insert (TLS, P); P->access_hash = DS_LVAL (DS_P->access_hash); @@ -827,7 +829,7 @@ struct tgl_photo *tglf_fetch_alloc_photo (struct tgl_state *TLS, struct tl_ds_ph /*if (DS_P->geo) { tglf_fetch_geo (TLS, &P->geo, DS_P->geo); }*/ - + P->sizes_num = DS_LVAL (DS_P->sizes->cnt); P->sizes = talloc (sizeof (struct tgl_photo_size) * P->sizes_num); int i; @@ -840,9 +842,9 @@ struct tgl_photo *tglf_fetch_alloc_photo (struct tgl_state *TLS, struct tl_ds_ph struct tgl_document *tglf_fetch_alloc_video (struct tgl_state *TLS, struct tl_ds_video *DS_V) { if (!DS_V) { return NULL; } - + if (DS_V->magic == CODE_video_empty) { return NULL; } - + struct tgl_document *D = tgl_document_get (TLS, DS_LVAL (DS_V->id)); if (D) { D->refcnt ++; @@ -853,7 +855,7 @@ struct tgl_document *tglf_fetch_alloc_video (struct tgl_state *TLS, struct tl_ds D = talloc0 (sizeof (*D)); D->id = DS_LVAL (DS_V->id); D->refcnt = 1; - + tgl_document_insert (TLS, D); D->flags = TGLDF_VIDEO; @@ -875,9 +877,9 @@ struct tgl_document *tglf_fetch_alloc_video (struct tgl_state *TLS, struct tl_ds struct tgl_document *tglf_fetch_alloc_audio (struct tgl_state *TLS, struct tl_ds_audio *DS_A) { if (!DS_A) { return NULL; } - + if (DS_A->magic == CODE_audio_empty) { return NULL; } - + struct tgl_document *D = tgl_document_get (TLS, DS_LVAL (DS_A->id)); if (D) { D->refcnt ++; @@ -888,11 +890,11 @@ struct tgl_document *tglf_fetch_alloc_audio (struct tgl_state *TLS, struct tl_ds D = talloc0 (sizeof (*D)); D->id = DS_LVAL (DS_A->id); D->refcnt = 1; - + tgl_document_insert (TLS, D); - + D->flags = TGLDF_AUDIO; - + D->access_hash = DS_LVAL (DS_A->access_hash); //D->user_id = DS_LVAL (DS_A->user_id); D->date = DS_LVAL (DS_A->date); @@ -937,9 +939,9 @@ void tglf_fetch_document_attribute (struct tgl_state *TLS, struct tgl_document * struct tgl_document *tglf_fetch_alloc_document (struct tgl_state *TLS, struct tl_ds_document *DS_D) { if (!DS_D) { return NULL; } - + if (DS_D->magic == CODE_document_empty) { return NULL; } - + struct tgl_document *D = tgl_document_get (TLS, DS_LVAL (DS_D->id)); if (D) { D->refcnt ++; @@ -950,7 +952,7 @@ struct tgl_document *tglf_fetch_alloc_document (struct tgl_state *TLS, struct tl D = talloc0 (sizeof (*D)); D->id = DS_LVAL (DS_D->id); D->refcnt = 1; - + tgl_document_insert (TLS, D); D->access_hash = DS_LVAL (DS_D->access_hash); @@ -974,7 +976,7 @@ struct tgl_document *tglf_fetch_alloc_document (struct tgl_state *TLS, struct tl struct tgl_webpage *tglf_fetch_alloc_webpage (struct tgl_state *TLS, struct tl_ds_web_page *DS_W) { if (!DS_W) { return NULL; } - + struct tgl_webpage *W = tgl_webpage_get (TLS, DS_LVAL (DS_W->id)); if (W) { W->refcnt ++; @@ -982,7 +984,7 @@ struct tgl_webpage *tglf_fetch_alloc_webpage (struct tgl_state *TLS, struct tl_d W = talloc0 (sizeof (*W)); W->id = DS_LVAL (DS_W->id); W->refcnt = 1; - + tgl_webpage_insert (TLS, W); } @@ -994,7 +996,7 @@ struct tgl_webpage *tglf_fetch_alloc_webpage (struct tgl_state *TLS, struct tl_d W->display_url = DS_STR_DUP (DS_W->display_url); } - if (!W->type) { + if (!W->type) { W->type = DS_STR_DUP (DS_W->type); } @@ -1037,7 +1039,7 @@ struct tgl_webpage *tglf_fetch_alloc_webpage (struct tgl_state *TLS, struct tl_d void tglf_fetch_message_action (struct tgl_state *TLS, struct tgl_message_action *M, struct tl_ds_message_action *DS_MA) { if (!DS_MA) { return; } memset (M, 0, sizeof (*M)); - + switch (DS_MA->magic) { case CODE_message_action_empty: M->type = tgl_message_action_none; @@ -1055,7 +1057,7 @@ void tglf_fetch_message_action (struct tgl_state *TLS, struct tgl_message_action { M->type = tgl_message_action_chat_create; M->title = DS_STR_DUP (DS_MA->title); - + M->user_num = DS_LVAL (DS_MA->users->cnt); M->users = talloc (M->user_num * 4); int i; @@ -1100,6 +1102,7 @@ void tglf_fetch_message_action (struct tgl_state *TLS, struct tgl_message_action break; case CODE_message_action_chat_migrate_to: M->type = tgl_message_action_migrated_to; + M->channel_id = DS_LVAL (DS_MA->channel_id); break; case CODE_message_action_channel_migrate_from: M->type = tgl_message_action_migrated_from; @@ -1116,7 +1119,7 @@ struct tgl_message *tglf_fetch_alloc_message_short (struct tgl_state *TLS, struc tgl_do_get_difference (TLS, 0, 0, 0); return NULL; } - + tgl_message_id_t msg_id = tgl_peer_id_to_msg_id (P->id, DS_LVAL (DS_U->id)); struct tgl_message *M = tgl_message_get (TLS, &msg_id); if (!M) { @@ -1129,7 +1132,7 @@ struct tgl_message *tglf_fetch_alloc_message_short (struct tgl_state *TLS, struc } int flags = M->flags & 0xffff; - + if (M->flags & TGLMF_PENDING) { M->flags ^= TGLMF_PENDING; } @@ -1163,7 +1166,7 @@ struct tgl_message *tglf_fetch_alloc_message_short (struct tgl_state *TLS, struc fwd_from_id = TGL_MK_USER (0); } - bl_do_edit_message (TLS, &msg_id, + bl_do_edit_message (TLS, &msg_id, (f & 2) ? &our_id : &peer_id, (f & 2) ? &peer_id : &our_id, DS_U->fwd_from_id ? &fwd_from_id : NULL, @@ -1173,7 +1176,7 @@ struct tgl_message *tglf_fetch_alloc_message_short (struct tgl_state *TLS, struc &A, NULL, DS_U->reply_to_msg_id, - NULL, + NULL, (void *)DS_U->entities, flags ); @@ -1191,7 +1194,7 @@ struct tgl_message *tglf_fetch_alloc_message_short_chat (struct tgl_state *TLS, tgl_do_get_difference (TLS, 0, 0, 0); return NULL; } - + tgl_message_id_t msg_id = tgl_peer_id_to_msg_id (T->id, DS_LVAL (DS_U->id)); struct tgl_message *M = tgl_message_get (TLS, &msg_id); if (!M) { @@ -1202,7 +1205,7 @@ struct tgl_message *tglf_fetch_alloc_message_short_chat (struct tgl_state *TLS, } int flags = M->flags & 0xffff; - + if (M->flags & TGLMF_PENDING) { M->flags ^= TGLMF_PENDING; } @@ -1303,13 +1306,14 @@ void tglf_fetch_message_media (struct tgl_state *TLS, struct tgl_message_media * M->venue.title = DS_STR_DUP (DS_MM->title); M->venue.address = DS_STR_DUP (DS_MM->address); M->venue.provider = DS_STR_DUP (DS_MM->provider); - M->venue.venue_id = DS_STR_DUP (DS_MM->venue_id); + M->venue.venue_id = DS_STR_DUP (DS_MM->venue_id); break; case CODE_message_media_unsupported: M->type = tgl_message_media_unsupported; break; default: - assert (0); + M->type = tgl_message_media_unsupported; + break; } } @@ -1329,9 +1333,9 @@ void tglf_fetch_message_media_encrypted (struct tgl_state *TLS, struct tgl_messa case CODE_decrypted_message_media_audio: //M->type = CODE_decrypted_message_media_video; M->type = tgl_message_media_document_encr; - + M->encr_document = talloc0 (sizeof (*M->encr_document)); - + switch (DS_DMM->magic) { case CODE_decrypted_message_media_photo: M->encr_document->flags = TGLDF_IMAGE; @@ -1347,13 +1351,13 @@ void tglf_fetch_message_media_encrypted (struct tgl_state *TLS, struct tgl_messa M->encr_document->flags = TGLDF_AUDIO; break; } - + M->encr_document->w = DS_LVAL (DS_DMM->w); M->encr_document->h = DS_LVAL (DS_DMM->h); M->encr_document->size = DS_LVAL (DS_DMM->size); M->encr_document->duration = DS_LVAL (DS_DMM->duration); M->encr_document->mime_type = DS_STR_DUP (DS_DMM->mime_type); - + M->encr_document->key = talloc (32); str_to_32 (M->encr_document->key, DS_STR (DS_DMM->key)); M->encr_document->iv = talloc (32); @@ -1378,17 +1382,17 @@ void tglf_fetch_message_media_encrypted (struct tgl_state *TLS, struct tgl_messa void tglf_fetch_message_action_encrypted (struct tgl_state *TLS, struct tgl_message_action *M, struct tl_ds_decrypted_message_action *DS_DMA) { if (!DS_DMA) { return; } - + switch (DS_DMA->magic) { case CODE_decrypted_message_action_set_message_t_t_l: M->type = tgl_message_action_set_message_ttl; M->ttl = DS_LVAL (DS_DMA->ttl_seconds); break; - case CODE_decrypted_message_action_read_messages: + case CODE_decrypted_message_action_read_messages: M->type = tgl_message_action_read_messages; - { + { M->read_cnt = DS_LVAL (DS_DMA->random_ids->cnt); - + int i; for (i = 0; i < M->read_cnt; i++) { tgl_message_id_t id; @@ -1401,16 +1405,16 @@ void tglf_fetch_message_action_encrypted (struct tgl_state *TLS, struct tgl_mess } } break; - case CODE_decrypted_message_action_delete_messages: + case CODE_decrypted_message_action_delete_messages: M->type = tgl_message_action_delete_messages; break; - case CODE_decrypted_message_action_screenshot_messages: + case CODE_decrypted_message_action_screenshot_messages: M->type = tgl_message_action_screenshot_messages; - { + { M->screenshot_cnt = DS_LVAL (DS_DMA->random_ids->cnt); } break; - case CODE_decrypted_message_action_notify_layer: + case CODE_decrypted_message_action_notify_layer: M->type = tgl_message_action_notify_layer; M->layer = DS_LVAL (DS_DMA->layer); break; @@ -1431,14 +1435,14 @@ void tglf_fetch_message_action_encrypted (struct tgl_state *TLS, struct tgl_mess break; case CODE_decrypted_message_action_request_key: M->type = tgl_message_action_request_key; - + M->exchange_id = DS_LVAL (DS_DMA->exchange_id); M->g_a = talloc (256); str_to_256 (M->g_a, DS_STR (DS_DMA->g_a)); break; case CODE_decrypted_message_action_accept_key: M->type = tgl_message_action_accept_key; - + M->exchange_id = DS_LVAL (DS_DMA->exchange_id); M->g_a = talloc (256); str_to_256 (M->g_a, DS_STR (DS_DMA->g_b)); @@ -1446,13 +1450,13 @@ void tglf_fetch_message_action_encrypted (struct tgl_state *TLS, struct tgl_mess break; case CODE_decrypted_message_action_commit_key: M->type = tgl_message_action_commit_key; - + M->exchange_id = DS_LVAL (DS_DMA->exchange_id); M->key_fingerprint = DS_LVAL (DS_DMA->key_fingerprint); break; case CODE_decrypted_message_action_abort_key: M->type = tgl_message_action_abort_key; - + M->exchange_id = DS_LVAL (DS_DMA->exchange_id); break; default: @@ -1518,11 +1522,11 @@ struct tgl_message *tglf_fetch_alloc_message (struct tgl_state *TLS, struct tl_d *new_msg = 0; } //assert (0); - if (!DS_M || DS_M->magic == CODE_message_empty) { + if (!DS_M || DS_M->magic == CODE_message_empty) { vlogprintf (E_NOTICE, "empty message\n"); - return NULL; + return NULL; } - + tgl_peer_id_t to_id = tglf_fetch_peer_id (TLS, DS_M->to_id); tgl_peer_t *T = tgl_peer_get (TLS, to_id); if (!T || !(T->flags & TGLPF_CREATED)) { @@ -1544,7 +1548,7 @@ struct tgl_message *tglf_fetch_alloc_message (struct tgl_state *TLS, struct tl_d P = F; } } - + tgl_peer_t *FF = NULL; if (DS_M->fwd_from_id) { @@ -1584,14 +1588,14 @@ struct tgl_message *tglf_fetch_alloc_message (struct tgl_state *TLS, struct tl_d if (DS_LVAL (DS_M->flags) & 16) { flags |= TGLMF_MENTION; } - + tgl_peer_id_t from_id; if (DS_M->from_id) { from_id = F->id; } else { from_id = TGL_MK_USER (0); } - + tgl_peer_id_t to_id = T->id; tgl_peer_id_t fwd_from_id; @@ -1630,7 +1634,7 @@ static int decrypt_encrypted_message (struct tgl_secret_chat *E) { static unsigned char sha1b_buffer[20]; static unsigned char sha1c_buffer[20]; static unsigned char sha1d_buffer[20]; - + static unsigned char buf[64]; int *e_key = E->exchange_state != tgl_sce_committed ? E->key : E->exchange_key; @@ -1638,16 +1642,16 @@ static int decrypt_encrypted_message (struct tgl_secret_chat *E) { memcpy (buf, msg_key, 16); memcpy (buf + 16, e_key, 32); TGLC_sha1 (buf, 48, sha1a_buffer); - + memcpy (buf, e_key + 8, 16); memcpy (buf + 16, msg_key, 16); memcpy (buf + 32, e_key + 12, 16); TGLC_sha1 (buf, 48, sha1b_buffer); - + memcpy (buf, e_key + 16, 32); memcpy (buf + 32, msg_key, 16); TGLC_sha1 (buf, 48, sha1c_buffer); - + memcpy (buf, msg_key, 16); memcpy (buf + 16, e_key + 24, 32); TGLC_sha1 (buf, 48, sha1d_buffer); @@ -1683,7 +1687,7 @@ static int decrypt_encrypted_message (struct tgl_secret_chat *E) { struct tgl_message *tglf_fetch_encrypted_message (struct tgl_state *TLS, struct tl_ds_encrypted_message *DS_EM) { if (!DS_EM) { return NULL; } - + tgl_peer_t *P = tgl_peer_get (TLS, TGL_MK_ENCR_CHAT (DS_LVAL (DS_EM->chat_id))); if (!P || P->encr_chat.state != sc_ok) { vlogprintf (E_WARNING, "Encrypted message to unknown chat. Dropping\n"); @@ -1707,31 +1711,31 @@ struct tgl_message *tglf_fetch_encrypted_message (struct tgl_state *TLS, struct decr_ptr = (void *)DS_EM->bytes->data; decr_end = decr_ptr + (DS_EM->bytes->len / 4); - + if (P->encr_chat.exchange_state == tgl_sce_committed && P->encr_chat.key_fingerprint == *(long long *)decr_ptr) { tgl_do_confirm_exchange (TLS, (void *)P, 0); assert (P->encr_chat.exchange_state == tgl_sce_none); } - + long long key_fingerprint = P->encr_chat.exchange_state != tgl_sce_committed ? P->encr_chat.key_fingerprint : P->encr_chat.exchange_key_fingerprint; if (*(long long *)decr_ptr != key_fingerprint) { vlogprintf (E_WARNING, "Encrypted message with bad fingerprint to chat %s\n", P->print_name); return M; } - + decr_ptr += 2; if (decrypt_encrypted_message (&P->encr_chat) < 0) { vlogprintf (E_WARNING, "can not decrypt message\n"); return M; } - + int *save_in_ptr = in_ptr; int *save_in_end = in_end; - + in_ptr = decr_ptr; int ll = *in_ptr; - in_end = in_ptr + ll / 4 + 1; + in_end = in_ptr + ll / 4 + 1; assert (fetch_int () == ll); if (skip_type_decrypted_message_layer (TYPE_TO_PARAM (decrypted_message_layer)) < 0 || in_ptr != in_end) { @@ -1767,8 +1771,8 @@ struct tgl_message *tglf_fetch_encrypted_message (struct tgl_state *TLS, struct free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); return M; } - - if ((in_seq_no & 1) != 1 - (P->encr_chat.admin_id == tgl_get_peer_id (TLS->our_id)) || + + if ((in_seq_no & 1) != 1 - (P->encr_chat.admin_id == tgl_get_peer_id (TLS->our_id)) || (out_seq_no & 1) != (P->encr_chat.admin_id == tgl_get_peer_id (TLS->our_id))) { vlogprintf (E_WARNING, "Bad msg admin\n"); free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); @@ -1808,7 +1812,7 @@ struct tgl_message *tglf_fetch_encrypted_message (struct tgl_state *TLS, struct ); assert (P->encr_chat.in_seq_no == in_seq_no); } - + free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); return M; } @@ -1899,7 +1903,7 @@ struct tgl_message *tglf_fetch_alloc_encrypted_message (struct tgl_state *TLS, s ); } if (M->action.type == tgl_message_action_set_message_ttl) { - //bl_do_encr_chat_set_ttl (TLS, E, M->action.ttl); + //bl_do_encr_chat_set_ttl (TLS, E, M->action.ttl); bl_do_encr_chat (TLS, tgl_get_peer_id (E->id), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -2041,7 +2045,7 @@ void tgls_free_document (struct tgl_state *TLS, struct tgl_document *D) { if (D->mime_type) { tfree_str (D->mime_type);} if (D->caption) {tfree_str (D->caption);} tgls_free_photo_size (TLS, &D->thumb); - + TLS->document_tree = tree_delete_document (TLS->document_tree, D); tfree (D, sizeof (*D)); } @@ -2061,7 +2065,7 @@ void tgls_free_webpage (struct tgl_state *TLS, struct tgl_webpage *W) { if (W->embed_url) { tfree_str (W->embed_url); } if (W->embed_type) { tfree_str (W->embed_type); } if (W->author) { tfree_str (W->author); } - + TLS->webpage_tree = tree_delete_webpage (TLS->webpage_tree, W); tfree (W, sizeof (*W)); } @@ -2072,7 +2076,7 @@ void tgls_free_message_media (struct tgl_state *TLS, struct tgl_message_media *M case tgl_message_media_geo: return; case tgl_message_media_photo: - tgls_free_photo (TLS, M->photo); + if (M->photo) { tgls_free_photo (TLS, M->photo); } if (M->caption) { tfree_str (M->caption); } M->photo = NULL; return; @@ -2181,7 +2185,7 @@ void tgls_clear_message (struct tgl_state *TLS, struct tgl_message *M) { tfree (M->entities, M->entities_num * sizeof (struct tgl_message_entity)); } -void tgls_free_reply_markup (struct tgl_state *TLS, struct tgl_message_reply_markup *R) { +void tgls_free_reply_markup (struct tgl_state *TLS, struct tgl_message_reply_markup *R) { if (!--R->refcnt) { int i; for (i = 0; i < R->row_start[R->rows]; i++) { @@ -2229,7 +2233,7 @@ void tgls_free_user (struct tgl_state *TLS, struct tgl_user *U) { void tgls_free_encr_chat (struct tgl_state *TLS, struct tgl_secret_chat *U) { if (U->print_name) { tfree_str (U->print_name); } - if (U->g_key) { tfree (U->g_key, 256); } + if (U->g_key) { tfree (U->g_key, 256); } tfree (U, sizeof (tgl_peer_t)); } @@ -2325,7 +2329,7 @@ void tglm_message_add_peer (struct tgl_state *TLS, struct tgl_message *M) { N = N->next; } if (N) { - assert (N->permanent_id.id < M->permanent_id.id); + assert (N->permanent_id.id < M->permanent_id.id); } M->next = N; M->prev = NP; @@ -2571,11 +2575,22 @@ void tgls_free_message_gw (struct tgl_message *M, void *TLS) { tgls_free_message (TLS, M); } +void tgls_remove_message_gw (struct tgl_state *TLS, struct tgl_message *M) { + if (tree_lookup_message (TLS->message_unsent_tree, M)) { + tglm_message_remove_unsent (TLS, M); + } +} + +void tgls_remove_and_free_message_gw (struct tgl_message *M, void *TLS) { + tgls_remove_message_gw (TLS, M); + tgls_free_message (TLS, M); +} + void tgl_free_all (struct tgl_state *TLS) { tree_act_ex_peer (TLS->peer_tree, tgls_free_peer_gw, TLS); TLS->peer_tree = tree_clear_peer (TLS->peer_tree); TLS->peer_by_name_tree = tree_clear_peer_by_name (TLS->peer_by_name_tree); - tree_act_ex_message (TLS->message_tree, tgls_free_message_gw, TLS); + tree_act_ex_message (TLS->message_tree, tgls_remove_and_free_message_gw, TLS); TLS->message_tree = tree_clear_message (TLS->message_tree); tree_act_ex_message (TLS->message_unsent_tree, tgls_free_message_gw, TLS); TLS->message_unsent_tree = tree_clear_message (TLS->message_unsent_tree); @@ -2616,7 +2631,7 @@ void tgl_free_all (struct tgl_state *TLS) { } int tgl_print_stat (struct tgl_state *TLS, char *s, int len) { - return tsnprintf (s, len, + return tsnprintf (s, len, "users_allocated\t%d\n" "chats_allocated\t%d\n" "encr_chats_allocated\t%d\n" @@ -2647,13 +2662,13 @@ void tglf_fetch_int_tuple (int *dst, int **src, int len) { void tgls_messages_mark_read (struct tgl_state *TLS, struct tgl_message *M, int out, int seq) { - while (M && M->permanent_id.id > seq) { + while (M && M->permanent_id.id > seq) { if ((M->flags & TGLMF_OUT) == out) { if (!(M->flags & TGLMF_UNREAD)) { return; } } - M = M->next; + M = M->next; } while (M) { if ((M->flags & TGLMF_OUT) == out) { @@ -2664,10 +2679,10 @@ void tgls_messages_mark_read (struct tgl_state *TLS, struct tgl_message *M, int return; } } - M = M->next; + M = M->next; } } - + /* void tgls_insert_random2local (struct tgl_state *TLS, long long random_id, tgl_message_id_t *msg_id) { struct random2local *X = talloc (sizeof (*X)); @@ -2676,7 +2691,7 @@ void tgls_insert_random2local (struct tgl_state *TLS, long long random_id, tgl_m struct random2local *R = tree_lookup_random_id (TLS->random_id_tree, X); assert (!R); - + TLS->random_id_tree = tree_insert_random_id (TLS->random_id_tree, X, rand ()); } @@ -2684,14 +2699,14 @@ tgl_message_id_t *tgls_get_local_by_random (struct tgl_state *TLS, long long ran struct random2local X; X.random_id = random_id; struct random2local *Y = tree_lookup_random_id (TLS->random_id_tree, &X); - if (Y) { + if (Y) { //TLS->random_id_tree = tree_delete_random_id (TLS->random_id_tree, Y); return &Y->local_id; } else { return NULL; } } - + void tgls_insert_temp2local (struct tgl_state *TLS, int temp_id, tgl_message_id_t *msg_id) { struct random2local *X = talloc (sizeof (*X)); X->random_id = temp_id; @@ -2699,7 +2714,7 @@ void tgls_insert_temp2local (struct tgl_state *TLS, int temp_id, tgl_message_id_ struct random2local *R = tree_lookup_random_id (TLS->temp_id_tree, X); assert (!R); - + TLS->temp_id_tree = tree_insert_random_id (TLS->temp_id_tree, X, rand ()); } }*/ diff --git a/tgl-layout.h b/tgl-layout.h index c821668..9829b88 100644 --- a/tgl-layout.h +++ b/tgl-layout.h @@ -1,4 +1,4 @@ -/* +/* This file is part of tgl-library This library is free software; you can redistribute it and/or @@ -120,9 +120,9 @@ #define TGL_PERMANENT_ID_SIZE 24 #pragma pack(push,4) -typedef struct { - int peer_type; - int peer_id; +typedef struct { + int peer_type; + int peer_id; long long access_hash; } tgl_peer_id_t; @@ -144,7 +144,7 @@ enum tgl_dc_state { struct tgl_session { struct tgl_dc *dc; long long session_id; - long long last_msg_id; + long long last_msg_id; // See tglmp_encrypt_send_message int seq_no; int received_messages; struct connection *c; @@ -309,7 +309,7 @@ struct tgl_encr_document { int size; int key_fingerprint; int flags; - + unsigned char *key; unsigned char *iv; int w; @@ -361,7 +361,7 @@ struct tgl_user { int blocked; char *real_first_name; char *real_last_name; - + int bot; struct tgl_bot_info *bot_info; }; @@ -531,6 +531,7 @@ struct tgl_message_action { int read_cnt; int delete_cnt; int screenshot_cnt; + int channel_id; enum tgl_typing_status typing; struct { int start_seq_no; @@ -573,7 +574,7 @@ struct tgl_message_media { }; char *caption; }; - + struct tgl_encr_document *encr_document; struct tgl_webpage *webpage; @@ -584,7 +585,7 @@ struct tgl_message_media { char *last_name; int user_id; }; - + struct { void *data; int data_size; @@ -599,13 +600,13 @@ struct tgl_message_media { }; }; -struct tgl_message_reply_markup { +typedef struct tgl_message_reply_markup { int refcnt; int flags; int rows; int *row_start; char **buttons; -}; +} tgl_message_reply_markup; typedef struct tgl_message_id { unsigned peer_type; diff --git a/tgl-queries.h b/tgl-queries.h index 3921455..0fdd26b 100644 --- a/tgl-queries.h +++ b/tgl-queries.h @@ -3,6 +3,10 @@ #include "tgl.h" +#ifdef __cplusplus +extern "C" { +#endif + void tgl_do_get_terms_of_service (struct tgl_state *TLS, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success, const char *ans), void *callback_extra); /* {{{ WORK WITH ACCOUNT */ @@ -121,7 +125,7 @@ void tgl_do_export_chat_link (struct tgl_state *TLS, tgl_peer_id_t id, void (*ca // joins to secret chat by link (or hash of this link) void tgl_do_import_chat_link (struct tgl_state *TLS, const char *link, int link_len, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success), void *callback_extra); -// upgrades chat to channel. +// upgrades chat to channel. void tgl_do_upgrade_group (struct tgl_state *TLS, tgl_peer_id_t id, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success), void *callback_extra); /* }}} */ @@ -236,6 +240,12 @@ void tgl_do_messages_mark_read (struct tgl_state *TLS, tgl_peer_id_t id, int max // also marks messages from this chat as read void tgl_do_get_history (struct tgl_state *TLS, tgl_peer_id_t id, int offset, int limit, int offline_mode, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success, int size, struct tgl_message *list[]), void *callback_extra); +// like tgl_do_get_history, but will fetch any messages between *min_id* and *max_id* +// when *limit* is too small, the older messages will not be displayed despite being bigger than *min_id* +// when *max_id* is 0, all messages newer than *min_id* are fetched +// when *min_id* is 0, all messages older than *max_id* are fetched +void tgl_do_get_history_range (struct tgl_state *TLS, tgl_peer_id_t id, int min_id, int max_id, int limit, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success, int size, struct tgl_message *list[]), void *callback_extra); + // sends typing event to chat // set status=tgl_typing_typing for default typing event void tgl_do_send_typing (struct tgl_state *TLS, tgl_peer_id_t id, enum tgl_typing_status status, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success), void *callback_extra); @@ -293,4 +303,8 @@ char *tglf_extf_fetch (struct tgl_state *TLS, struct paramed_type *T); void tgl_do_start_bot (struct tgl_state *TLS, tgl_peer_id_t bot, tgl_peer_id_t chat, const char *str, int str_len, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success), void *callback_extra); /* }}} */ +#ifdef __cplusplus +} +#endif + #endif @@ -22,6 +22,7 @@ #include "config.h" #endif +#include "crypto/err.h" #include "crypto/rsa_pem.h" #include "tgl.h" #include "tools.h" @@ -82,10 +83,14 @@ int tgl_init (struct tgl_state *TLS) { TLS->message_list.next_use = &TLS->message_list; TLS->message_list.prev_use = &TLS->message_list; + if (TGLC_init (TLS) != 0) { + return -1; + } + if (tglmp_on_start (TLS) < 0) { return -1; } - + if (!TLS->app_id) { TLS->app_id = TG_APP_ID; TLS->app_hash = tstrdup (TG_APP_HASH); @@ -210,7 +210,7 @@ struct tgl_state { long long rsa_key_fingerprint[TGL_MAX_RSA_KEYS_NUM]; int rsa_key_num; - TGLC_bn_ctx *TGLC_bn_ctx; + struct TGLC_bn_ctx *TGLC_bn_ctx; struct tgl_allocator *allocator; @@ -264,6 +264,7 @@ struct tgl_state { int is_bot; int last_temp_id; + long long last_msg_id; }; #pragma pack(pop) //extern struct tgl_state tgl_state; @@ -386,6 +387,7 @@ void tgl_disable_link_preview (struct tgl_state *TLS); void tgl_do_lookup_state (struct tgl_state *TLS); long long tgl_get_allocated_bytes (void); + #ifdef __cplusplus } #endif diff --git a/tl-parser b/tl-parser deleted file mode 160000 -Subproject 36bf1902ff3476c75d0b1f42b34a91e944123b3 diff --git a/tl-parser/.gitignore b/tl-parser/.gitignore new file mode 100644 index 0000000..a49c101 --- /dev/null +++ b/tl-parser/.gitignore @@ -0,0 +1,8 @@ +Makefile +config.h +dep +objs +bin +autom4te.cache +config.log +config.status diff --git a/tl-parser/LICENSE b/tl-parser/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/tl-parser/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 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 Lesser 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 + + 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) <year> <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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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 Lesser General +Public License instead of this License. diff --git a/tl-parser/README.md b/tl-parser/README.md new file mode 100644 index 0000000..ce211a2 --- /dev/null +++ b/tl-parser/README.md @@ -0,0 +1 @@ +Parse tl scheme to tlo file. Formely part of telegram-cli diff --git a/tl-parser/portable_endian.h b/tl-parser/portable_endian.h new file mode 100644 index 0000000..c7720ae --- /dev/null +++ b/tl-parser/portable_endian.h @@ -0,0 +1,140 @@ +// "License": Public Domain +// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. +// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to +// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it +// an example on how to get the endian conversion functions on different platforms. + +/* Originally cloned from https://gist.github.com/PkmX/63dd23f28ba885be53a5 + * Commit was: 1eca2ab34f2301b9641aa73d1016b951fff3fc39 + * Re-published at https://github.com/BenWiederhake/portable-endian.h to provide a means to submit patches and report issues. */ + +#ifndef PORTABLE_ENDIAN_H__ +#define PORTABLE_ENDIAN_H__ + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) + +# include <endian.h> + +#elif defined(__APPLE__) + +# include <libkern/OSByteOrder.h> + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) || defined(__FreeBSD__) + +# include <sys/endian.h> + +#elif defined(__sun) + +# include <sys/byteorder.h> +# define htobe16(x) BE_16(x) +# define htole16(x) LE_16(x) +# define be16toh(x) BE_IN16(x) +# define le16toh(x) LE_IN16(x) + +# define htobe32(x) BE_32(x) +# define htole32(x) LE_32(x) +# define be32toh(x) BE_IN32(x) +# define le32toh(x) LE_IN32(x) + +# define htobe64(x) BE_64(x) +# define htole64(x) LE_64(x) +# define be64toh(x) BE_IN64(x) +# define le64toh(x) LE_IN64(x) + +#elif defined(__NetBSD__) || defined(__DragonFly__) + +# include <sys/endian.h> + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) + +#elif defined(__WINDOWS__) + +# include <winsock2.h> +# include <sys/param.h> + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) htons(x) +# define htole16(x) (x) +# define be16toh(x) ntohs(x) +# define le16toh(x) (x) + +# define htobe32(x) htonl(x) +# define htole32(x) (x) +# define be32toh(x) ntohl(x) +# define le32toh(x) (x) + +# define htobe64(x) __builtin_bswap64(x) +# define htole64(x) (x) +# define be64toh(x) __builtin_bswap64(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#else + +# error platform not supported + +#endif + +#endif diff --git a/tl-parser/tl-parser-tree.h b/tl-parser/tl-parser-tree.h new file mode 100644 index 0000000..fba7c67 --- /dev/null +++ b/tl-parser/tl-parser-tree.h @@ -0,0 +1,178 @@ +/* + This file is part of tgl-library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Copyright Vitaly Valtman 2013-2014 +*/ +#ifndef __TREE_H__ +#define __TREE_H__ +#include <stdio.h> + +#include <memory.h> +#include <assert.h> + +#pragma pack(push,4) +#define DEFINE_TREE(X_NAME, X_TYPE, X_CMP, X_UNSET) \ +struct tree_ ## X_NAME { \ + struct tree_ ## X_NAME *left, *right;\ + X_TYPE x;\ + int y;\ +};\ +\ +static struct tree_ ## X_NAME *new_tree_node_ ## X_NAME (X_TYPE x, int y) {\ + struct tree_ ## X_NAME *T = malloc (sizeof (*T));\ + T->x = x;\ + T->y = y;\ + T->left = T->right = 0;\ + return T;\ +}\ +\ +static void delete_tree_node_ ## X_NAME (struct tree_ ## X_NAME *T) {\ + free (T);\ +}\ +\ +static void tree_split_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x, struct tree_ ## X_NAME **L, struct tree_ ## X_NAME **R) {\ + if (!T) {\ + *L = *R = 0;\ + } else {\ + int c = X_CMP (x, T->x);\ + if (c < 0) {\ + tree_split_ ## X_NAME (T->left, x, L, &T->left);\ + *R = T;\ + } else {\ + tree_split_ ## X_NAME (T->right, x, &T->right, R);\ + *L = T;\ + }\ + }\ +}\ +\ +static struct tree_ ## X_NAME *tree_insert_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x, int y) __attribute__ ((warn_unused_result,unused));\ +static struct tree_ ## X_NAME *tree_insert_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x, int y) {\ + if (!T) {\ + return new_tree_node_ ## X_NAME (x, y);\ + } else {\ + if (y > T->y) {\ + struct tree_ ## X_NAME *N = new_tree_node_ ## X_NAME (x, y);\ + tree_split_ ## X_NAME (T, x, &N->left, &N->right);\ + return N;\ + } else {\ + int c = X_CMP (x, T->x);\ + assert (c);\ + if (c < 0) { \ + T->left = tree_insert_ ## X_NAME (T->left, x, y);\ + } else { \ + T->right = tree_insert_ ## X_NAME (T->right, x, y);\ + } \ + return T; \ + }\ + }\ +}\ +\ +static struct tree_ ## X_NAME *tree_merge_ ## X_NAME (struct tree_ ## X_NAME *L, struct tree_ ## X_NAME *R) {\ + if (!L || !R) {\ + return L ? L : R;\ + } else {\ + if (L->y > R->y) {\ + L->right = tree_merge_ ## X_NAME (L->right, R);\ + return L;\ + } else {\ + R->left = tree_merge_ ## X_NAME (L, R->left);\ + return R;\ + }\ + }\ +}\ +\ +static struct tree_ ## X_NAME *tree_delete_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x) __attribute__ ((warn_unused_result,unused));\ +static struct tree_ ## X_NAME *tree_delete_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x) {\ + assert (T);\ + int c = X_CMP (x, T->x);\ + if (!c) {\ + struct tree_ ## X_NAME *N = tree_merge_ ## X_NAME (T->left, T->right);\ + delete_tree_node_ ## X_NAME (T);\ + return N;\ + } else {\ + if (c < 0) { \ + T->left = tree_delete_ ## X_NAME (T->left, x); \ + } else { \ + T->right = tree_delete_ ## X_NAME (T->right, x); \ + } \ + return T; \ + }\ +}\ +\ +static X_TYPE tree_get_min_ ## X_NAME (struct tree_ ## X_NAME *t) __attribute__ ((unused));\ +static X_TYPE tree_get_min_ ## X_NAME (struct tree_ ## X_NAME *T) {\ + if (!T) { return X_UNSET; } \ + while (T->left) { T = T->left; }\ + return T->x; \ +} \ +\ +static X_TYPE tree_lookup_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x) __attribute__ ((unused));\ +static X_TYPE tree_lookup_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x) {\ + int c;\ + while (T && (c = X_CMP (x, T->x))) {\ + T = (c < 0 ? T->left : T->right);\ + }\ + return T ? T->x : X_UNSET;\ +}\ +\ +static void tree_act_ ## X_NAME (struct tree_ ## X_NAME *T, void (*act)(X_TYPE)) __attribute__ ((unused));\ +static void tree_act_ ## X_NAME (struct tree_ ## X_NAME *T, void (*act)(X_TYPE)) {\ + if (!T) { return; } \ + tree_act_ ## X_NAME (T->left, act); \ + act (T->x); \ + tree_act_ ## X_NAME (T->right, act); \ +}\ +\ +static void tree_act_ex_ ## X_NAME (struct tree_ ## X_NAME *T, void (*act)(X_TYPE, void *), void *extra) __attribute__ ((unused));\ +static void tree_act_ex_ ## X_NAME (struct tree_ ## X_NAME *T, void (*act)(X_TYPE, void *), void *extra) {\ + if (!T) { return; } \ + tree_act_ex_ ## X_NAME (T->left, act, extra); \ + act (T->x, extra); \ + tree_act_ex_ ## X_NAME (T->right, act, extra); \ +}\ +\ +static int tree_count_ ## X_NAME (struct tree_ ## X_NAME *T) __attribute__ ((unused));\ +static int tree_count_ ## X_NAME (struct tree_ ## X_NAME *T) { \ + if (!T) { return 0; }\ + return 1 + tree_count_ ## X_NAME (T->left) + tree_count_ ## X_NAME (T->right); \ +}\ +static void tree_check_ ## X_NAME (struct tree_ ## X_NAME *T) __attribute__ ((unused));\ +static void tree_check_ ## X_NAME (struct tree_ ## X_NAME *T) { \ + if (!T) { return; }\ + if (T->left) { \ + assert (T->left->y <= T->y);\ + assert (X_CMP (T->left->x, T->x) < 0); \ + }\ + if (T->right) { \ + assert (T->right->y <= T->y);\ + assert (X_CMP (T->right->x, T->x) > 0); \ + }\ + tree_check_ ## X_NAME (T->left); \ + tree_check_ ## X_NAME (T->right); \ +}\ +static struct tree_ ## X_NAME *tree_clear_ ## X_NAME (struct tree_ ## X_NAME *T) __attribute__ ((unused));\ +static struct tree_ ## X_NAME *tree_clear_ ## X_NAME (struct tree_ ## X_NAME *T) { \ + if (!T) { return 0; }\ + tree_clear_ ## X_NAME (T->left); \ + tree_clear_ ## X_NAME (T->right); \ + delete_tree_node_ ## X_NAME (T); \ + return 0; \ +} \ + +#define int_cmp(a,b) ((a) - (b)) +#pragma pack(pop) +#endif diff --git a/tl-parser/tl-parser.c b/tl-parser/tl-parser.c new file mode 100644 index 0000000..c2fd179 --- /dev/null +++ b/tl-parser/tl-parser.c @@ -0,0 +1,3014 @@ +/* + This file is part of tl-parser + + tl-parser 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. + + tl-parser 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 tl-parser. If not, see <http://www.gnu.org/licenses/>. + + Copyright Vitaly Valtman 2014 + + It is derivative work of VK/KittenPHP-DB-Engine (https://github.com/vk-com/kphp-kdb/) + Copyright 2012-2013 Vkontakte Ltd + 2012-2013 Vitaliy Valtman + +*/ + +#define _FILE_OFFSET_BITS 64 +#include "config.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <time.h> +#include <zlib.h> +#include "portable_endian.h" +#include "tl-parser-tree.h" +#include "tl-parser.h" +#include "tl-tl.h" +#include "config.h" + +extern int verbosity; +extern int schema_version; +extern int output_expressions; + + +int total_types_num; +int total_constructors_num; +int total_functions_num; + + +/*char *tstrdup (const char *s) { + assert (s); + char *r = talloc (strlen (s) + 1); + memcpy (r, s, strlen (s) + 1); + return r; +}*/ + +#define talloc(a) malloc(a) +#define tfree(a,b) free (a) +#define talloc0(a) calloc(a,1) +#define tstrdup(a) strdup(a) + +typedef char error_int_must_be_4_byte[(sizeof (int) == 4) ? 1 : -1]; +typedef char error_long_long_must_be_8_byte[(sizeof (long long) == 8) ? 1 : -1]; + +char curch; +struct parse parse; + +struct tree *tree; + +struct tree *tree_alloc (void) { + struct tree *T = talloc (sizeof (*T)); + assert (T); + memset (T, 0, sizeof (*T)); + return T; +} + +#define CRC32_INITIAL crc32 (0, 0, 0) + +void tree_add_child (struct tree *P, struct tree *C) { + if (P->nc == P->size) { + void **t = talloc (sizeof (void *) * (++P->size)); + memcpy (t, P->c, sizeof (void *) * (P->size - 1)); + if (P->c) { + tfree (P->c, sizeof (void *) * (P->size - 1)); + } + P->c = (void *)t; + assert (P->c); + } + P->c[P->nc ++] = C; +} + +void tree_delete (struct tree *T) { + assert (T); + int i; + for (i = 0; i < T->nc; i++) { + assert (T->c[i]); + tree_delete (T->c[i]); + } + if (T->c) { + tfree (T->c, sizeof (void *) * T->nc); + } + tfree (T, sizeof (*T)); +} + +void tree_del_child (struct tree *P) { + assert (P->nc); + tree_delete (P->c[--P->nc]); +} + + +char nextch (void) { + if (parse.pos < parse.len - 1) { + curch = parse.text[++parse.pos]; + } else { + curch = 0; + } + if (curch == 10) { + parse.line ++; + parse.line_pos = 0; + } else { + if (curch) { + parse.line_pos ++; + } + } + return curch; +} + + +struct parse save_parse (void) { + return parse; +} + +void load_parse (struct parse _parse) { + parse = _parse; + curch = parse.pos > parse.len ? 0: parse.text[parse.pos] ; +} + +int is_whitespace (char c) { + return (c <= 32); +} + +int is_uletter (char c) { + return (c >= 'A' && c <= 'Z'); +} + +int is_lletter (char c) { + return (c >= 'a' && c <= 'z'); +} + +int is_letter (char c) { + return is_uletter (c) || is_lletter (c); +} + +int is_digit (char c) { + return (c >= '0' && c <= '9'); +} + +int is_hexdigit (char c) { + return is_digit (c) || (c >= 'a' && c <= 'f'); +} + +int is_ident_char (char c) { + return is_digit (c) || is_letter (c) || c == '_'; +} + +int last_error_pos; +int last_error_line; +int last_error_line_pos; +char *last_error; + +void parse_error (const char *e) { + if (parse.pos > last_error_pos) { + last_error_pos = parse.pos; + last_error_line = parse.line; + last_error_line_pos = parse.line_pos; + if (last_error) { + tfree (last_error, strlen (last_error) + 1); + } + last_error = tstrdup (e); + } +} + +void tl_print_parse_error (void) { + fprintf (stderr, "Error near line %d pos %d: `%s`\n", last_error_line + 1, last_error_line_pos + 1, last_error); +} + +char *parse_lex (void) { + while (1) { + while (curch && is_whitespace (curch)) { nextch (); } + if (curch == '/' && nextch () == '/') { + while (nextch () != 10); + nextch (); + } else { + break; + } + } + if (!curch) { + parse.lex.len = 0; + parse.lex.type = lex_eof; + return (parse.lex.ptr = 0); + } + char *p = parse.text + parse.pos; + parse.lex.flags = 0; + switch (curch) { + case '-': + if (nextch () != '-' || nextch () != '-') { + parse_error ("Can not parse triple minus"); + parse.lex.type = lex_error; + return (parse.lex.ptr = (void *)-1); + } else { + parse.lex.len = 3; + parse.lex.type = lex_triple_minus; + nextch (); + return (parse.lex.ptr = p); + } + case ':': + case ';': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case '=': + case '#': + case '?': + case '%': + case '<': + case '>': + case '+': + case ',': + case '*': + case '_': + case '!': + case '.': + nextch (); + parse.lex.len = 1; + parse.lex.type = lex_char; + return (parse.lex.ptr = p); + case 'a'...'z': + case 'A'...'Z': + parse.lex.flags = 0; + if (is_uletter (curch)) { + while (is_ident_char (nextch ())); + parse.lex.len = parse.text + parse.pos - p; + parse.lex.ptr = p; + if (parse.lex.len == 5 && !memcmp (parse.lex.ptr, "Final", 5)) { + parse.lex.type = lex_final; + } else if (parse.lex.len == 3 && !memcmp (parse.lex.ptr, "New", 3)) { + parse.lex.type = lex_new; + } else if (parse.lex.len == 5 && !memcmp (parse.lex.ptr, "Empty", 5)) { + parse.lex.type = lex_empty; + } else { + parse.lex.type = lex_uc_ident; + } + return (parse.lex.ptr = p); + } + while (is_ident_char (nextch ())); + if (curch == '.' && !is_letter (parse.text[parse.pos + 1])) { + parse.lex.len = parse.text + parse.pos - p; + parse.lex.type = lex_lc_ident; + return (parse.lex.ptr = p); + } + if (curch == '.') { + parse.lex.flags |= 1; + nextch (); + if (is_uletter (curch)) { + while (is_ident_char (nextch ())); + parse.lex.len = parse.text + parse.pos - p; + parse.lex.type = lex_uc_ident; + return (parse.lex.ptr = p); + } + if (is_lletter (curch)) { + while (is_ident_char (nextch ())); + } else { + parse_error ("Expected letter"); + parse.lex.type = lex_error; + return (parse.lex.ptr = (void *)-1); + } + } + if (curch == '#') { + parse.lex.flags |= 2; + int i; + int ok = 1; + for (i = 0; i < 8; i++) { + if (!is_hexdigit (nextch())) { + if (curch == ' ' && i >= 5) { + ok = 2; + break; + } else { + parse_error ("Hex digit expected"); + parse.lex.type = lex_error; + return (parse.lex.ptr = (void *)-1); + } + } + } + if (ok == 1) { + nextch (); + } + } + parse.lex.len = parse.text + parse.pos - p; + parse.lex.type = lex_lc_ident; + return (parse.lex.ptr = p); + case '0'...'9': + while (is_digit (nextch ())); + parse.lex.len = parse.text + parse.pos - p; + parse.lex.type = lex_num; + return (parse.lex.ptr = p); + default: + parse_error ("Unknown lexem"); + parse.lex.type = lex_error; + return (parse.lex.ptr = (void *)-1); + } + +} + +int expect (char *s) { + if (!parse.lex.ptr || parse.lex.ptr == (void *)-1 || parse.lex.type == lex_error || parse.lex.type == lex_none || parse.lex.len != (int)strlen (s) || memcmp (s, parse.lex.ptr, parse.lex.len)) { + static char buf[1000]; + sprintf (buf, "Expected %s", s); + parse_error (buf); + return -1; + } else { + parse_lex (); + } + return 1; +} + +struct parse *tl_init_parse_file (const char *fname) { + int fd = open (fname, O_RDONLY); + if (fd < 0) { + fprintf (stderr, "Error %m\n"); + assert (0); + return 0; + } + long long size = lseek (fd, 0, SEEK_END); + if (size <= 0) { + fprintf (stderr, "size is %lld. Too small.\n", size); + return 0; + } + static struct parse save; + save.text = talloc (size); + lseek (fd, 0, SEEK_SET); + save.len = read (fd, save.text, size); + assert (save.len == size); + save.pos = 0; + save.line = 0; + save.line_pos = 0; + save.lex.ptr = save.text; + save.lex.len = 0; + save.lex.type = lex_none; + return &save; +} + +#define PARSE_INIT(_type) struct parse save = save_parse (); struct tree *T = tree_alloc (); T->type = (_type); T->lex_line = parse.line; T->lex_line_pos = parse.line_pos; struct tree *S __attribute__ ((unused)); +#define PARSE_FAIL load_parse (save); tree_delete (T); return 0; +#define PARSE_OK return T; +#define PARSE_TRY_PES(x) if (!(S = x ())) { PARSE_FAIL; } { tree_add_child (T, S); } +#define PARSE_TRY_OPT(x) if ((S = x ())) { tree_add_child (T, S); PARSE_OK } +#define PARSE_TRY(x) S = x (); +#define PARSE_ADD(_type) S = tree_alloc (); S->type = _type; tree_add_child (T, S); +#define EXPECT(s) if (expect (s) < 0) { PARSE_FAIL; } +#define LEX_CHAR(c) (parse.lex.type == lex_char && *parse.lex.ptr == c) +struct tree *parse_args (void); +struct tree *parse_expr (void); + +struct tree *parse_boxed_type_ident (void) { + PARSE_INIT (type_boxed_type_ident); + if (parse.lex.type != lex_uc_ident) { + parse_error ("Can not parse boxed type"); + PARSE_FAIL; + } else { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } +} + +struct tree *parse_full_combinator_id (void) { + PARSE_INIT (type_full_combinator_id); + if (parse.lex.type == lex_lc_ident || LEX_CHAR('_')) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse full combinator id"); + PARSE_FAIL; + } +} + +struct tree *parse_combinator_id (void) { + PARSE_INIT (type_combinator_id); + if (parse.lex.type == lex_lc_ident && !(parse.lex.flags & 2)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse combinator id"); + PARSE_FAIL; + } +} + +struct tree *parse_var_ident (void) { + PARSE_INIT (type_var_ident); + if ((parse.lex.type == lex_lc_ident || parse.lex.type == lex_uc_ident) && !(parse.lex.flags & 3)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse var ident"); + PARSE_FAIL; + } +} + +struct tree *parse_var_ident_opt (void) { + PARSE_INIT (type_var_ident_opt); + if ((parse.lex.type == lex_lc_ident || parse.lex.type == lex_uc_ident)&& !(parse.lex.flags & 3)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else if (LEX_CHAR ('_')) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse var ident opt"); + PARSE_FAIL; + } +} + +struct tree *parse_nat_const (void) { + PARSE_INIT (type_nat_const); + if (parse.lex.type == lex_num) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse nat const"); + PARSE_FAIL; + } +} + +struct tree *parse_type_ident (void) { + PARSE_INIT (type_type_ident); + if (parse.lex.type == lex_uc_ident && !(parse.lex.flags & 2)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else if (parse.lex.type == lex_lc_ident && !(parse.lex.flags & 2)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else if (LEX_CHAR ('#')) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse type ident"); + PARSE_FAIL; + } +} + +struct tree *parse_term (void) { + PARSE_INIT (type_term); + while (LEX_CHAR ('%')) { + EXPECT ("%") + PARSE_ADD (type_percent); + } + if (LEX_CHAR ('(')) { + EXPECT ("("); + PARSE_TRY_PES (parse_expr); + EXPECT (")"); + PARSE_OK; + } + PARSE_TRY (parse_type_ident); + if (S) { + tree_add_child (T, S); + if (LEX_CHAR ('<')) { + EXPECT ("<"); + while (1) { + PARSE_TRY_PES (parse_expr); + if (LEX_CHAR ('>')) { break; } + EXPECT (","); + } + EXPECT (">"); + } + PARSE_OK; + } + PARSE_TRY_OPT (parse_type_ident); + PARSE_TRY_OPT (parse_var_ident); + PARSE_TRY_OPT (parse_nat_const); + PARSE_FAIL; +} + +struct tree *parse_nat_term (void) { + PARSE_INIT (type_nat_term); + PARSE_TRY_PES (parse_term); + PARSE_OK; +} + +struct tree *parse_subexpr (void) { + PARSE_INIT (type_subexpr); + int was_term = 0; + int cc = 0; + + while (1) { + PARSE_TRY (parse_nat_const); + if (S) { + tree_add_child (T, S); + } else if (!was_term) { + was_term = 1; + PARSE_TRY (parse_term); + if (S) { + tree_add_child (T, S); + } else { + break; + } + } + cc ++; + if (!LEX_CHAR ('+')) { + break; + } + EXPECT ("+"); + } + if (!cc) { + PARSE_FAIL; + } else { + PARSE_OK; + } +} + +struct tree *parse_expr (void) { + PARSE_INIT (type_expr); + int cc = 0; + while (1) { + PARSE_TRY (parse_subexpr); + if (S) { + tree_add_child (T, S); + cc ++; + } else { + if (cc < 1) { PARSE_FAIL; } + else { PARSE_OK; } + } + } +} + + + +struct tree *parse_final_empty (void) { + PARSE_INIT (type_final_empty); + EXPECT ("Empty"); + PARSE_TRY_PES (parse_boxed_type_ident); + PARSE_OK; +} + +struct tree *parse_final_new (void) { + PARSE_INIT (type_final_new); + EXPECT ("New"); + PARSE_TRY_PES (parse_boxed_type_ident); + PARSE_OK; +} + +struct tree *parse_final_final (void) { + PARSE_INIT (type_final_final); + EXPECT ("Final"); + PARSE_TRY_PES (parse_boxed_type_ident); + PARSE_OK; +} + +struct tree *parse_partial_comb_app_decl (void) { + PARSE_INIT (type_partial_comb_app_decl); + PARSE_TRY_PES (parse_combinator_id); + while (1) { + PARSE_TRY_PES (parse_subexpr); + if (LEX_CHAR (';')) { break; } + } + PARSE_OK; +} + +struct tree *parse_partial_type_app_decl (void) { + PARSE_INIT (type_partial_type_app_decl); + PARSE_TRY_PES (parse_boxed_type_ident); + if (LEX_CHAR ('<')) { + EXPECT ("<"); + while (1) { + PARSE_TRY_PES (parse_expr); + if (LEX_CHAR ('>')) { break; } + EXPECT (","); + } + EXPECT (">"); + PARSE_OK; + } else { + while (1) { + PARSE_TRY_PES (parse_subexpr); + if (LEX_CHAR (';')) { break; } + } + PARSE_OK; + } +} + + + + +struct tree *parse_multiplicity (void) { + PARSE_INIT (type_multiplicity); + PARSE_TRY_PES (parse_nat_term); + PARSE_OK; +} + + +struct tree *parse_type_term (void) { + PARSE_INIT (type_type_term); + PARSE_TRY_PES (parse_term); + PARSE_OK; +} + +struct tree *parse_optional_arg_def (void) { + PARSE_INIT (type_optional_arg_def); + PARSE_TRY_PES (parse_var_ident); + EXPECT ("."); + PARSE_TRY_PES (parse_nat_const); + EXPECT ("?"); + PARSE_OK; +} + +struct tree *parse_args4 (void) { + PARSE_INIT (type_args4); + struct parse so = save_parse (); + PARSE_TRY (parse_optional_arg_def); + if (S) { + tree_add_child (T, S); + } else { + load_parse (so); + } + if (LEX_CHAR ('!')) { + PARSE_ADD (type_exclam); + EXPECT ("!"); + } + PARSE_TRY_PES (parse_type_term); + PARSE_OK; +} + +struct tree *parse_args3 (void) { + PARSE_INIT (type_args3); + PARSE_TRY_PES (parse_var_ident_opt); + EXPECT (":"); + struct parse so = save_parse (); + PARSE_TRY (parse_optional_arg_def); + if (S) { + tree_add_child (T, S); + } else { + load_parse (so); + } + if (LEX_CHAR ('!')) { + PARSE_ADD (type_exclam); + EXPECT ("!"); + } + PARSE_TRY_PES (parse_type_term); + PARSE_OK; +} + +struct tree *parse_args2 (void) { + PARSE_INIT (type_args2); + PARSE_TRY (parse_var_ident_opt); + if (S && LEX_CHAR (':')) { + tree_add_child (T, S); + EXPECT (":"); + } else { + load_parse (save); + } + struct parse so = save_parse (); + PARSE_TRY (parse_optional_arg_def); + if (S) { + tree_add_child (T, S); + } else { + load_parse (so); + } + struct parse save2 = save_parse (); + PARSE_TRY (parse_multiplicity); + if (S && LEX_CHAR ('*')) { + tree_add_child (T, S); + EXPECT ("*"); + } else { + load_parse (save2); + } + EXPECT ("["); + while (1) { + if (LEX_CHAR (']')) { break; } + PARSE_TRY_PES (parse_args); + } + EXPECT ("]"); + PARSE_OK; +} + +struct tree *parse_args1 (void) { + PARSE_INIT (type_args1); + EXPECT ("("); + while (1) { + PARSE_TRY_PES (parse_var_ident_opt); + if (LEX_CHAR(':')) { break; } + } + EXPECT (":"); + struct parse so = save_parse (); + PARSE_TRY (parse_optional_arg_def); + if (S) { + tree_add_child (T, S); + } else { + load_parse (so); + } + if (LEX_CHAR ('!')) { + PARSE_ADD (type_exclam); + EXPECT ("!"); + } + PARSE_TRY_PES (parse_type_term); + EXPECT (")"); + PARSE_OK; +} + +struct tree *parse_args (void) { + PARSE_INIT (type_args); + PARSE_TRY_OPT (parse_args1); + PARSE_TRY_OPT (parse_args2); + PARSE_TRY_OPT (parse_args3); + PARSE_TRY_OPT (parse_args4); + PARSE_FAIL; +} + +struct tree *parse_opt_args (void) { + PARSE_INIT (type_opt_args); + while (1) { + PARSE_TRY_PES (parse_var_ident); + if (parse.lex.type == lex_char && *parse.lex.ptr == ':') { break;} + } + EXPECT (":"); + PARSE_TRY_PES (parse_type_term); + PARSE_OK; +} + +struct tree *parse_final_decl (void) { + PARSE_INIT (type_final_decl); + PARSE_TRY_OPT (parse_final_new); + PARSE_TRY_OPT (parse_final_final); + PARSE_TRY_OPT (parse_final_empty); + PARSE_FAIL; +} + +struct tree *parse_partial_app_decl (void) { + PARSE_INIT (type_partial_app_decl); + PARSE_TRY_OPT (parse_partial_type_app_decl); + PARSE_TRY_OPT (parse_partial_comb_app_decl); + PARSE_FAIL; +} + +struct tree *parse_result_type (void) { + PARSE_INIT (type_result_type); + PARSE_TRY_PES (parse_boxed_type_ident); + if (LEX_CHAR ('<')) { + EXPECT ("<"); + while (1) { + PARSE_TRY_PES (parse_expr); + if (LEX_CHAR ('>')) { break; } + EXPECT (","); + } + EXPECT (">"); + PARSE_OK; + } else { + while (1) { + if (LEX_CHAR (';')) { PARSE_OK; } + PARSE_TRY_PES (parse_subexpr); + } + } +} + +struct tree *parse_combinator_decl (void) { + PARSE_INIT (type_combinator_decl); + PARSE_TRY_PES (parse_full_combinator_id) + while (1) { + if (LEX_CHAR ('{')) { + parse_lex (); + PARSE_TRY_PES (parse_opt_args); + EXPECT ("}"); + } else { + break; + } + } + while (1) { + if (LEX_CHAR ('=')) { break; } + PARSE_TRY_PES (parse_args); + } + EXPECT ("="); + PARSE_ADD (type_equals); + + PARSE_TRY_PES (parse_result_type); + PARSE_OK; +} + +struct tree *parse_builtin_combinator_decl (void) { + PARSE_INIT (type_builtin_combinator_decl); + PARSE_TRY_PES (parse_full_combinator_id) + EXPECT ("?"); + EXPECT ("="); + PARSE_TRY_PES (parse_boxed_type_ident); + PARSE_OK; +} + +struct tree *parse_declaration (void) { + PARSE_INIT (type_declaration); + PARSE_TRY_OPT (parse_combinator_decl); + PARSE_TRY_OPT (parse_partial_app_decl); + PARSE_TRY_OPT (parse_final_decl); + PARSE_TRY_OPT (parse_builtin_combinator_decl); + PARSE_FAIL; +} + +struct tree *parse_constr_declarations (void) { + PARSE_INIT (type_constr_declarations); + if (parse.lex.type == lex_triple_minus || parse.lex.type == lex_eof) { PARSE_OK; } + while (1) { + PARSE_TRY_PES (parse_declaration); + EXPECT (";"); + if (parse.lex.type == lex_eof || parse.lex.type == lex_triple_minus) { PARSE_OK; } + } +} + +struct tree *parse_fun_declarations (void) { + PARSE_INIT (type_fun_declarations); + if (parse.lex.type == lex_triple_minus || parse.lex.type == lex_eof) { PARSE_OK; } + while (1) { + PARSE_TRY_PES (parse_declaration); + EXPECT (";"); + if (parse.lex.type == lex_eof || parse.lex.type == lex_triple_minus) { PARSE_OK; } + } +} + +struct tree *parse_program (void) { + PARSE_INIT (type_tl_program); + while (1) { + PARSE_TRY_PES (parse_constr_declarations); + if (parse.lex.type == lex_eof) { PARSE_OK; } + if (parse.lex.type == lex_error || expect ("---") < 0 || expect ("functions") < 0 || expect ("---") < 0) { PARSE_FAIL; } + + PARSE_TRY_PES (parse_fun_declarations); + if (parse.lex.type == lex_eof) { PARSE_OK; } + if (parse.lex.type == lex_error || expect ("---") < 0 || expect ("types") < 0 || expect ("---") < 0) { PARSE_FAIL; } + } +} + +struct tree *tl_parse_lex (struct parse *_parse) { + assert (_parse); + load_parse (*_parse); + if (parse.lex.type == lex_none) { + parse_lex (); + } + if (parse.lex.type == lex_error) { + return 0; + } + return parse_program (); +} + +int mystrcmp2 (const char *b, int len, const char *a) { + int c = strncmp (b, a, len); + return c ? a[len] ? -1 : 0 : c; +} + +char *mystrdup (const char *a, int len) { + char *z = talloc (len + 1); + memcpy (z, a, len); + z[len] = 0; + return z; +} + +struct tl_program *tl_program_cur; +#define TL_TRY_PES(x) if (!(x)) { return 0; } + +#define tl_type_cmp(a,b) (strcmp (a->id, b->id)) +DEFINE_TREE (tl_type,struct tl_type *,tl_type_cmp,0) +struct tree_tl_type *tl_type_tree; + +DEFINE_TREE (tl_constructor,struct tl_constructor *,tl_type_cmp,0) +struct tree_tl_constructor *tl_constructor_tree; +struct tree_tl_constructor *tl_function_tree; + +DEFINE_TREE (tl_var,struct tl_var *,tl_type_cmp,0) + +struct tl_var_value { + struct tl_combinator_tree *ptr; + struct tl_combinator_tree *val; + int num_val; +}; + +#define tl_var_value_cmp(a,b) (((char *)a.ptr) - ((char *)b.ptr)) +struct tl_var_value empty; +DEFINE_TREE (var_value, struct tl_var_value, tl_var_value_cmp, empty) +//tree_tl_var_t *tl_var_tree; + +DEFINE_TREE (tl_field,char *,strcmp, 0) +//tree_tl_field_t *tl_field_tree; +#define TL_FAIL return 0; +#define TL_INIT(x) struct tl_combinator_tree *x = 0; +#define TL_TRY(f,x) { struct tl_combinator_tree *_t = f; if (!_t) { TL_FAIL;} x = tl_union (x, _t); if (!x) { TL_FAIL; }} +#define TL_ERROR(...) fprintf (stderr, __VA_ARGS__); +#define TL_WARNING(...) fprintf (stderr, __VA_ARGS__); + +void tl_set_var_value (struct tree_var_value **T, struct tl_combinator_tree *var, struct tl_combinator_tree *value) { + struct tl_var_value t = {.ptr = var, .val = value, .num_val = 0}; + if (tree_lookup_var_value (*T, t).ptr) { + *T = tree_delete_var_value (*T, t); + } + *T = tree_insert_var_value (*T, t, lrand48 ()); +} + +void tl_set_var_value_num (struct tree_var_value **T, struct tl_combinator_tree *var, struct tl_combinator_tree *value, long long num_value) { + struct tl_var_value t = {.ptr = var, .val = value, .num_val = num_value}; + if (tree_lookup_var_value (*T, t).ptr) { + *T = tree_delete_var_value (*T, t); + } + *T = tree_insert_var_value (*T, t, lrand48 ()); +} + +struct tl_combinator_tree *tl_get_var_value (struct tree_var_value **T, struct tl_combinator_tree *var) { + struct tl_var_value t = {.ptr = var, .val = 0, .num_val = 0}; + struct tl_var_value r = tree_lookup_var_value (*T, t); + return r.ptr ? r.val : 0; +} + +int tl_get_var_value_num (struct tree_var_value **T, struct tl_combinator_tree *var) { + struct tl_var_value t = {.ptr = var, .val = 0}; + struct tl_var_value r = tree_lookup_var_value (*T, t); + return r.ptr ? r.num_val : 0; +} + +int namespace_level; + +struct tree_tl_var *vars[10]; +struct tree_tl_field *fields[10]; +struct tl_var *last_num_var[10]; + +int tl_is_type_name (const char *id, int len) { + if (len == 1 && *id == '#') { return 1;} + int ok = id[0] >= 'A' && id[0] <= 'Z'; + int i; + for (i = 0; i < len - 1; i++) if (id[i] == '.') { + ok = id[i + 1] >= 'A' && id[i + 1] <= 'Z'; + } + return ok; +} + +int tl_add_field (char *id) { + assert (namespace_level < 10); + assert (namespace_level >= 0); + if (tree_lookup_tl_field (fields[namespace_level], id)) { + return 0; + } + fields[namespace_level] = tree_insert_tl_field (fields[namespace_level], id, lrand48 ()); + return 1; +} + +void tl_clear_fields (void) { +// tree_act_tl_field (fields[namespace_level], (void *)free); + fields[namespace_level] = tree_clear_tl_field (fields[namespace_level]); +} + +struct tl_var *tl_add_var (char *id, struct tl_combinator_tree *ptr, int type) { + struct tl_var *v = talloc (sizeof (*v)); + v->id = tstrdup (id); + v->type = type; + v->ptr = ptr; + v->flags = 0; + if (tree_lookup_tl_var (vars[namespace_level], v)) { + return 0; + } + vars[namespace_level] = tree_insert_tl_var (vars[namespace_level], v, lrand48 ()); + if (type) { + last_num_var[namespace_level] = v; + } + return v; +} + +void tl_del_var (struct tl_var *v) { +// free (v->id); + tfree (v, sizeof (*v)); +} + +void tl_clear_vars (void) { + tree_act_tl_var (vars[namespace_level], tl_del_var); + vars[namespace_level] = tree_clear_tl_var (vars[namespace_level]); + last_num_var[namespace_level] = 0; +} + +struct tl_var *tl_get_last_num_var (void) { + return last_num_var[namespace_level]; +} + +struct tl_var *tl_get_var (char *_id, int len) { + char *id = mystrdup (_id, len); + struct tl_var v = {.id = id}; + int i; + for (i = namespace_level; i >= 0; i--) { + struct tl_var *w = tree_lookup_tl_var (vars[i], &v); + if (w) { + tfree (id, len + 1); + return w; + } + } + tfree (id, len + 1); + return 0; +} + +void namespace_push (void) { + namespace_level ++; + assert (namespace_level < 10); + tl_clear_vars (); + tl_clear_fields (); +} + +void namespace_pop (void) { + namespace_level --; + assert (namespace_level >= 0); +} + +struct tl_type *tl_get_type (const char *_id, int len) { + char *id = mystrdup (_id, len); + struct tl_type _t = {.id = id}; + struct tl_type *r = tree_lookup_tl_type (tl_type_tree, &_t); + tfree (id, len + 1); + return r; +} + +struct tl_type *tl_add_type (const char *_id, int len, int params_num, long long params_types) { + char *id = talloc (len + 1); + memcpy (id, _id, len); + id[len] = 0; + struct tl_type _t = {.id = id}; + struct tl_type *_r = 0; + if ((_r = tree_lookup_tl_type (tl_type_tree, &_t))) { + tfree (id, len + 1); + if (params_num >= 0 && (_r->params_num != params_num || _r->params_types != params_types)) { + TL_ERROR ("Wrong params_num or types for type %s\n", _r->id); + return 0; + } + return _r; + } + struct tl_type *t = talloc (sizeof (*t)); + t->id = id; + t->print_id = tstrdup (t->id); + int i; + for (i = 0; i < len; i++) if (t->print_id[i] == '.' || t->print_id[i] == '#' || t->print_id[i] == ' ') { + t->print_id[i] = '$'; + } + t->name = 0; + t->constructors_num = 0; + t->constructors = 0; + t->flags = 0; + t->real_id = 0; + if (params_num >= 0) { + assert (params_num <= 64); + t->params_num = params_num; + t->params_types = params_types; + } else { + t->flags |= 4; + t->params_num = -1; + } + tl_type_tree = tree_insert_tl_type (tl_type_tree, t, lrand48 ()); + total_types_num ++; + return t; +} + +void tl_add_type_param (struct tl_type *t, int x) { + assert (t->flags & 4); + assert (t->params_num <= 64); + if (x) { + t->params_types |= (1ull << (t->params_num ++)); + } else { + t->params_num ++; + } +} + +int tl_type_set_params (struct tl_type *t, int x, long long y) { + if (t->flags & 4) { + t->params_num = x; + t->params_types = y; + t->flags &= ~4; + } else { + if (t->params_num != x || t->params_types != y) { + fprintf (stderr, "Wrong num of params (type %s)\n", t->id); + return 0; + } + } + return 1; +} + +void tl_type_finalize (struct tl_type *t) { + t->flags &= ~4; +} + +struct tl_constructor *tl_get_constructor (const char *_id, int len) { + char *id = mystrdup (_id, len); + struct tl_constructor _t = {.id = id}; + struct tl_constructor *r = tree_lookup_tl_constructor (tl_constructor_tree, &_t); + tfree (id, len + 1); + return r; +} + +struct tl_constructor *tl_add_constructor (struct tl_type *a, const char *_id, int len, int force_magic) { + assert (a); + if (a->flags & 1) { + TL_ERROR ("New constructor for type `%s` after final statement\n", a->id); + return 0; + } + int x = 0; + while (x < len && (_id[x] != '#' || force_magic)) { x++; } + char *id = talloc (x + 1); + memcpy (id, _id, x); + id[x] = 0; + + unsigned magic = 0; + if (x < len) { + assert (len - x >= 6 && len - x <= 9); + int i; + for (i = 1; i < len - x; i++) { + magic = (magic << 4) + (_id[x + i] <= '9' ? _id[x + i] - '0' : _id[x + i] - 'a' + 10); + } + assert (magic && magic != (unsigned)-1); + } + + len = x; + if (*id != '_') { + struct tl_constructor _t = {.id = id}; + if (tree_lookup_tl_constructor (tl_constructor_tree, &_t)) { + TL_ERROR ("Duplicate constructor id `%s`\n", id); + tfree (id, len + 1); + return 0; + } + } else { + assert (len == 1); + } + + struct tl_constructor *t = talloc (sizeof (*t)); + t->type = a; + t->name = magic; + t->id = id; + t->print_id = tstrdup (id); + t->real_id = 0; + + int i; + for (i = 0; i < len; i++) if (t->print_id[i] == '.' || t->print_id[i] == '#' || t->print_id[i] == ' ') { + t->print_id[i] = '$'; + } + + t->left = t->right = 0; + a->constructors = realloc (a->constructors, sizeof (void *) * (a->constructors_num + 1)); + assert (a->constructors); + a->constructors[a->constructors_num ++] = t; + if (*id != '_') { + tl_constructor_tree = tree_insert_tl_constructor (tl_constructor_tree, t, lrand48 ()); + } else { + a->flags |= FLAG_DEFAULT_CONSTRUCTOR; + } + total_constructors_num ++; + return t; +} + +struct tl_constructor *tl_get_function (const char *_id, int len) { + char *id = mystrdup (_id, len); + struct tl_constructor _t = {.id = id}; + struct tl_constructor *r = tree_lookup_tl_constructor (tl_function_tree, &_t); + tfree (id, len + 1); + return r; +} + +struct tl_constructor *tl_add_function (struct tl_type *a, const char *_id, int len, int force_magic) { +// assert (a); + int x = 0; + while (x < len && ((_id[x] != '#') || force_magic)) { x++; } + char *id = talloc (x + 1); + memcpy (id, _id, x); + id[x] = 0; + + unsigned magic = 0; + if (x < len) { + assert (len - x >= 6 && len - x <= 9); + int i; + for (i = 1; i < len - x; i++) { + magic = (magic << 4) + (_id[x + i] <= '9' ? _id[x + i] - '0' : _id[x + i] - 'a' + 10); + } + assert (magic && magic != (unsigned)-1); + } + + len = x; + + struct tl_constructor _t = {.id = id}; + if (tree_lookup_tl_constructor (tl_function_tree, &_t)) { + TL_ERROR ("Duplicate function id `%s`\n", id); + tfree (id, len + 1); + return 0; + } + + struct tl_constructor *t = talloc (sizeof (*t)); + t->type = a; + t->name = magic; + t->id = id; + t->print_id = tstrdup (id); + t->real_id = 0; + + int i; + for (i = 0; i < len; i++) if (t->print_id[i] == '.' || t->print_id[i] == '#' || t->print_id[i] == ' ') { + t->print_id[i] = '$'; + } + + t->left = t->right = 0; + tl_function_tree = tree_insert_tl_constructor (tl_function_tree, t, lrand48 ()); + total_functions_num ++; + return t; +} + +static char buf[(1 << 20)]; +int buf_pos; + +struct tl_combinator_tree *alloc_ctree_node (void) { + struct tl_combinator_tree *T = talloc (sizeof (*T)); + assert (T); + memset (T, 0, sizeof (*T)); + return T; +} + +struct tl_combinator_tree *tl_tree_dup (struct tl_combinator_tree *T) { + if (!T) { return 0; } + struct tl_combinator_tree *S = talloc (sizeof (*S)); + memcpy (S, T, sizeof (*S)); + S->left = tl_tree_dup (T->left); + S->right = tl_tree_dup (T->right); + return S; +} + +struct tl_type *tl_tree_get_type (struct tl_combinator_tree *T) { + assert (T->type == type_type); + if (T->act == act_array) { return 0;} + while (T->left) { + T = T->left; + if (T->act == act_array) { return 0;} + assert (T->type == type_type); + } + assert (T->act == act_type || T->act == act_var || T->act == act_array); + return T->act == act_type ? T->data : 0; +} + +void tl_tree_set_len (struct tl_combinator_tree *T) { + TL_INIT (H); + H = T; + while (H->left) { + H->left->type_len = H->type_len + 1; + H = H->left; + } + assert (H->type == type_type); + struct tl_type *t = H->data; + assert (t); + assert (H->type_len == t->params_num); +} + +void tl_buf_reset (void) { + buf_pos = 0; +} + +void tl_buf_add_string (char *s, int len) { + if (len < 0) { len = strlen (s); } + buf[buf_pos ++] = ' '; + memcpy (buf + buf_pos, s, len); buf_pos += len; + buf[buf_pos] = 0; +} + +void tl_buf_add_string_nospace (char *s, int len) { + if (len < 0) { len = strlen (s); } +// if (buf_pos) { buf[buf_pos ++] = ' '; } + memcpy (buf + buf_pos, s, len); buf_pos += len; + buf[buf_pos] = 0; +} + +void tl_buf_add_string_q (char *s, int len, int x) { + if (x) { + tl_buf_add_string (s, len); + } else { + tl_buf_add_string_nospace (s, len); + } +} + + +void tl_buf_add_tree (struct tl_combinator_tree *T, int x) { + if (!T) { return; } + assert (T != (void *)-1l && T != (void *)-2l); + switch (T->act) { + case act_question_mark: + tl_buf_add_string_q ("?", -1, x); + return; + case act_type: + if ((T->flags & 1) && !(T->flags & 4)) { + tl_buf_add_string_q ("%", -1, x); + x = 0; + } + if (T->flags & 2) { + tl_buf_add_string_q ((char *)T->data, -1, x); + } else { + struct tl_type *t = T->data; + if (T->flags & 4) { + assert (t->constructors_num == 1); + tl_buf_add_string_q (t->constructors[0]->real_id ? t->constructors[0]->real_id : t->constructors[0]->id, -1, x); + } else { + tl_buf_add_string_q (t->real_id ? t->real_id : t->id, -1, x); + } + } + return; + case act_field: + if (T->data) { + tl_buf_add_string_q ((char *)T->data, -1, x); + x = 0; + tl_buf_add_string_q (":", -1, 0); + } + tl_buf_add_tree (T->left, x); + tl_buf_add_tree (T->right, 1); + return; + case act_union: + tl_buf_add_tree (T->left, x); + tl_buf_add_tree (T->right, 1); + return; + case act_var: + { + if (T->data == (void *)-1l) { return; } + struct tl_combinator_tree *v = T->data; + tl_buf_add_string_q ((char *)v->data, -1, x); + if (T->type == type_num && T->type_flags) { + static char _buf[30]; + sprintf (_buf, "+%lld", T->type_flags); + tl_buf_add_string_q (_buf, -1, 0); + } + } + return; + case act_arg: + tl_buf_add_tree (T->left, x); + tl_buf_add_tree (T->right, 1); + return; + case act_array: + if (T->left && !(T->left->flags & 128)) { + tl_buf_add_tree (T->left, x); + x = 0; + tl_buf_add_string_q ("*", -1, x); + } + tl_buf_add_string_q ("[", -1, x); + tl_buf_add_tree (T->right, 1); + tl_buf_add_string_q ("]", -1, 1); + return; + case act_plus: + tl_buf_add_tree (T->left, x); + tl_buf_add_string_q ("+", -1, 0); + tl_buf_add_tree (T->right, 0); + return; + case act_nat_const: + { + static char _buf[30]; + snprintf (_buf, 29, "%lld", T->type_flags); + tl_buf_add_string_q (_buf, -1, x); + return; + } + case act_opt_field: + { + struct tl_combinator_tree *v = T->left->data; + tl_buf_add_string_q ((char *)v->data, -1, x); + tl_buf_add_string_q (".", -1, 0); + static char _buf[30]; + sprintf (_buf, "%lld", T->left->type_flags); + tl_buf_add_string_q (_buf, -1, 0); + tl_buf_add_string_q ("?", -1, 0); + tl_buf_add_tree (T->right, 0); + return; + } + + default: + fprintf (stderr, "%s %s\n", TL_ACT (T->act), TL_TYPE (T->type)); + assert (0); + return; + } +} + +int tl_count_combinator_name (struct tl_constructor *c) { + assert (c); + tl_buf_reset (); + tl_buf_add_string_nospace (c->real_id ? c->real_id : c->id, -1); + tl_buf_add_tree (c->left, 1); + tl_buf_add_string ("=", -1); + tl_buf_add_tree (c->right, 1); + //fprintf (stderr, "%.*s\n", buf_pos, buf); + if (!c->name) { + c->name = crc32 (CRC32_INITIAL, (void *) buf, buf_pos); + } + return c->name; +} + +int tl_print_combinator (struct tl_constructor *c) { + tl_buf_reset (); + tl_buf_add_string_nospace (c->real_id ? c->real_id : c->id, -1); + static char _buf[10]; + sprintf (_buf, "#%08x", c->name); + tl_buf_add_string_nospace (_buf, -1); + tl_buf_add_tree (c->left, 1); + tl_buf_add_string ("=", -1); + tl_buf_add_tree (c->right, 1); + if (output_expressions >= 1) { + fprintf (stderr, "%.*s\n", buf_pos, buf); + } +/* if (!c->name) { + c->name = crc32 (CRC32_INITIAL, (void *) bbuf, buf_pos); + }*/ + return c->name; +} + +int _tl_finish_subtree (struct tl_combinator_tree *R, int x, long long y) { + assert (R->type == type_type); + assert (R->type_len < 0); + assert (R->act == act_arg || R->act == act_type); + R->type_len = x; + R->type_flags = y; + if (R->act == act_type) { + struct tl_type *t = R->data; + assert (t); + return tl_type_set_params (t, x, y); + } + assert ((R->right->type == type_type && R->right->type_len == 0) || R->right->type == type_num || R->right->type == type_num_value); + return _tl_finish_subtree (R->left, x + 1, y * 2 + (R->right->type == type_num || R->right->type == type_num_value)); +} + +int tl_finish_subtree (struct tl_combinator_tree *R) { + assert (R); + if (R->type != type_type) { + return 1; + } + if (R->type_len >= 0) { + if (R->type_len > 0) { + TL_ERROR ("Not enough params\n"); + return 0; + } + return 1; + } + return _tl_finish_subtree (R, 0, 0); +} + +struct tl_combinator_tree *tl_union (struct tl_combinator_tree *L, struct tl_combinator_tree *R) { + if (!L) { return R; } + if (!R) { return L; } + TL_INIT (v); + v = alloc_ctree_node (); + v->left = L; + v->right = R; + switch (L->type) { + case type_num: + if (R->type != type_num_value) { + TL_ERROR ("Union: type mistmatch\n"); + return 0; + } + tfree (v, sizeof (*v)); + L->type_flags += R->type_flags; + return L; + case type_num_value: + if (R->type != type_num_value && R->type != type_num) { + TL_ERROR ("Union: type mistmatch\n"); + return 0; + } + tfree (v, sizeof (*v)); + R->type_flags += L->type_flags; + return R; + case type_list_item: + case type_list: + if (R->type != type_list_item) { + TL_ERROR ("Union: type mistmatch\n"); + return 0; + } + v->type = type_list; + v->act = act_union; + return v; + case type_type: + if (L->type_len == 0) { + TL_ERROR ("Arguments number exceeds type arity\n"); + return 0; + } + if (R->type != type_num && R->type != type_type && R->type != type_num_value) { + TL_ERROR ("Union: type mistmatch\n"); + return 0; + } + if (R->type_len < 0) { + if (!tl_finish_subtree (R)) { + return 0; + } + } + if (R->type_len > 0) { + TL_ERROR ("Argument type must have full number of arguments\n"); + return 0; + } + if (L->type_len > 0 && ((L->type_flags & 1) != (R->type == type_num || R->type == type_num_value))) { + TL_ERROR ("Argument types mistmatch: L->type_flags = %lld, R->type = %s\n", L->flags, TL_TYPE (R->type)); + return 0; + } + v->type = type_type; + v->act = act_arg; + v->type_len = L->type_len > 0 ? L->type_len - 1 : -1; + v->type_flags = L->type_flags >> 1; + return v; + default: + assert (0); + return 0; + } +} + +struct tl_combinator_tree *tl_parse_any_term (struct tree *T, int s); +struct tl_combinator_tree *tl_parse_term (struct tree *T, int s) { + assert (T->type == type_term); + int i = 0; + while (i < T->nc && T->c[i]->type == type_percent) { i ++; s ++; } + assert (i < T->nc); + TL_INIT (L); + while (i < T->nc) { + TL_TRY (tl_parse_any_term (T->c[i], s), L); + s = 0; + i ++; + } + return L; +} + + +struct tl_combinator_tree *tl_parse_type_term (struct tree *T, int s) { + assert (T->type == type_type_term); + assert (T->nc == 1); + struct tl_combinator_tree *Z = tl_parse_term (T->c[0], s); + if (!Z || Z->type != type_type) { if (Z) { TL_ERROR ("type_term: found type %s\n", TL_TYPE (Z->type)); } TL_FAIL; } + return Z; +} + +struct tl_combinator_tree *tl_parse_nat_term (struct tree *T, int s) { + assert (T->type == type_nat_term); + assert (T->nc == 1); + struct tl_combinator_tree *Z = tl_parse_term (T->c[0], s); + if (!Z || (Z->type != type_num && Z->type != type_num_value)) { if (Z) { TL_ERROR ("nat_term: found type %s\n", TL_TYPE (Z->type)); }TL_FAIL; } + return Z; +} + +struct tl_combinator_tree *tl_parse_subexpr (struct tree *T, int s) { + assert (T->type == type_subexpr); + assert (T->nc >= 1); + int i; + TL_INIT (L); + for (i = 0; i < T->nc; i++) { + TL_TRY (tl_parse_any_term (T->c[i], s), L); + s = 0; + } + return L; +} + +struct tl_combinator_tree *tl_parse_expr (struct tree *T, int s) { + assert (T->type == type_expr); + assert (T->nc >= 1); + int i; + TL_INIT (L); + for (i = 0; i < T->nc; i++) { + TL_TRY (tl_parse_subexpr (T->c[i], s), L); + s = 0; + } + return L; +} + +struct tl_combinator_tree *tl_parse_nat_const (struct tree *T, int s) { + assert (T->type == type_nat_const); + assert (!T->nc); + if (s > 0) { + TL_ERROR ("Nat const can not precede with %%\n"); + TL_FAIL; + } + assert (T->type == type_nat_const); + assert (!T->nc); + TL_INIT (L); + L = alloc_ctree_node (); + L->act = act_nat_const; + L->type = type_num_value; + int i; + long long x = 0; + for (i = 0; i < T->len; i++) { + x = x * 10 + T->text[i] - '0'; + } + L->type_flags = x; + return L; +} + +struct tl_combinator_tree *tl_parse_ident (struct tree *T, int s) { + assert (T->type == type_type_ident || T->type == type_var_ident || T->type == type_boxed_type_ident); + assert (!T->nc); + struct tl_var *v = tl_get_var (T->text, T->len); + TL_INIT (L); + if (v) { + L = alloc_ctree_node (); + L->act = act_var; + L->type = v->type ? type_num : type_type; + if (L->type == type_num && s) { + TL_ERROR ("Nat var can not precede with %%\n"); + TL_FAIL; + } else { + if (s) { + L->flags |= 1; + } + } + L->type_len = 0; + L->type_flags = 0; + L->data = v->ptr; + return L; + } + +/* if (!mystrcmp2 (T->text, T->len, "#") || !mystrcmp2 (T->text, T->len, "Type")) { + L = alloc_ctree_node (); + L->act = act_type; + L->flags |= 2; + L->data = tl_get_type (T->text, T->len); + assert (L->data); + L->type = type_type; + L->type_len = 0; + L->type_flags = 0; + return L; + }*/ + + struct tl_constructor *c = tl_get_constructor (T->text, T->len); + if (c) { + assert (c->type); + if (c->type->constructors_num != 1) { + TL_ERROR ("Constructor can be used only if it is the only constructor of the type\n"); + return 0; + } + c->type->flags |= 1; + L = alloc_ctree_node (); + L->act = act_type; + L->flags |= 5; + L->data = c->type; + L->type = type_type; + L->type_len = c->type->params_num; + L->type_flags = c->type->params_types; + return L; + } + int x = tl_is_type_name (T->text, T->len); + if (x) { + struct tl_type *t = tl_add_type (T->text, T->len, -1, 0); + L = alloc_ctree_node (); + if (s) { + L->flags |= 1; + t->flags |= 8; + } + L->act = act_type; + L->data = t; + L->type = type_type; + L->type_len = t->params_num; + L->type_flags = t->params_types; + return L; + } else { + TL_ERROR ("Not a type/var ident `%.*s`\n", T->len, T->text); + return 0; + } +} + +struct tl_combinator_tree *tl_parse_any_term (struct tree *T, int s) { + switch (T->type) { + case type_type_term: + return tl_parse_type_term (T, s); + case type_nat_term: + return tl_parse_nat_term (T, s); + case type_term: + return tl_parse_term (T, s); + case type_expr: + return tl_parse_expr (T, s); + case type_subexpr: + return tl_parse_subexpr (T, s); + case type_nat_const: + return tl_parse_nat_const (T, s); + case type_type_ident: + case type_var_ident: + return tl_parse_ident (T, s); + default: + fprintf (stderr, "type = %d\n", T->type); + assert (0); + return 0; + } +} + +struct tl_combinator_tree *tl_parse_multiplicity (struct tree *T) { + assert (T->type == type_multiplicity); + assert (T->nc == 1); + return tl_parse_nat_term (T->c[0], 0); +} + +struct tl_combinator_tree *tl_parse_opt_args (struct tree *T) { + assert (T); + assert (T->type == type_opt_args); + assert (T->nc >= 2); + TL_INIT (R); + TL_TRY (tl_parse_type_term (T->c[T->nc - 1], 0), R); + assert (R->type == type_type && !R->type_len); + assert (tl_finish_subtree (R)); + struct tl_type *t = tl_tree_get_type (R); + //assert (t); + int tt = -1; + if (t && !strcmp (t->id, "#")) { + tt = 1; + } else if (t && !strcmp (t->id, "Type")) { + tt = 0; + } + if (tt < 0) { + TL_ERROR ("Optargs can be only of type # or Type\n"); + TL_FAIL; + } + + int i; + for (i = 0; i < T->nc - 1; i++) { + if (T->c[i]->type != type_var_ident) { + TL_ERROR ("Variable name expected\n"); + TL_FAIL; + } + if (T->c[i]->len == 1 && *T->c[i]->text == '_') { + TL_ERROR ("Variables can not be unnamed\n"); + TL_FAIL; + } + } + TL_INIT (H); +// for (i = T->nc - 2; i >= (T->nc >= 2 ? 0 : -1); i--) { + for (i = 0; i <= T->nc - 2; i++) { + TL_INIT (S); S = alloc_ctree_node (); + S->left = (i == T->nc - 2) ? R : tl_tree_dup (R) ; S->right = 0; + S->type = type_list_item; + S->type_len = 0; + S->act = act_field; + S->data = i >= 0 ? mystrdup (T->c[i]->text, T->c[i]->len) : 0; + if (tt >= 0) { + assert (S->data); + tl_add_var (S->data, S, tt); + } + S->flags = 33; + H = tl_union (H, S); + } + return H; +} + +struct tl_combinator_tree *tl_parse_args (struct tree *T); +struct tl_combinator_tree *tl_parse_args2 (struct tree *T) { + assert (T); + assert (T->type == type_args2); + assert (T->nc >= 1); + TL_INIT (R); + TL_INIT (L); + int x = 0; + char *field_name = 0; + if (T->c[x]->type == type_var_ident_opt || T->c[x]->type == type_var_ident) { + field_name = mystrdup (T->c[x]->text, T->c[x]->len); + if (!tl_add_field (field_name)) { + TL_ERROR ("Duplicate field name %s\n", field_name); + TL_FAIL; + } + x ++; + } + //fprintf (stderr, "%d %d\n", x, T->nc); + if (T->c[x]->type == type_multiplicity) { + L = tl_parse_multiplicity (T->c[x]); + if (!L) { TL_FAIL;} + x ++; + } else { + struct tl_var *v = tl_get_last_num_var (); + if (!v) { + TL_ERROR ("Expected multiplicity or nat var\n"); + TL_FAIL; + } + L = alloc_ctree_node (); + L->act = act_var; + L->type = type_num; + L->flags |= 128; + L->type_len = 0; + L->type_flags = 0; + L->data = v->ptr; + ((struct tl_combinator_tree *)(v->ptr))->flags |= 256; + } + namespace_push (); + while (x < T->nc) { + TL_TRY (tl_parse_args (T->c[x]), R); + x ++; + } + namespace_pop (); + struct tl_combinator_tree *S = alloc_ctree_node (); + S->type = type_type; + S->type_len = 0; + S->act = act_array; + S->left = L; + S->right = R; + //S->data = field_name; + + struct tl_combinator_tree *H = alloc_ctree_node (); + H->type = type_list_item; + H->act = act_field; + H->left = S; + H->right = 0; + H->data = field_name; + H->type_len = 0; + + return H; +} + +void tl_mark_vars (struct tl_combinator_tree *T); +struct tl_combinator_tree *tl_parse_args134 (struct tree *T) { + assert (T); + assert (T->type == type_args1 || T->type == type_args3 || T->type == type_args4); + assert (T->nc >= 1); + TL_INIT (R); + TL_TRY (tl_parse_type_term (T->c[T->nc - 1], 0), R); + assert (tl_finish_subtree (R)); + assert (R->type == type_type && !R->type_len); + struct tl_type *t = tl_tree_get_type (R); + //assert (t); + int tt = -1; + if (t && !strcmp (t->id, "#")) { + tt = 1; + } else if (t && !strcmp (t->id, "Type")) { + tt = 0; + } + +/* if (tt >= 0 && T->nc == 1) { + TL_ERROR ("Variables can not be unnamed (type %d)\n", tt); + }*/ + int last = T->nc - 2; + int excl = 0; + if (last >= 0 && T->c[last]->type == type_exclam) { + excl ++; + tl_mark_vars (R); + last --; + } + if (last >= 0 && T->c[last]->type == type_optional_arg_def) { + assert (T->c[last]->nc == 2); + TL_INIT (E); E = alloc_ctree_node (); + E->type = type_type; + E->act = act_opt_field; + E->left = tl_parse_ident (T->c[last]->c[0], 0); + int i; + long long x = 0; + for (i = 0; i < T->c[last]->c[1]->len; i++) { + x = x * 10 + T->c[last]->c[1]->text[i] - '0'; + } + E->left->type_flags = x; + E->type_flags = R->type_flags; + E->type_len = R->type_len; + E->right = R; + R = E; + last --; + } + int i; + for (i = 0; i < last; i++) { + if (T->c[i]->type != type_var_ident && T->c[i]->type != type_var_ident_opt) { + TL_ERROR ("Variable name expected\n"); + TL_FAIL; + } +/* if (tt >= 0 && (T->nc == 1 || (T->c[i]->len == 1 && *T->c[i]->text == '_'))) { + TL_ERROR ("Variables can not be unnamed\n"); + TL_FAIL; + }*/ + } + TL_INIT (H); +// for (i = T->nc - 2; i >= (T->nc >= 2 ? 0 : -1); i--) { + for (i = (last >= 0 ? 0 : -1); i <= last; i++) { + TL_INIT (S); S = alloc_ctree_node (); + S->left = (i == last) ? R : tl_tree_dup (R) ; S->right = 0; + S->type = type_list_item; + S->type_len = 0; + S->act = act_field; + S->data = i >= 0 ? mystrdup (T->c[i]->text, T->c[i]->len) : 0; + if (excl) { + S->flags |= FLAG_EXCL; + } + if (S->data && (T->c[i]->len >= 2 || *T->c[i]->text != '_')) { + if (!tl_add_field (S->data)) { + TL_ERROR ("Duplicate field name %s\n", (char *)S->data); + TL_FAIL; + } + } + if (tt >= 0) { + char *name = S->data; + static char s[21]; + if (!name) { + sprintf (s, "%lld", lrand48 () * (1ll << 32) + lrand48 ()); + name = s; // will be strdup'd, so reference-to-stack is fine. + } + struct tl_var *v = tl_add_var (name, S, tt); + if (!v) {TL_FAIL;} + v->flags |= 2; + } + + H = tl_union (H, S); + } + return H; +} + + +struct tl_combinator_tree *tl_parse_args (struct tree *T) { + assert (T->type == type_args); + assert (T->nc == 1); + switch (T->c[0]->type) { + case type_args1: + return tl_parse_args134 (T->c[0]); + case type_args2: + return tl_parse_args2 (T->c[0]); + case type_args3: + return tl_parse_args134 (T->c[0]); + case type_args4: + return tl_parse_args134 (T->c[0]); + default: + assert (0); + return 0; + } +} + +void tl_mark_vars (struct tl_combinator_tree *T) { + if (!T) { return; } + if (T->act == act_var) { + char *id = ((struct tl_combinator_tree *)(T->data))->data; + struct tl_var *v = tl_get_var (id, strlen (id)); + assert (v); + v->flags |= 1; + } + tl_mark_vars (T->left); + tl_mark_vars (T->right); +} + +struct tl_combinator_tree *tl_parse_result_type (struct tree *T) { + assert (T->type == type_result_type); + assert (T->nc >= 1); + assert (T->nc <= 64); + + TL_INIT (L); + + if (tl_get_var (T->c[0]->text, T->c[0]->len)) { + if (T->nc != 1) { + TL_ERROR ("Variable can not take params\n"); + TL_FAIL; + } + L = alloc_ctree_node (); + L->act = act_var; + L->type = type_type; + struct tl_var *v = tl_get_var (T->c[0]->text, T->c[0]->len); + if (v->type) { + TL_ERROR ("Type mistmatch\n"); + TL_FAIL; + } + L->data = v->ptr; +// assert (v->ptr); + } else { + L = alloc_ctree_node (); + L->act = act_type; + L->type = type_type; + struct tl_type *t = tl_add_type (T->c[0]->text, T->c[0]->len, -1, 0); + assert (t); + L->type_len = t->params_num; + L->type_flags = t->params_types; + L->data = t; + + int i; + for (i = 1; i < T->nc; i++) { + TL_TRY (tl_parse_any_term (T->c[i], 0), L); + assert (L->right); + assert (L->right->type == type_num || L->right->type == type_num_value || (L->right->type == type_type && L->right->type_len == 0)); + } + } + + if (!tl_finish_subtree (L)) { + TL_FAIL; + } + + tl_mark_vars (L); + return L; +} + +int __ok; +void tl_var_check_used (struct tl_var *v) { + __ok = __ok && (v->flags & 3); +} + +int tl_parse_combinator_decl (struct tree *T, int fun) { + assert (T->type == type_combinator_decl); + assert (T->nc >= 3); + namespace_level = 0; + tl_clear_vars (); + tl_clear_fields (); + TL_INIT (L); + TL_INIT (R); + + int i = 1; + while (i < T->nc - 2 && T->c[i]->type == type_opt_args) { + TL_TRY (tl_parse_opt_args (T->c[i]), L); + i++; + } + while (i < T->nc - 2 && T->c[i]->type == type_args) { + TL_TRY (tl_parse_args (T->c[i]), L); + i++; + } + assert (i == T->nc - 2 && T->c[i]->type == type_equals); + i ++; + + R = tl_parse_result_type (T->c[i]); + if (!R) { TL_FAIL; } + + struct tl_type *t = tl_tree_get_type (R); + if (!fun && !t) { + TL_ERROR ("Only functions can return variables\n"); + } + assert (t || fun); + + assert (namespace_level == 0); + __ok = 1; + tree_act_tl_var (vars[0], tl_var_check_used); + if (!__ok) { + TL_ERROR ("Not all variables are used in right side\n"); + TL_FAIL; + } + + if (tl_get_constructor (T->c[0]->text, T->c[0]->len) || tl_get_function (T->c[0]->text, T->c[0]->len)) { + TL_ERROR ("Duplicate combinator id %.*s\n", T->c[0]->len, T->c[0]->text); + return 0; + } + struct tl_constructor *c = !fun ? tl_add_constructor (t, T->c[0]->text, T->c[0]->len, 0) : tl_add_function (t, T->c[0]->text, T->c[0]->len, 0); + if (!c) { TL_FAIL; } + c->left = L; + c->right = R; + + if (!c->name) { + tl_count_combinator_name (c); + } + tl_print_combinator (c); + + return 1; +} + +void change_var_ptrs (struct tl_combinator_tree *O, struct tl_combinator_tree *D, struct tree_var_value **V) { + if (!O || !D) { + assert (!O && !D); + return; + } + if (O->act == act_field) { + struct tl_type *t = tl_tree_get_type (O->left); + if (t && (!strcmp (t->id, "#") || !strcmp (t->id, "Type"))) { + tl_set_var_value (V, O, D); + } + } + if (O->act == act_var) { + assert (D->data == O->data); + D->data = tl_get_var_value (V, O->data); + assert (D->data); + } + change_var_ptrs (O->left, D->left, V); + change_var_ptrs (O->right, D->right, V); +} + +struct tl_combinator_tree *change_first_var (struct tl_combinator_tree *O, struct tl_combinator_tree **X, struct tl_combinator_tree *Y) { + if (!O) { return (void *)-2l; }; + if (O->act == act_field && !*X) { + struct tl_type *t = tl_tree_get_type (O->left); + if (t && !strcmp (t->id, "#")) { + if (Y->type != type_num && Y->type != type_num_value) { + TL_ERROR ("change_var: Type mistmatch\n"); + return 0; + } else { + *X = O; + return (void *)-1l; + } + } + if (t && !strcmp (t->id, "Type")) { + if (Y->type != type_type || Y->type_len != 0) { + TL_ERROR ("change_var: Type mistmatch\n"); + return 0; + } else { + *X = O; + return (void *)-1l; + } + } + } + if (O->act == act_var) { + if (O->data == *X) { + struct tl_combinator_tree *R = tl_tree_dup (Y); + if (O->type == type_num || O->type == type_num_value) { R->type_flags += O->type_flags; } + return R; + } + } + struct tl_combinator_tree *t; + t = change_first_var (O->left, X, Y); + if (!t) { return 0;} + if (t == (void *)-1l) { + t = change_first_var (O->right, X, Y); + if (!t) { return 0;} + if (t == (void *)-1l) { return (void *)-1l; } + if (t != (void *)-2l) { return t;} + return (void *)-1l; + } + if (t != (void *)-2l) { + O->left = t; + } + t = change_first_var (O->right, X, Y); + if (!t) { return 0;} + if (t == (void *)-1l) { + return O->left; + } + if (t != (void *)-2l) { + O->right = t; + } + return O; +} + + +int uniformize (struct tl_combinator_tree *L, struct tl_combinator_tree *R, struct tree_var_value **T); +struct tree_var_value **_T; +int __tok; +void check_nat_val (struct tl_var_value v) { + if (!__tok) { return; } + long long x = v.num_val; + struct tl_combinator_tree *L = v.val; + if (L->type == type_type) { return;} + while (1) { + if (L->type == type_num_value) { + if (x + L->type_flags < 0) { + __tok = 0; + return; + } else { + return; + } + } + assert (L->type == type_num); + x += L->type_flags; + x += tl_get_var_value_num (_T, L->data); + L = tl_get_var_value (_T, L->data); + if (!L) { return;} + } +} + +int check_constructors_equal (struct tl_combinator_tree *L, struct tl_combinator_tree *R, struct tree_var_value **T) { + if (!uniformize (L, R, T)) { return 0; } + __tok = 1; + _T = T; + tree_act_var_value (*T, check_nat_val); + return __tok; +} + +struct tl_combinator_tree *reduce_type (struct tl_combinator_tree *A, struct tl_type *t) { + assert (A); + if (A->type_len == t->params_num) { + assert (A->type_flags == t->params_types); + A->act = act_type; + A->type = type_type; + A->left = A->right = 0; + A->data = t; + return A; + } + A->left = reduce_type (A->left, t); + return A; +} + +struct tl_combinator_tree *change_value_var (struct tl_combinator_tree *O, struct tree_var_value **X) { + if (!O) { return (void *)-2l; }; + while (O->act == act_var) { + assert (O->data); + if (!tl_get_var_value (X, O->data)) { + break; + } + if (O->type == type_type) { + O = tl_tree_dup (tl_get_var_value (X, O->data)); + } else { + long long n = tl_get_var_value_num (X, O->data); + struct tl_combinator_tree *T = tl_get_var_value (X, O->data); + O->data = T->data; + O->type = T->type; + O->act = T->act; + O->type_flags = O->type_flags + n + T->type_flags; + } + } + if (O->act == act_field) { + if (tl_get_var_value (X, O)) { return (void *)-1l; } + } + struct tl_combinator_tree *t; + t = change_value_var (O->left, X); + if (!t) { return 0;} + if (t == (void *)-1l) { + t = change_value_var (O->right, X); + if (!t) { return 0;} + if (t == (void *)-1l) { return (void *)-1l; } + if (t != (void *)-2l) { return t;} + return (void *)-1l; + } + if (t != (void *)-2l) { + O->left = t; + } + t = change_value_var (O->right, X); + if (!t) { return 0;} + if (t == (void *)-1l) { + return O->left; + } + if (t != (void *)-2l) { + O->right = t; + } + return O; +} + +int tl_parse_partial_type_app_decl (struct tree *T) { + assert (T->type == type_partial_type_app_decl); + assert (T->nc >= 1); + + assert (T->c[0]->type == type_boxed_type_ident); + struct tl_type *t = tl_get_type (T->c[0]->text, T->c[0]->len); + if (!t) { + TL_ERROR ("Can not make partial app for unknown type\n"); + return 0; + } + + tl_type_finalize (t); + + struct tl_combinator_tree *L = tl_parse_ident (T->c[0], 0); + assert (L); + int i; + tl_buf_reset (); + int cc = T->nc - 1; + for (i = 1; i < T->nc; i++) { + TL_TRY (tl_parse_any_term (T->c[i], 0), L); + tl_buf_add_tree (L->right, 1); + } + + while (L->type_len) { + struct tl_combinator_tree *C = alloc_ctree_node (); + C->act = act_var; + C->type = (L->type_flags & 1) ? type_num : type_type; + C->type_len = 0; + C->type_flags = 0; + C->data = (void *)-1l; + L = tl_union (L, C); + if (!L) { return 0; } + } + + + static char _buf[100000]; + snprintf (_buf, 100000, "%s%.*s", t->id, buf_pos, buf); + struct tl_type *nt = tl_add_type (_buf, strlen (_buf), t->params_num - cc, t->params_types >> cc); + assert (nt); + //snprintf (_buf, 100000, "%s #", t->id); + //nt->real_id = strdup (_buf); + + for (i = 0; i < t->constructors_num; i++) { + struct tl_constructor *c = t->constructors[i]; + struct tree_var_value *V = 0; + TL_INIT (A); + TL_INIT (B); + A = tl_tree_dup (c->left); + B = tl_tree_dup (c->right); + + struct tree_var_value *W = 0; + change_var_ptrs (c->left, A, &W); + change_var_ptrs (c->right, B, &W); + + + if (!check_constructors_equal (B, L, &V)) { continue; } + B = reduce_type (B, nt); + A = change_value_var (A, &V); + if (A == (void *)-1l) { A = 0;} + B = change_value_var (B, &V); + assert (B != (void *)-1l); + snprintf (_buf, 100000, "%s%.*s", c->id, buf_pos, buf); + + struct tl_constructor *r = tl_add_constructor (nt, _buf, strlen (_buf), 1); + snprintf (_buf, 100000, "%s", c->id); + r->real_id = tstrdup (_buf); + + r->left = A; + r->right = B; + if (!r->name) { + tl_count_combinator_name (r); + } + tl_print_combinator (r); + } + + return 1; +} + +int tl_parse_partial_comb_app_decl (struct tree *T, int fun) { + assert (T->type == type_partial_comb_app_decl); + + struct tl_constructor *c = !fun ? tl_get_constructor (T->c[0]->text, T->c[0]->len) : tl_get_function (T->c[0]->text, T->c[0]->len); + if (!c) { + TL_ERROR ("Can not make partial app for undefined combinator\n"); + return 0; + } + + //TL_INIT (K); + //static char buf[1000]; + //int x = sprintf (buf, "%s", c->id); + TL_INIT (L); + TL_INIT (R); + L = tl_tree_dup (c->left); + R = tl_tree_dup (c->right); + + + struct tree_var_value *V = 0; + change_var_ptrs (c->left, L, &V); + change_var_ptrs (c->right, R, &V); + V = tree_clear_var_value (V); + + int i; + tl_buf_reset (); + for (i = 1; i < T->nc; i++) { + TL_INIT (X); + TL_INIT (Z); + X = tl_parse_any_term (T->c[i], 0); + struct tl_combinator_tree *K = 0; + if (!(Z = change_first_var (L, &K, X))) { + TL_FAIL; + } + L = Z; + if (!K) { + TL_ERROR ("Partial app: not enougth variables (i = %d)\n", i); + TL_FAIL; + } + if (!(Z = change_first_var (R, &K, X))) { + TL_FAIL; + } + assert (Z == R); + tl_buf_add_tree (X, 1); + } + + static char _buf[100000]; + snprintf (_buf, 100000, "%s%.*s", c->id, buf_pos, buf); +// fprintf (stderr, "Local id: %s\n", _buf); + + struct tl_constructor *r = !fun ? tl_add_constructor (c->type, _buf, strlen (_buf), 1) : tl_add_function (c->type, _buf, strlen (_buf), 1); + r->left = L; + r->right = R; + snprintf (_buf, 100000, "%s", c->id); + r->real_id = tstrdup (_buf); + if (!r->name) { + tl_count_combinator_name (r); + } + tl_print_combinator (r); + return 1; +} + + +int tl_parse_partial_app_decl (struct tree *T, int fun) { + assert (T->type == type_partial_app_decl); + assert (T->nc == 1); + if (T->c[0]->type == type_partial_comb_app_decl) { + return tl_parse_partial_comb_app_decl (T->c[0], fun); + } else { + if (fun) { + TL_ERROR ("Partial type app in functions block\n"); + TL_FAIL; + } + return tl_parse_partial_type_app_decl (T->c[0]); + } +} + +int tl_parse_final_final (struct tree *T) { + assert (T->type == type_final_final); + assert (T->nc == 1); + struct tl_type *R; + if ((R = tl_get_type (T->c[0]->text, T->c[0]->len))) { + R->flags |= 1; + return 1; + } else { + TL_ERROR ("Final statement for type `%.*s` before declaration\n", T->c[0]->len, T->c[0]->text); + TL_FAIL; + } +} + +int tl_parse_final_new (struct tree *T) { + assert (T->type == type_final_new); + assert (T->nc == 1); + if (tl_get_type (T->c[0]->text, T->c[0]->len)) { + TL_ERROR ("New statement: type `%.*s` already declared\n", T->c[0]->len, T->c[0]->text); + TL_FAIL; + } else { + return 1; + } +} + +int tl_parse_final_empty (struct tree *T) { + assert (T->type == type_final_empty); + assert (T->nc == 1); + if (tl_get_type (T->c[0]->text, T->c[0]->len)) { + TL_ERROR ("New statement: type `%.*s` already declared\n", T->c[0]->len, T->c[0]->text); + TL_FAIL; + } + struct tl_type *t = tl_add_type (T->c[0]->text, T->c[0]->len, 0, 0); + assert (t); + t->flags |= 1 | FLAG_EMPTY; + return 1; +} + +int tl_parse_final_decl (struct tree *T, int fun) { + assert (T->type == type_final_decl); + assert (!fun); + assert (T->nc == 1); + switch (T->c[0]->type) { + case type_final_new: + return tl_parse_final_new (T->c[0]); + case type_final_final: + return tl_parse_final_final (T->c[0]); + case type_final_empty: + return tl_parse_final_empty (T->c[0]); + default: + assert (0); + return 0; + } +} + +int tl_parse_builtin_combinator_decl (struct tree *T, int fun) { + if (fun) { + TL_ERROR ("Builtin type can not be described in function block\n"); + return -1; + } + assert (T->type == type_builtin_combinator_decl); + assert (T->nc == 2); + assert (T->c[0]->type == type_full_combinator_id); + assert (T->c[1]->type == type_boxed_type_ident); + + + if ((!mystrcmp2 (T->c[0]->text, T->c[0]->len, "int") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "Int")) || + (!mystrcmp2 (T->c[0]->text, T->c[0]->len, "long") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "Long")) || + (!mystrcmp2 (T->c[0]->text, T->c[0]->len, "double") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "Double")) || + (!mystrcmp2 (T->c[0]->text, T->c[0]->len, "string") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "String"))) { + struct tl_type *t = tl_add_type (T->c[1]->text, T->c[1]->len, 0, 0); + if (!t) { + return 0; + } + struct tl_constructor *c = tl_add_constructor (t, T->c[0]->text, T->c[0]->len, 0); + if (!c) { + return 0; + } + + c->left = alloc_ctree_node (); + c->left->act = act_question_mark; + c->left->type = type_list_item; + + c->right = alloc_ctree_node (); + c->right->act = act_type; + c->right->data = t; + c->right->type = type_type; + + if (!c->name) { + tl_count_combinator_name (c); + } + tl_print_combinator (c); + } else { + TL_ERROR ("Unknown builting type `%.*s`\n", T->c[0]->len, T->c[0]->text); + return 0; + } + + return 1; +} + +int tl_parse_declaration (struct tree *T, int fun) { + assert (T->type == type_declaration); + assert (T->nc == 1); + switch (T->c[0]->type) { + case type_combinator_decl: + return tl_parse_combinator_decl (T->c[0], fun); + case type_partial_app_decl: + return tl_parse_partial_app_decl (T->c[0], fun); + case type_final_decl: + return tl_parse_final_decl (T->c[0], fun); + case type_builtin_combinator_decl: + return tl_parse_builtin_combinator_decl (T->c[0], fun); + default: + assert (0); + return 0; + } +} + +int tl_parse_constr_declarations (struct tree *T) { + assert (T->type == type_constr_declarations); + int i; + for (i = 0; i < T->nc; i++) { + TL_TRY_PES (tl_parse_declaration (T->c[i], 0)); + } + return 1; +} + +int tl_parse_fun_declarations (struct tree *T) { + assert (T->type == type_fun_declarations); + int i; + for (i = 0; i < T->nc; i++) { + TL_TRY_PES (tl_parse_declaration (T->c[i], 1)); + } + return 1; +} + +int tl_tree_lookup_value (struct tl_combinator_tree *L, void *var, struct tree_var_value **T) { + if (!L) { + return -1; + } + if (L->act == act_var && L->data == var) { + return 0; + } + if (L->act == act_var) { + struct tl_combinator_tree *E = tl_get_var_value (T, L->data); + if (!E) { return -1;} + else { return tl_tree_lookup_value (E, var, T); } + } + if (tl_tree_lookup_value (L->left, var, T) >= 0) { return 1; } + if (tl_tree_lookup_value (L->right, var, T) >= 0) { return 1; } + return -1; +} + +int tl_tree_lookup_value_nat (struct tl_combinator_tree *L, void *var, long long x, struct tree_var_value **T) { + assert (L); + if (L->type == type_num_value) { return -1; } + assert (L->type == type_num); + assert (L->act == act_var); + if (L->data == var) { + return x == L->type_flags ? 0 : 1; + } else { + if (!tl_get_var_value (T, L->data)) { + return -1; + } + return tl_tree_lookup_value_nat (tl_get_var_value (T, L->data), var, x + tl_get_var_value_num (T, L->data), T); + } + +} + +int uniformize (struct tl_combinator_tree *L, struct tl_combinator_tree *R, struct tree_var_value **T) { + if (!L || !R) { + assert (!L && !R); + return 1; + } + if (R->act == act_var) { + struct tl_combinator_tree *_ = R; R = L; L = _; + } + + if (L->type == type_type) { + if (R->type != type_type || L->type_len != R->type_len || L->type_flags != R->type_flags) { + return 0; + } + if (R->data == (void *)-1l || L->data == (void *)-1l) { return 1;} + if (L->act == act_var) { + int x = tl_tree_lookup_value (R, L->data, T); + if (x > 0) { +// if (tl_tree_lookup_value (R, L->data, T) > 0) { + return 0; + } + if (x == 0) { + return 1; + } + struct tl_combinator_tree *E = tl_get_var_value (T, L->data); + if (!E) { + tl_set_var_value (T, L->data, R); + return 1; + } else { + return uniformize (E, R, T); + } + } else { + if (L->act != R->act || L->data != R->data) { + return 0; + } + return uniformize (L->left, R->left, T) && uniformize (L->right, R->right, T); + } + } else { + assert (L->type == type_num || L->type == type_num_value); + if (R->type != type_num && R->type != type_num_value) { + return 0; + } + assert (R->type == type_num || R->type == type_num_value); + if (R->data == (void *)-1l || L->data == (void *)-1l) { return 1;} + long long x = 0; + struct tl_combinator_tree *K = L; + while (1) { + x += K->type_flags; + if (K->type == type_num_value) { + break; + } + if (!tl_get_var_value (T, K->data)) { + int s = tl_tree_lookup_value_nat (R, K->data, K->type_flags, T); + if (s > 0) { + return 0; + } + if (s == 0) { + return 1; + } + /*tl_set_var_value_num (T, K->data, R, -x); + return 1;*/ + break; + } + x += tl_get_var_value_num (T, K->data); + K = tl_get_var_value (T, K->data); + } + long long y = 0; + struct tl_combinator_tree *M = R; + while (1) { + y += M->type_flags; + if (M->type == type_num_value) { + break; + } + if (!tl_get_var_value (T, M->data)) { + int s = tl_tree_lookup_value_nat (L, M->data, M->type_flags, T); + if (s > 0) { + return 0; + } + if (s == 0) { + return 1; + } + /*tl_set_var_value_num (T, M->data, L, -y); + return 1;*/ + break; + } + y += tl_get_var_value_num (T, M->data); + M = tl_get_var_value (T, M->data); + } + if (K->type == type_num_value && M->type == type_num_value) { + return x == y; + } + if (M->type == type_num_value) { + tl_set_var_value_num (T, K->data, M, -(x - y + M->type_flags)); + return 1; + } else if (K->type == type_num_value) { + tl_set_var_value_num (T, M->data, K, -(y - x + K->type_flags)); + return 1; + } else { + if (x >= y) { + tl_set_var_value_num (T, K->data, M, -(x - y + M->type_flags)); + } else { + tl_set_var_value_num (T, M->data, K, -(y - x + K->type_flags)); + } + return 1; + } + } + return 0; +} + + +void tl_type_check (struct tl_type *t) { + if (!__ok) return; + if (!strcmp (t->id, "#")) { t->name = 0x70659eff; return; } + if (!strcmp (t->id, "Type")) { t->name = 0x2cecf817; return; } + if (t->constructors_num <= 0 && !(t->flags & FLAG_EMPTY)) { + TL_ERROR ("Type %s has no constructors\n", t->id); + __ok = 0; + return; + } + int i, j; + t->name = 0; + for (i = 0; i < t->constructors_num; i++) { + t->name ^= t->constructors[i]->name; + } + for (i = 0; i < t->constructors_num; i++) { + for (j = i + 1; j < t->constructors_num; j++) { + struct tree_var_value *v = 0; + if (check_constructors_equal (t->constructors[i]->right, t->constructors[j]->right, &v)) { + t->flags |= 16; + } + } + } + if ((t->flags & 24) == 24) { + TL_WARNING ("Warning: Type %s has overlapping costructors, but it is used with `%%`\n", t->id); + } + int z = 0; + int sid = 0; + for (i = 0; i < t->constructors_num; i++) if (*t->constructors[i]->id == '_') { + z ++; + sid = i; + } + if (z > 1) { + TL_ERROR ("Type %s has %d default constructors\n", t->id, z); + __ok = 0; + return; + } + if (z == 1 && (t->flags & 8)) { + TL_ERROR ("Type %s has default constructors and used bare\n", t->id); + __ok = 0; + return; + } + if (z) { + struct tl_constructor *c; + c = t->constructors[sid]; + t->constructors[sid] = t->constructors[t->constructors_num - 1]; + t->constructors[t->constructors_num - 1] = c; + } +} + +struct tl_program *tl_parse (struct tree *T) { + assert (T); + assert (T->type == type_tl_program); + int i; + tl_program_cur = talloc (sizeof (*tl_program_cur)); + tl_add_type ("#", 1, 0, 0); + tl_add_type ("Type", 4, 0, 0); + for (i = 0; i < T->nc; i++) { + if (T->c[i]->type == type_constr_declarations) { TL_TRY_PES (tl_parse_constr_declarations (T->c[i])); } + else { TL_TRY_PES (tl_parse_fun_declarations (T->c[i])) } + } + __ok = 1; + tree_act_tl_type (tl_type_tree, tl_type_check); + if (!__ok) { + return 0; + } + return tl_program_cur; +} + +int __f; +int num = 0; + +void wint (int a) { +// printf ("%d ", a); + a = htole32 (a); + assert (write (__f, &a, 4) == 4); +} + +void wdata (const void *x, int len) { + assert (write (__f, x, len) == len); +} + +void wstr (const char *s) { + if (s) { +// printf ("\"%s\" ", s); + int x = strlen (s); + if (x <= 254) { + unsigned char x_c = (unsigned char)x; + assert (write (__f, &x_c, 1) == 1); + } else { + fprintf (stderr, "String is too big...\n"); + assert (0); + } + wdata (s, x); + x ++; // The header, containing the length, which is 1 byte + int t = 0; + if (x & 3) { + // Let's hope it's truly zero on every platform + wdata (&t, 4 - (x & 3)); + } + } else { +// printf ("<none> "); + wint (0); + } +} + +void wll (long long a) { +// printf ("%lld ", a); + a = htole64 (a); + assert (write (__f, &a, 8) == 8); +} + +int count_list_size (struct tl_combinator_tree *T) { + assert (T->type == type_list || T->type == type_list_item); + if (T->type == type_list_item) { + return 1; + } else { + return count_list_size (T->left) + count_list_size (T->right); + } +} + +void write_type_flags (long long flags) { + int new_flags = 0; + if (flags & 1) { + new_flags |= FLAG_BARE; + } + if (flags & FLAG_DEFAULT_CONSTRUCTOR) { + new_flags |= FLAG_DEFAULT_CONSTRUCTOR; + } + wint (new_flags); +} + +void write_field_flags (long long flags) { + int new_flags = 0; + //fprintf (stderr, "%lld\n", flags); + if (flags & 1) { + new_flags |= FLAG_BARE; + } + if (flags & 32) { + new_flags |= FLAG_OPT_VAR; + } + if (flags & FLAG_EXCL) { + new_flags |= FLAG_EXCL; + } + if (flags & FLAG_OPT_FIELD) { + // new_flags |= FLAG_OPT_FIELD; + new_flags |= 2; + } + if (flags & (1 << 21)) { + new_flags |= 4; + } + wint (new_flags); +} + +void write_var_type_flags (long long flags) { + int new_flags = 0; + if (flags & 1) { + new_flags |= FLAG_BARE; + } + if (new_flags & FLAG_BARE) { + TL_ERROR ("Sorry, bare vars are not (yet ?) supported.\n"); + assert (!(new_flags & FLAG_BARE)); + } + wint (new_flags); +} + +void write_tree (struct tl_combinator_tree *T, int extra, struct tree_var_value **v, int *last_var); +void write_args (struct tl_combinator_tree *T, struct tree_var_value **v, int *last_var) { + assert (T->type == type_list || T->type == type_list_item); + if (T->type == type_list) { + assert (T->act == act_union); + assert (T->left); + assert (T->right); + write_args (T->left, v, last_var); + write_args (T->right, v, last_var); + return; + } + wint (TLS_ARG_V2); + assert (T->act == act_field); + assert (T->left); + wstr (T->data && strcmp (T->data, "_") ? T->data : 0); + long long f = T->flags; + if (T->left->act == act_opt_field) { + f |= (1 << 20); + } + if (T->left->act == act_type && T->left->data && (!strcmp (((struct tl_type *)T->left->data)->id, "#") || !strcmp (((struct tl_type *)T->left->data)->id, "Type"))) { + write_field_flags (f | (1 << 21)); + wint (*last_var); + *last_var = (*last_var) + 1; + tl_set_var_value_num (v, T, 0, (*last_var) - 1); + } else { + write_field_flags (f); + } + write_tree (T->left, 0, v, last_var); +} + +void write_array (struct tl_combinator_tree *T, struct tree_var_value **v, int *last_var) { + wint (TLS_ARRAY); + write_tree (T->left, 0, v, last_var); + write_tree (T->right, 0, v, last_var); +} + +void write_type_rec (struct tl_combinator_tree *T, int cc, struct tree_var_value **v, int *last_var) { + if (T->act == act_arg) { + write_type_rec (T->left, cc + 1, v, last_var); + if (T->right->type == type_num_value || T->right->type == type_num) { + wint (TLS_EXPR_NAT); + } else { + wint (TLS_EXPR_TYPE); + } + write_tree (T->right, 0, v, last_var); + } else { + assert (T->act == act_var || T->act == act_type); + if (T->act == act_var) { + assert (!cc); + wint (TLS_TYPE_VAR); + wint (tl_get_var_value_num (v, T->data)); + write_var_type_flags (T->flags); + //wint (T->flags); + } else { + wint (TLS_TYPE_EXPR); + struct tl_type *t = T->data; + wint (t->name); + write_type_flags (T->flags); +// wint (T->flags); + wint (cc); +// fprintf (stderr, "cc = %d\n", cc); + } + } +} + +void write_opt_type (struct tl_combinator_tree *T, struct tree_var_value **v, int *last_var) { + wint (tl_get_var_value_num (v, T->left->data)); + wint (T->left->type_flags); +// write_tree (T->right, 0, v, last_var); + assert (T); + T = T->right; + switch (T->type) { + case type_type: + if (T->act == act_array) { + write_array (T, v, last_var); + } else if (T->act == act_type || T->act == act_var || T->act == act_arg) { + write_type_rec (T, 0, v, last_var); + } else { + assert (0); + } + break; + default: + assert (0); + } +} + +void write_tree (struct tl_combinator_tree *T, int extra, struct tree_var_value **v, int *last_var) { + assert (T); + switch (T->type) { + case type_list_item: + case type_list: + if (extra) { + wint (TLS_COMBINATOR_RIGHT_V2); + } + wint (count_list_size (T)); + write_args (T, v, last_var); + break; + case type_num_value: + wint ((int)TLS_NAT_CONST); + wint (T->type_flags); + break; + case type_num: + wint ((int)TLS_NAT_VAR); + wint (T->type_flags); + wint (tl_get_var_value_num (v, T->data)); + break; + case type_type: + if (T->act == act_array) { + write_array (T, v, last_var); + } else if (T->act == act_type || T->act == act_var || T->act == act_arg) { + write_type_rec (T, 0, v, last_var); + } else { + assert (T->act == act_opt_field); + write_opt_type (T, v, last_var); + } + break; + default: + assert (0); + } +} + +void write_type (struct tl_type *t) { + wint (TLS_TYPE); + wint (t->name); + wstr (t->id); + wint (t->constructors_num); + wint (t->flags); + wint (t->params_num); + wll (t->params_types); +} + +int is_builtin_type (const char *id) { + return !strcmp (id, "int") || !strcmp (id, "long") || !strcmp (id, "double") || !strcmp (id, "string"); +} + +void write_combinator (struct tl_constructor *c) { + wint (c->name); + wstr (c->id); + wint (c->type ? c->type->name : 0); + struct tree_var_value *T = 0; + int x = 0; + assert (c->right); + if (c->left) { + if (is_builtin_type (c->id)) { + wint (TLS_COMBINATOR_LEFT_BUILTIN); + } else { + wint (TLS_COMBINATOR_LEFT); + // FIXME: What is that? +// wint (count_list_size (c->left)); + write_tree (c->left, 0, &T, &x); + } + } else { + wint (TLS_COMBINATOR_LEFT); + wint (0); + } + wint (TLS_COMBINATOR_RIGHT_V2); + write_tree (c->right, 1, &T, &x); +} + +void write_constructor (struct tl_constructor *c) { + wint (TLS_COMBINATOR); + write_combinator (c); +} + +void write_function (struct tl_constructor *c) { + wint (TLS_COMBINATOR); + write_combinator (c); +} + +void write_type_constructors (struct tl_type *t) { + int i; + for (i = 0; i < t->constructors_num; i++) { + write_constructor (t->constructors[i]); + } +} + +void write_types (int f) { + __f = f; + wint (TLS_SCHEMA_V2); + wint (0); +#ifdef TL_PARSER_NEED_TIME + wint (time (0)); +#else + /* Make the tlo reproducible by default. Rationale: https://wiki.debian.org/ReproducibleBuilds/Howto#Introduction */ + wint (0); +#endif + num = 0; + wint (total_types_num); + tree_act_tl_type (tl_type_tree, write_type); + wint (total_constructors_num); + tree_act_tl_type (tl_type_tree, write_type_constructors); + wint (total_functions_num); + tree_act_tl_constructor (tl_function_tree, write_function); +} diff --git a/tl-parser/tl-parser.h b/tl-parser/tl-parser.h new file mode 100644 index 0000000..faa142b --- /dev/null +++ b/tl-parser/tl-parser.h @@ -0,0 +1,206 @@ +/* + This file is part of tgl-libary/tlc + + Tgl-library/tlc 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. + + Tgl-library/tlc 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 tgl-library/tlc. If not, see <http://www.gnu.org/licenses/>. + + Copyright Vitaly Valtman 2014 + + It is derivative work of VK/KittenPHP-DB-Engine (https://github.com/vk-com/kphp-kdb/) + Copyright 2012-2013 Vkontakte Ltd + 2012-2013 Vitaliy Valtman + +*/ + +#ifndef __TL_PARSER_NEW_H__ +#define __TL_PARSER_NEW_H__ +enum lex_type { + lex_error, + lex_char, + lex_triple_minus, + lex_uc_ident, + lex_lc_ident, + lex_eof, + lex_final, + lex_new, + lex_none, + lex_num, + lex_empty +}; + + +struct curlex { + char *ptr; + int len; + enum lex_type type; + int flags; +}; + +struct parse { + char *text; + int pos; + int len; + int line; + int line_pos; + struct curlex lex; +}; + + +enum tree_type { + type_tl_program, + type_fun_declarations, + type_constr_declarations, + type_declaration, + type_combinator_decl, + type_equals, + type_partial_app_decl, + type_final_decl, + type_full_combinator_id, + type_opt_args, + type_args, + type_args1, + type_args2, + type_args3, + type_args4, + type_boxed_type_ident, + type_subexpr, + type_partial_comb_app_decl, + type_partial_type_app_decl, + type_final_new, + type_final_final, + type_final_empty, +// type_type, + type_var_ident, + type_var_ident_opt, + type_multiplicity, + type_type_term, + type_term, + type_percent, + type_result_type, + type_expr, + type_nat_term, + type_combinator_id, + type_nat_const, + type_type_ident, + type_builtin_combinator_decl, + type_exclam, + type_optional_arg_def +}; + +struct tree { + char *text; + int len; + enum tree_type type; + int lex_line; + int lex_line_pos; + int flags; + int size; + int nc; + struct tree **c; +}; + + +#define TL_ACT(x) (x == act_var ? "act_var" : x == act_field ? "act_field" : x == act_plus ? "act_plus" : x == act_type ? "act_type" : x == act_nat_const ? "act_nat_const" : x == act_array ? "act_array" : x == act_question_mark ? "act_question_mark" : \ + x == act_union ? "act_union" : x == act_arg ? "act_arg" : x == act_opt_field ? "act_opt_field" : "act_unknown") + +#define TL_TYPE(x) (x == type_num ? "type_num" : x == type_type ? "type_type" : x == type_list_item ? "type_list_item" : x == type_list ? "type_list" : x == type_num_value ? "type_num_value" : "type_unknown") +enum combinator_tree_action { + act_var, + act_field, + act_plus, + act_type, + act_nat_const, + act_array, + act_question_mark, + act_union, + act_arg, + act_opt_field +}; + +enum combinator_tree_type { + type_num, + type_num_value, + type_type, + type_list_item, + type_list +}; + +struct tl_combinator_tree { + enum combinator_tree_action act; + struct tl_combinator_tree *left, *right; + char *name; + void *data; + long long flags; + enum combinator_tree_type type; + int type_len; + long long type_flags; +}; + + +struct tl_program { + int types_num; + int functions_num; + int constructors_num; + struct tl_type **types; + struct tl_function **functions; +// struct tl_constuctor **constructors; +}; + +struct tl_type { + char *id; + char *print_id; + char *real_id; + unsigned name; + int flags; + + int params_num; + long long params_types; + + int constructors_num; + struct tl_constructor **constructors; +}; + +struct tl_constructor { + char *id; + char *print_id; + char *real_id; + unsigned name; + struct tl_type *type; + + struct tl_combinator_tree *left; + struct tl_combinator_tree *right; +}; + +struct tl_var { + char *id; + struct tl_combinator_tree *ptr; + int type; + int flags; +}; + +struct parse *tl_init_parse_file (const char *fname); +struct tree *tl_parse_lex (struct parse *P); +void tl_print_parse_error (void); +struct tl_program *tl_parse (struct tree *T); + +void write_types (int f); + +#define FLAG_BARE 1 +#define FLAG_OPT_VAR (1 << 17) +#define FLAG_EXCL (1 << 18) +#define FLAG_OPT_FIELD (1 << 20) +#define FLAG_IS_VAR (1 << 21) +#define FLAG_DEFAULT_CONSTRUCTOR (1 << 25) +#define FLAG_EMPTY (1 << 10) + +#endif diff --git a/tl-parser/tl-tl.h b/tl-parser/tl-tl.h new file mode 100644 index 0000000..8bc0a70 --- /dev/null +++ b/tl-parser/tl-tl.h @@ -0,0 +1,55 @@ +/* + This file is part of VK/KittenPHP-DB-Engine. + + VK/KittenPHP-DB-Engine 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. + + VK/KittenPHP-DB-Engine 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 VK/KittenPHP-DB-Engine. If not, see <http://www.gnu.org/licenses/>. + + This program is released under the GPL with the additional exemption + that compiling, linking, and/or using OpenSSL is allowed. + You are free to remove this exemption from derived works. + + Copyright 2012-2013 Vkontakte Ltd + 2012-2013 Vitaliy Valtman +*/ + +#ifndef __TL_TL_H__ +#define __TL_TL_H__ + +// Current tl-tl schema is V2 +// See https://core.telegram.org/mtproto/TL-tl + +#define TLS_SCHEMA_V2 0x3a2f9be2 +#define TLS_TYPE 0x12eb4386 +#define TLS_COMBINATOR 0x5c0a1ed5 +#define TLS_COMBINATOR_LEFT_BUILTIN 0xcd211f63 +#define TLS_COMBINATOR_LEFT 0x4c12c6d9 +#define TLS_COMBINATOR_RIGHT_V2 0x2c064372 +#define TLS_ARG_V2 0x29dfe61b + +#define TLS_EXPR_TYPE 0xecc9da78 +#define TLS_EXPR_NAT 0xdcb49bd8 + +#define TLS_NAT_CONST 0xdcb49bd8 +#define TLS_NAT_VAR 0x4e8a14f0 +#define TLS_TYPE_VAR 0x0142ceae +#define TLS_ARRAY 0xd9fb20de +#define TLS_TYPE_EXPR 0xc1863d08 + +/* Deprecated (old versions), read-only */ +#define TLS_TREE_NAT_CONST 0xc09f07d7 +#define TLS_TREE_NAT_VAR 0x90ea6f58 +#define TLS_TREE_TYPE_VAR 0x1caa237a +#define TLS_TREE_ARRAY 0x80479360 +#define TLS_TREE_TYPE 0x10f32190 + +#endif diff --git a/tl-parser/tlc.c b/tl-parser/tlc.c new file mode 100644 index 0000000..cd0300f --- /dev/null +++ b/tl-parser/tlc.c @@ -0,0 +1,165 @@ +/* + This file is part of tl-parser + + tl-parser 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. + + tl-parser 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 tl-parser. If not, see <http://www.gnu.org/licenses/>. + + Copyright Vitaly Valtman 2014 + + It is derivative work of VK/KittenPHP-DB-Engine (https://github.com/vk-com/kphp-kdb/) + Copyright 2012-2013 Vkontakte Ltd + 2012-2013 Vitaliy Valtman + +*/ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "tl-parser.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <signal.h> +#include "config.h" + +#ifdef HAVE_EXECINFO_H +#include <execinfo.h> +#endif +#include <stdarg.h> + +int verbosity; +int output_expressions; +void usage (void) { + printf ("usage: tl-parser [-v] [-h] <TL-schema-file>\n" + "\tTL compiler\n" + "\t-v\toutput statistical and debug information into stderr\n" + "\t-E\twhenever is possible output to stdout expressions\n" + "\t-e <file>\texport serialized schema to file\n" + ); + exit (2); +} + +int vkext_write (const char *filename) { + int f = open (filename, O_CREAT | O_WRONLY | O_TRUNC, 0640); + assert (f >= 0); + write_types (f); + close (f); + return 0; +} + +void logprintf (const char *format, ...) __attribute__ ((format (printf, 1, 2))); +void logprintf (const char *format __attribute__ ((unused)), ...) { + va_list ap; + va_start (ap, format); + vfprintf (stderr, format, ap); + va_end (ap); +} + +void hexdump (int *in_ptr, int *in_end) { + int *ptr = in_ptr; + while (ptr < in_end) { printf (" %08x", *(ptr ++)); } + printf ("\n"); +} + +#ifdef HAVE_EXECINFO_H +void print_backtrace (void) { + void *buffer[255]; + const int calls = backtrace (buffer, sizeof (buffer) / sizeof (void *)); + backtrace_symbols_fd (buffer, calls, 1); +} +#else +void print_backtrace (void) { + if (write (1, "No libexec. Backtrace disabled\n", 32) < 0) { + // Sad thing + } +} +#endif + +void sig_segv_handler (int signum __attribute__ ((unused))) { + if (write (1, "SIGSEGV received\n", 18) < 0) { + // Sad thing + } + print_backtrace (); + exit (EXIT_FAILURE); +} + +void sig_abrt_handler (int signum __attribute__ ((unused))) { + if (write (1, "SIGABRT received\n", 18) < 0) { + // Sad thing + } + print_backtrace (); + exit (EXIT_FAILURE); +} + +int main (int argc, char **argv) { + signal (SIGSEGV, sig_segv_handler); + signal (SIGABRT, sig_abrt_handler); + int i; + char *vkext_file = 0; + while ((i = getopt (argc, argv, "Ehve:w:")) != -1) { + switch (i) { + case 'E': + output_expressions++; + break; + case 'h': + usage (); + return 2; + case 'e': + vkext_file = optarg; + break; + case 'v': + verbosity++; + break; + } + } + + if (argc != optind + 1) { + usage (); + } + + + struct parse *P = tl_init_parse_file (argv[optind]); + if (!P) { + return 0; + } + struct tree *T; + if (!(T = tl_parse_lex (P))) { + fprintf (stderr, "Error in parse:\n"); + tl_print_parse_error (); + return 0; + } else { + if (verbosity) { + fprintf (stderr, "Parse ok\n"); + } + if (!tl_parse (T)) { + if (verbosity) { + fprintf (stderr, "Fail\n"); + } + return 1; + } else { + if (verbosity) { + fprintf (stderr, "Ok\n"); + } + } + } + if (vkext_file) { + vkext_write (vkext_file); + } + return 0; +} |