From 03a7fda8ba643155bbf7334713b8d9489248fa24 Mon Sep 17 00:00:00 2001 From: jo Date: Wed, 8 Jul 1998 07:49:59 +0000 Subject: [PATCH] Initial revision --- configure | 2182 +++++++++++++++++++++++++++++++++++++++++++ help/Help_0 | 18 + help/Help_1 | 98 ++ help/Help_2 | 32 + help/Help_3 | 218 +++++ help/Help_4 | 240 +++++ help/Help_m | 16 + install-sh | 238 +++++ man/Makefile | 4 + man/flrn.1.iso | 225 +++++ man/zapaccents | 8 + src/ChangeLog | 222 +++++ src/Makefile.in | 80 ++ src/art_group.c | 900 ++++++++++++++++++ src/art_group.h | 102 ++ src/compatibility.c | 15 + src/compatibility.h | 150 +++ src/config.h | 88 ++ src/config.h.in | 87 ++ src/extern.h | 242 +++++ src/flrn.h | 16 + src/flrn_color.c | 420 +++++++++ src/flrn_color.h | 30 + src/flrn_config.h | 236 +++++ src/flrn_files.c | 212 +++++ src/flrn_filter.c | 290 ++++++ src/flrn_filter.h | 39 + src/flrn_format.c | 546 +++++++++++ src/flrn_glob.h | 48 + src/flrn_help.c | 38 + src/flrn_inter.c | 2051 ++++++++++++++++++++++++++++++++++++++++ src/flrn_inter.h | 27 + src/flrn_menus.c | 187 ++++ src/flrn_menus.h | 15 + src/flrn_pager.c | 142 +++ src/flrn_pager.h | 38 + src/flrn_regexp.c | 78 ++ src/flrn_shell.c | 143 +++ src/flrn_slang.c | 307 ++++++ src/flrn_slang.h | 22 + src/flrn_string.c | 150 +++ src/flrn_string.h | 17 + src/flrn_tcp.c | 494 ++++++++++ src/flrn_xover.c | 453 +++++++++ src/group.c | 697 ++++++++++++++ src/group.h | 50 + src/main.c | 109 +++ src/options.c | 463 +++++++++ src/options.h | 156 ++++ src/pathdef.h | 3 + src/post.c | 1098 ++++++++++++++++++++++ src/post.h | 20 + src/rfc2047.c | 385 ++++++++ src/rfc2047.h | 4 + src/site_config.h | 46 + src/tty_display.c | 895 ++++++++++++++++++ src/tty_keyboard.c | 154 +++ src/version.h | 7 + 58 files changed, 15251 insertions(+) create mode 100755 configure create mode 100644 help/Help_0 create mode 100644 help/Help_1 create mode 100644 help/Help_2 create mode 100644 help/Help_3 create mode 100644 help/Help_4 create mode 100644 help/Help_m create mode 100755 install-sh create mode 100644 man/Makefile create mode 100644 man/flrn.1.iso create mode 100644 man/zapaccents create mode 100644 src/ChangeLog create mode 100644 src/Makefile.in create mode 100644 src/art_group.c create mode 100644 src/art_group.h create mode 100644 src/compatibility.c create mode 100644 src/compatibility.h create mode 100644 src/config.h create mode 100644 src/config.h.in create mode 100644 src/extern.h create mode 100644 src/flrn.h create mode 100644 src/flrn_color.c create mode 100644 src/flrn_color.h create mode 100644 src/flrn_config.h create mode 100644 src/flrn_files.c create mode 100644 src/flrn_filter.c create mode 100644 src/flrn_filter.h create mode 100644 src/flrn_format.c create mode 100644 src/flrn_glob.h create mode 100644 src/flrn_help.c create mode 100644 src/flrn_inter.c create mode 100644 src/flrn_inter.h create mode 100644 src/flrn_menus.c create mode 100644 src/flrn_menus.h create mode 100644 src/flrn_pager.c create mode 100644 src/flrn_pager.h create mode 100644 src/flrn_regexp.c create mode 100644 src/flrn_shell.c create mode 100644 src/flrn_slang.c create mode 100644 src/flrn_slang.h create mode 100644 src/flrn_string.c create mode 100644 src/flrn_string.h create mode 100644 src/flrn_tcp.c create mode 100644 src/flrn_xover.c create mode 100644 src/group.c create mode 100644 src/group.h create mode 100644 src/main.c create mode 100644 src/options.c create mode 100644 src/options.h create mode 100644 src/pathdef.h create mode 100644 src/post.c create mode 100644 src/post.h create mode 100644 src/rfc2047.c create mode 100644 src/rfc2047.h create mode 100644 src/site_config.h create mode 100644 src/tty_display.c create mode 100644 src/tty_keyboard.c create mode 100644 src/version.h diff --git a/configure b/configure new file mode 100755 index 0000000..96bdf51 --- /dev/null +++ b/configure @@ -0,0 +1,2182 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.11.2 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --with-default-host[=MACHINE] default host for adresses" +ac_help="$ac_help + --with-domain[=DOMAIN] default host for adresses" +ac_help="$ac_help + --with-slang[=DIR] use S-Lang in dir DIR" +ac_help="$ac_help + --with-rx[=DIR] Use GNU rx " + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.11.2" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=src/art_group.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:535: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:564: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:612: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:646: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:651: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes + ac_test_CFLAGS="${CFLAGS+set}" + ac_save_CFLAGS="$CFLAGS" + CFLAGS= + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:675: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 + if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" + elif test $ac_cv_prog_cc_g = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-O2" + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:732: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +for ac_prog in gccmakedep makedepend +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:787: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_MAKEDEPEND'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$MAKEDEPEND"; then + ac_cv_prog_MAKEDEPEND="$MAKEDEPEND" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_MAKEDEPEND="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +MAKEDEPEND="$ac_cv_prog_MAKEDEPEND" +if test -n "$MAKEDEPEND"; then + echo "$ac_t""$MAKEDEPEND" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$MAKEDEPEND" && break +done +test -n "$MAKEDEPEND" || MAKEDEPEND="echo" + + +# Extract the first word of "sendmail", so it can be a program name with args. +set dummy sendmail; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:820: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_SENDMAIL'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$SENDMAIL" in + /*) + ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in `echo $PATH | sed "s/:/ /"` /usr/sbin /usr/lib$ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_SENDMAIL="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL="no" + ;; +esac +fi +SENDMAIL="$ac_cv_path_SENDMAIL" +if test -n "$SENDMAIL"; then + echo "$ac_t""$SENDMAIL" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +cat >> confdefs.h <> confdefs.h <&6 + +fi + + +# Check whether --with-domain or --without-domain was given. +if test "${with_domain+set}" = set; then + withval="$with_domain" + + cat >> confdefs.h <&6 + +else + cat >> confdefs.h <&6 + +fi + + +if test "$ac_cv_prog_CC" = gcc; then + CFLAGS="-Wall $CFLAGS" +fi + +echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6 +echo "configure:891: checking for socket in -lsocket" >&5 +ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6 +echo "configure:938: checking for gethostbyname in -lnsl" >&5 +ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lnsl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + +if test "`(uname) 2>/dev/null`" = SunOS && + uname -r | grep '^5' >/dev/null; then + solaris=yes; +fi + +echo $ac_n "checking for 8 bit nroff""... $ac_c" 1>&6 +echo "configure:991: checking for 8 bit nroff" >&5 +if test X`echo 'é'| nroff | head -1` = 'Xé'; then + MANISO=.iso + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + + +# Check whether --with-slang or --without-slang was given. +if test "${with_slang+set}" = set; then + withval="$with_slang" + echo $ac_n "checking for S-Lang""... $ac_c" 1>&6 +echo "configure:1005: checking for S-Lang" >&5 + if test $withval = yes; then + if test -d $srcdir/../slang; then + flrn_cv_slang=$srcdir/../slang/src + CPPFLAGS="$CPPFLAGS -I${flrn_cv_slang}" + LDFLAGS="$LDFLAGS -L${flrn_cv_slang}/objs" + else + flrn_cv_slang=yes + fi + else + if test -f $withval/src/slang.h; then + flrn_cv_slang=$withval/src + CPPFLAGS="$CPPFLAGS -I${flrn_cv_slang}" + LDFLAGS="$LDFLAGS -L${flrn_cv_slang}/objs" + else + flrn_cv_slang=$withval + if test -d $withval/include/slang; then + CPPFLAGS="$CPPFLAGS -I${withval}/include/slang" + elif test -d $withval/include; then + CPPFLAGS="$CPPFLAGS -I${withval}/include" + fi + LDFLAGS="$LDFLAGS -L${withval}/lib" + if test X$solaris = Xyes; then + LDFLAGS="$LDFLAGS -R${withval}/lib" + fi + fi + fi + echo "$ac_t""$flrn_cv_slang" 1>&6 + +fi + +LIBS="$LIBS -lslang -lm" + +echo $ac_n "checking if I can compile a test SLang program""... $ac_c" 1>&6 +echo "configure:1039: checking if I can compile a test SLang program" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + { echo "configure: error: unable to compile. check config.log" 1>&2; exit 1; } +fi +rm -f conftest* + + + +flrn_cv_rx=no +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:1063: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1084: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1101: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +# Check whether --with-rx or --without-rx was given. +if test "${with_rx+set}" = set; then + withval="$with_rx" + if test $withval != yes; then + if test -d $withval/lib; then + flrn_cv_rx=$withval/lib + CPPFLAGS="-I$withval/include $CPPFLAGS" + else + flrn_cv_rx=$withval + CPPFLAGS="-I$withval $CPPFLAGS" + fi + else + flrn_cv_rx=yes + fi +else + for ac_func in regcomp +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1141: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1169: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +flrn_cv_rx=yes +fi +done + + ac_safe=`echo "regdef.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for regdef.h""... $ac_c" 1>&6 +echo "configure:1196: checking for regdef.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1206: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + +fi + + + +if test X$flrn_cv_rx != Xno; then + if test X$flrn_cv_rx != Xyes; then + LDFLAGS="$LDFLAGS -L$flrn_cv_rx" + if test X$solaris = Xyes; then + LDFLAGS="$LDFLAGS -R$flrn_cv_rx" + fi + fi + echo checking for librx... $flrn_cv_rx + echo $ac_n "checking for regcomp in -lrx""... $ac_c" 1>&6 +echo "configure:1241: checking for regcomp in -lrx" >&5 +ac_lib_var=`echo rx'_'regcomp | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lrx $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo rx | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 + + { echo "configure: error: You need to use the option --with-rx" 1>&2; exit 1; } +fi + +fi + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1292: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1305: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1372: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_hdr in sys/time.h unistd.h ctype.h errno.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1399: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1409: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1437: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1491: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1512: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:1545: checking whether time.h and sys/time.h may both be included" >&5 +if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +int main() { +struct tm *tp; +; return 0; } +EOF +if { (eval echo configure:1559: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_time=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_time=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_time" 1>&6 +if test $ac_cv_header_time = yes; then + cat >> confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:1580: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:1593: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + + +echo $ac_n "checking for strftime""... $ac_c" 1>&6 +echo "configure:1615: checking for strftime" >&5 +if eval "test \"`echo '$''{'ac_cv_func_strftime'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_strftime) || defined (__stub___strftime) +choke me +#else +strftime(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1643: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_strftime=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_strftime=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'strftime`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +else + echo "$ac_t""no" 1>&6 +# strftime is in -lintl on SCO UNIX. +echo $ac_n "checking for strftime in -lintl""... $ac_c" 1>&6 +echo "configure:1665: checking for strftime in -lintl" >&5 +ac_lib_var=`echo intl'_'strftime | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lintl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +LIBS="-lintl $LIBS" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +for ac_func in gethostname select socket strdup strspn strstr strtol +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1713: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1741: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + +for ac_func in inet_aton snprintf isdigit isblank +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1768: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1796: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.11.2" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "src/Makefile src/config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@CC@%$CC%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@MAKEDEPEND@%$MAKEDEPEND%g +s%@SENDMAIL@%$SENDMAIL%g +s%@CPP@%$CPP%g +s%@MANISO@%$MANISO%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + + +cd src +echo starting make depend +make depend diff --git a/help/Help_0 b/help/Help_0 new file mode 100644 index 0000000..0d99bf0 --- /dev/null +++ b/help/Help_0 @@ -0,0 +1,18 @@ +0: présentation de flrn + + + Présentation de flrn + -------------------- + +Flrn est un super-programme méga-torche pour lire les news. +Son seul bug est d'être en français. Mais c'est promis, un jour je le +traduis en russe. + +Pour l'instant, c'est moi et Jo +qui travaillons dessus. Pour toute suggestion ou bug-report, envoyez-nous +un mail, nous serons ravis de (mal) satisfaire votre demande. + + Damien + +'q' permet de quitter l'aide à tout moment. +'m' permet de revenir au menu diff --git a/help/Help_1 b/help/Help_1 new file mode 100644 index 0000000..ba8bec8 --- /dev/null +++ b/help/Help_1 @@ -0,0 +1,98 @@ +1: commandes simples + +Les paragraphes en retrait sont des descriptions plus détaillées qui peuvent +être ignorés. + + flrn : commandes de bases + ------------------------- + + Syntaxe générale des commande + ----------------------------- + +Les commandes sont utilisables soit en tappant leur nom : +"\goto" ou en utilisant une touche sur laquelle elles sont bindées, comme 'g'. + +Certaines commandes prennent une chaine de caractères en argument. On peut la +mettre directement si l'on utilise la première forme : "\goto flrn". + + Enfin, on peut tapper avant toutes les commandes un prefix décrivant les + articles auxquels elle doit s'appliquer (la sémantique varie un peu pour + certaines commandes particuilères). La forme générale de ce préfixe est : + elem_1,elem_2... + où chaque élément peut être un numéro (15), une suite de numéro (10-20), + un article et ses réponses (15>) ou l'ensemble des ancètre d'un article (15_). + Il existe deux racourcis supplémentaires : '.' représente l'article courant, + '5<' représente l'origine du fil de discution et '1' représente le + premier article du groupe. Enfin, '-10' et '10-' sont utilisables, et + représentent respectivement tous les articles de numéro inférieur à 10, + et tous les articles dfe numéro supérieur à 10. + + Ainsi, pour avoir le résumé de la discution en cours, on peut tapper : + '.<>r'. + + Commandes de flrn + ----------------- + +Les touches peuvent être rebindées. Cependant on décrit le comportement +par défaut. + +* Déplacement dans les articles : + +espace, enter et n changent d'article. Espace affichera l'article suivant de +la discution qui n'as pas encore été lu. Enter (comme n) va juste à l'article +suivant. + +v affiche un article. Par exemple '15v'. + +Les flèches permettent de se déplacer dans les discutions. La flèche gauche +va au père, celles du haut et du bas se déplacent parmis les frères. +(On a aussi les bindings équivalents []() comme trn, ainsi que b et f issus +de forum). + +On peut encore avec B, F et H se déplacer parmis les articles que l'on vient +de lire. + +* Gestions des articles + +V (\view) lance $PAGER. +s (\save) sauve l'article. +S (\SAVE) sauve une discution. +o (\omet) marque l'article non lu. O marque la discution non lue. +k (\kill) marque l'article lu. Cela sert quand l'article est long. + K et J marquent l'article et ses descendants lus (toute la discution + pour J) + +* Affichage de résumés + +r (\summary) affiche un résumé. +t (\summ_fil) résumé d'un article et de ses fils. (équivalent à '.>r') +T (\summ_thr) résumé de la discution. (équivalent à '.<>r') + +* Gestion des groupes : + +u (\unsu) se désabonne du groupe. +a (\abon) s'abonne au groupe. +z (\zap) marque tous les messages du groupe lus. +g (\goto) change de groupe parmis les groupes auxquels on est abonné. +G (\GOTO) change de groupe. +l (\list) donne la liste des groupes auquel on est abonné. +L (\LIST) liste tous les groupes. + +* Posts : + +m (\post) poste un article. +R (\repond) répond à un article, c-à-d poste un followup. + +* Divers : + +(\config) permet de changer la valeur de certaines options par un menu. +: (\option) permet de donner une ligne d'option. +q (\quit) quitte. +Q (\QUIT) quitte sans sauver le .flnewsrc. +h (\help) lance cette aide. + +Cf la page 4 pour plus de détails. + + +'q' permet de quitter l'aide à tout moment. +'m' permet de revenir au menu diff --git a/help/Help_2 b/help/Help_2 new file mode 100644 index 0000000..f93a8e4 --- /dev/null +++ b/help/Help_2 @@ -0,0 +1,32 @@ +2: Poster des messages + + flrn : poster des messages + -------------------------- + +Pourquoi poster des messages ? N'est-ce pas mieux de lire simplement ? +Si vous voulez vraiment, les commande post et respond permettent de poster un +message. respond répond à un message. Si auto_edit est indéfini, vous entrez +alors dans l'éditeur intégré de flrn (qui est très sommaire). +Pour poster votre message, entrez à la fin de celui-ci une ligne ne +comportant qu'un '.' ou tapez ^D. + +Vous pouvez lancer une éditeur (défini par la variable d'environnement +EDITOR) en entrant juste ~e ou ~E sur une ligne. En mode edit_all_headers, +tous les headers définis seront affichés (et vous pourrez en ajouter). Dans +le mode par défaut, seul le sujet et les groupes dans lesquels le message +est postés sont affichés. + +Si vous entrez ~E, le corps du message auquel vous répondez sera inclus +dans le post avant de lancer l'éditeur, et sera quoté par la chaine +index_string. + +La commande mail-ans peut être utilisée pour envoyer un mail de réponse à +un article. Si respond est utilisé et qu'un Followup-To est défini, il +vous sera demandé si vous souhaitez en tenir compte. Si il est plutôt demandé +un post dans le newsgroup, et que le message contient en champ To, Cc, ou Bcc, +le message sera aussi envoyé par mail. + +Dans tous les cas, ^C annule le post. + +'q' permet de quitter l'aide à tout moment. +'m' permet de revenir au menu diff --git a/help/Help_3 b/help/Help_3 new file mode 100644 index 0000000..49077d5 --- /dev/null +++ b/help/Help_3 @@ -0,0 +1,218 @@ +Help_3 + + flrn : options du .flrn + ----------------------- + +Les options sont aussi disponibles sur la page de manuel de flrn. +La forme générale des options est inspirée de mutt. Il y a six commandes : + +set = + Permet de modifier la valeur d'une variable. On a les racourcis + suivants : "set " est équivalent à "set variable=1" ou + "set variable=yes". De même, "set no" est équivalent à + "set =no" ou "set =0". + +header list|weak|hide + donne l'ordre dans lequel les headers d'un + article doivent être affichés. À chaque chiffre correspond un header : + 1 : Auteur 2 : Réponse à 3 : Sujet 4 : Date + 5 : Newsgroups 6 : Followup-To 7 : Organization 8 : Lines + 9 : Sender 10 : Reply-To 11 : Expires 12 : X-Newsreader + On peut mettre d'autres headers en donnant leur nom complet. + On peut aussi mettre others pour signifier tous les autres headers. + (défaut : 2 1 4 3) + Avec weak, on donne ceux qui ne doivent pas être réaffichés après la + première page d'un message. + Avec hide, on indique les headers à ne pas afficher avec others. + (pas de défaut) + +color ,,... +mono , ... +regcolor ,,... + Fixe la couleur et/ou les propriétés des objets de flrn. La commande + color modifie les couleurs par défaut, la commande mono les spécifie + si le terminal est monochrome, la commande regcolor permet, pour un + objet choisi, de modifier des patterns donnés. + est une suite de mots séparés par des virgules, choisis dans : + normal : pour l'article par exemple (défaut: valeurs du terminal) + header : pour les headers (défaut: vert sur fond normal) + status : pour la barre de newsgroup (défaut: inverse video) + error : pour les messages d'erreur (défaut: rouge gras) + quoted : pour les citations (défaut: magenta) + fin : pour le "A vous:" final (défaut: en gras) + sig : pour les signatures (défaut: en bleu) + file : pour les fichiers (aide) (défaut: normal) + summary: pour les résumés (défaut: normal) + all : tous les objets + et sont les couleurs des caractères et du fond + ... sont les attributs des caractères : cela comprend + bold (gras), blink (souvent non défini), + underline (souligné), et reverse (en inverse video), + normal et - (qui équivaut à normal) + on peut aussi mettre pour les couleurs et les attributs std (couleur + du dernier type utilisé dans la commande), et std- qui + représente la couleur|attribut du dernier color|mono fait. + est une suite de flags, parmis : + case : la regexp est case-sensitive + exclude : on exclue les autres regcolor d'un ligne qui matche + line : toute la ligne est mise dans la couleur choisie + n dans [1-9] : seule le groupe n est coloré + est une expression régulière + Par exemple : + color file std-normal std-normal bold,underline + regcolor normal line std-quoted std-quoted std-quoted ^(»|»|<<) + mono status,error bold + + + +bind + Permet de changer la fonction d'une touche. La syntaxe changera + surement dans les versions a venir, comme les noms ou sémantiques des + commandes. Pour l'instant, on ne peut binder que des touches ne + correspondant pas à des caratères spéciaux du .flrn, et correspondant + à des caractères imprimables. + Par exemple : + bind U pipe urlview + bind + summary unread + bind \0x01 gotag é # ^A + bind A tag é + + +Voici les variables utilisées : + +* Abonnement + +auto_subscribe= + demande l'abonnement automatique aux newsgroups dont le nom + verifie l'expression régulière + (défaut : "") + +auto_ignore= + demande le non-abonnement automatique aux newsgroups dont le + nom vérifie l'expression régulière + (défaut : "^control|^[^.]+$", soit en gros ignorer control et junk) + +default_subscribe=yes|no + définit le comportement par défaut lors de la création d'un nouveau + groupe, quand il ne s'applique pas à auto_subscribe et auto_ignore. + (défaut : yes) + +* Affichage + +skip_line= + définit le nombre de lignes blanches à laisser sous la barre de status. + (défaut : 1) + +date_in_summary=yes|no + affiche ou non la date dans les résumés + (défaut : yes) + +ordered_summary=yes|no + affiche les résumés dans l'ordre de leur numéro, et non dans un ordre + threadé. + (défaut : yes) + +duplicate_subject=yes|no + affiche le sujet à chaque ligne de résumé, même si c'est le même que + la ligne au-dessus. + (défaut : no) + +color=yes|no|auto + demande l'utilisation de la couleur. Auto signifie que flrn tente une + détection automatique. + (défaut : auto) + +* Commandes + +space_is_return=yes|no + empèche de changer de groupe par la commande next (qui correspond par + défaut à la barre d'espacement). + (défaut : no) + +return_dont_scroll=yes|no + si cette option est activée, la touche "enter" ne scrolle pas d'une + ligne dans les messages longs, mais est directement executée hors + du message. + +cbreak=yes|no + passe en mode cbreak. Sans cette options, on est en mode nocbreak. + (défaut : yes) + +new_mode=yes|no + si cbreak=yes, définit le mode d'entrée des commandes. Le nouveau + mode est plus puissant que l'ancien, qui correspond aux commandes des + premières release de flrn. + (défaut : yes) + +use_menus=yes|no + utilise des menus interactifs avec certaines commandes. Cette option + est expérimentale. + (défaut : no pour l'instant) + +threaded_space=yes|no + permet de lire les messages en suivant la discussion (thread) ou dans + l'ordre des numéros. Cette option modifie l'action de la commande next. + (défaut : no) + +cool_arrows=yes|no + N'affiche pas de message d'erreur quand on utilise une commande de + déplacement dans le thread et que ce déplacement est invalide. + (défaut : yes) + +use_regexp=yes|no + permet d'utilise les expressions régulières pour les commandes list + et goto. Les expressions utilisées sont les expressions étendues de + rx. + (défaut : no) + +zap_change_group=yes|no + dit a flrn de changer de groupe si on utilise la commande zap. + (défaut : no) + +* Poster + +include_in_edit=yes|no + insère automatiquement le message auquel on répond dans l'éditeur. + Cette option n'a de sens qu'avec auto_edit. + (défaut : no) + +auto_edit=yes|no + n'utilise pas l'éditeur intégré de flrn, et lance directement l'éditeur + extérieur quand on poste un message. + (défaut : no) + +post_name= + choisis le nom utilisé dans les messages envoyés. + (défaut : "") + +index_string= + donne la chaîne ajoutée au début de chaque ligne du message auquel + on répond pour quoter celui-ci. + (défaut : "> ") + +smart_quote=yes|no + si index_string commence par '>' et que la ligne à quoter commence + aussi par '>', quote simplement par ">" et non par index_string. + (défaut : yes) + +* Autres + +quit_if_nothine=yes|no + Quitte directement flrn si il n'y a rien de nouveau. Malheureusement, + efface quand même l'écran. + (défaut : no, parce que ce comportement est insupportable actuellement) + +use_mailbox=yes|no + Sauve les messages au format mailbox. + (défaut : yes) + +server= + utilise la machine correspondant à ce nom comme serveur. + (défaut : selon l'installation locale) + +port= + utilise le port donné plutôt que le port nntp. + (défaut : le port nntp) + +'q' permet de quitter l'aide à tout moment. +'m' permet de revenir au menu. diff --git a/help/Help_4 b/help/Help_4 new file mode 100644 index 0000000..20dbb3e --- /dev/null +++ b/help/Help_4 @@ -0,0 +1,240 @@ +4: Liste des commandes + + +---------------------------+ + |flrn : Toutes les commandes| + +---------------------------+ + +flrn admet trois modes d'entrée de commandes : + _ le vieux mode cbreak, très semblable au mode de forum, qui sera remplacé +rapidement par le nouveau mode (non documenté) + _ le nouveau mode cbreak, décrit plus bas, qui est le mode par défaut + _ le mode nocbreak, pour l'instant très imparfait. + +On peut changer de mode avec les options new_mode et cbreak. +Dans les modes cbreak, taper '@' permet de rentrer localement une commande +en mode nocbreak. + + + Syntaxe générale d'une commande + ------------------------------- + +une commande se présente sous la forme suivante dans le nouveau mode : + + [ elem_1 [ ,elem_2 [ ,... ]]] [chaine] + +dans le mode nocbreak, la commande prend la forme suivante : + + [ elem_1 [ ,elem_2 [ ,... ]]][ [,]chaine ] + +elem_1, elem_2, etc... caractérisent les articles concernés par la commande. +Ces articles sont désignés par leur numéro dans le newsgroup courant. +Ils sont sans importance si la commande se rapporte à un groupe donné. +Chaque elem_i peut se présenter sous 3 formes : + n1 : désigne l'article portant le numéro n1 + n2-n3 : désigne l'ensemble des articles allant de n2 à n3 + n4> : désigne les articles ayant n4 comme ancêtre + n5_ : désigne les articles ayant n5 comme descendants + +Les formes particulières que ces numéros peuvent prendre sont les suivantes : + +Forme n1,n4 et n5 n2 n3 + C D F +0 ou . C C C +1 D D D + +C désignant le numéro de l'article courant, D celui du début du newsgroup +courant, et F celui de la fin du newsgroup. + +Chaque numéro peut être directement suivi du caractère <, auquel cas le +numéro est remplacé par celui de la racine du thread contenant l'article +de ce numéro. Ainsi .< désigne le numéro de la racine du thread contenant +l'article courant. + +Dans le cas où aucun elem_i n'est présent, les commandes ont le plus +souvent le même comportement qu'avec l'article courant comme seul +article sélectionné. Il y a toutefois des exceptions. + + + désigne le nom de la commande tapée. Cela peut être +soit une touche correspondant à un binding (par défaut ou obtenu par une +option), soit une commande explicite, préfixée par le caractère '\'. + +Enfin, certaines commandes (comme 'list' par exemple), prennent une chaine +de caractère comme dernier argument. Elle est demandée en fin de commande. +Certaines commandes (comme 'post') n'en demande pas, mais en acceptent en +mode nocbreak. + + + Commandes de flrn + ----------------- + +Chaque commande est donnée avec son nom explicite et sa touche correspondante +par défaut. Certaines touches ont des fonctions spéciales, comme 'enter' +et '-'. D'autres ne peuvent pas être liées à une commande : c'est le +cas de la virgule, des chiffres, du point et des caractères '<' et '>'. + +* Déplacement dans les articles : + +art (défaut : v, et aussi enter si un paramètre est donné) + Affiche le premier article donné en argument (ou l'article courant + s'il n'y a pas d'argument). + +suiv (défaut : enter, si aucun paramètre n'est donné) + Passe à l'article suivant le premier article donné en argument. + +prev (défaut : p, et - si c'est le PREMIER caractère de la commande) + Revient à l'article précédent le premier article donné en argument. + (la touche - n'est pas garantie dans les versions suivantes) + +up (défaut : flèche haut) +down (défaut : flèche bas) +left (défaut : flèche gauche) +right (défaut : flèche droite) + Permet de se déplacer au sein des articles de même sujet. On part + du premier article sélectionné, ou de l'article courant. + +next (défaut : space) + Va au premier article non lu. Cette commande a des effets différents + selon les options space_is_return et threaded_space. On part dans + tous les cas du premier article selectionné. + +nxt-thr (défaut : n) + Passe au thread suivant où il y a quelque chose à lire en partant du + premier article sélectionné ou de l'article courant. (non implémentée) + + +* Gestions des articles + +view (défaut : V) + Lance $PAGER sur les articles sélectionnés. Devrait être une macro + de pipe. + +save (défaut : s) + Sauve les articles sélectionnés dans un fichier (demandé en argument) + Une confirmation est demandé si le fichier existe déjà. +SAVE (défaut : S) + Même effet que save, mais sauve les descendants de tous les articles + sélectionnés, avec éventuellement plusieurs sauvegardes du même + article. + +omet (défaut : o) + marque comme non lus les articles sélectionnés. Dans le cas où aucun + article n'est sélectionné, marque juste l'article courant non lu. +OMET (défaut : O) + marque comme non lus les descendants de tous les articles sélectionnés. + Si aucun article n'est selectionnés, on se contente de tous les + descendants de l'article courant. + +kill (défaut : k) + marque comme lus les articles sélectionnés avec les crossposts. + Si aucun article n'est sélectionné, on marque le message courant comme + lu. On passe ensuite au premier article non lu. +KILL (défaut : K) + marque comme lus les descendants des articles sélectionnés en prenant + en compte les crossposts. Si aucun article n'est selectionné, on + marque les descendants du message courant comme lus. +KILL-THR (défaut : J) + marque comme lus les discussions complètes des articles sélectionnés + avec les crossposts. Si aucun article n'est sélectionné, on marque la + discussion courante comme lue. + + +* Affichage de résumés + +summary (défaut : r) + Résumé des articles sélectionnés. Dans le cas ou aucun article n'est + sélectionné, on affiche un écran d'articles, centré par défaut sur + l'article courant. Les articles sont placés dans l'ordre des leur + numéro ou sous une forme threadée, selon l'option ordered_summary. +summ_fil (défaut : t) + Résumé des articles sélectionnés sous une forme threadée. Dans le + cas où aucun article n'est sélectionné, on affiche les descendants de + l'article courant. +summ_thr (défaut : T) + Même action que t, mais le comportement par défaut affiche cette fois + le thread complet. + +* Gestion des groupes : + +unsu (défaut : u) + Se désabonne du groupe courant +abon (défaut : a) + S'abonne au groupe courant +zap (défaut : z) + Marque tous les messages du groupe comme lus, sans tenir compte des + crossposts. +goto (défaut : g) (prend comme argument) + Va au premier groupe à partir du groupe courant comportant la chaine + en mode nouse_regexp, sinon au groupe dont le nom correspond + à l'expression régulière associée à . Si est vide, va au + premier groupe comprenant des messages non lus. Cette commande se + limite aux groupes auxquels on est abonné. +GOTO (défaut : G) (prend comme argument) + Même action que goto, mais on considère ici TOUS les groupes, en + commençant par ceux auquels on est abonnés, puis ceux qui sont dans + le .flnewsrc, puis les autres. +swap-grp (pas de défaut) (prend comme argument) + Permet de changer de newsgroup sans changer l'article courant (pour peu + que celui-ci soit crossposté, ou ait été obtenu par get-father). Si + est non vide, on cherchera à aller dans un groupe dont le nom + contient . Les regexp seront gérées comme avec goto dès que + possible. +list (défaut : l) (prend comme argument) + Donne la liste des newsgroups auquel on est abonné dont le nom contient + , ou correspond à l'expression régulière associée à en + mode use_regexp. +LIST (défaut : L) (prend comme argument) + Même effet que l, mais sans restriction d'abonnement. + +* Posts : + +post (défaut : m) (prend éventuellement en paramètre) + Poste un article dans le newsgroup courant. Si existe, poste + le fichier SANS DEMANDER CONFIRMATION. +repond (défaut : R) (prend éventuellement en paramètre) + Répond au premier article sélectionné, et à l'article courant par + défaut. Si existe, on poste le fichier SANS DEMANDER + CONFIRMATION. +mail-ans (pas de défaut) (prend éventuellement en paramètre) + Répond au premier article sélectionné par mail. Si existe, on + envoie le fichier par mail SANS DEMANDER CONFIRMATION. + +* Shell-escapes : + +Toutes les commandes lancées sont équivalentes à sh -c . +(cf man system(3)) + +pipe (défaut : '|') (prend comme argument) + Lance une commande (lue par sh) avec comme entrée les articles + sélectionnées (par défaut l'article courant). +PIPE (pas de défaut) (prend comme argument) + Comme pipe, mais envoie les threads des articles sélectionnées en + entrée. +filter (défaut : '%') (prend comme argument) + Comme pipe, mais reprend la sortie standard et l'affiche. +FILTER (pas de défaut) (prend comme argument) + Comme PIPE, mais reprend la sortie standard (cf filter) +shell (pas de défaut) (prend comme argument) + Lance la commande , en laissant le contrôle du terminal. +shin (défaut : '!') (prend comme argument) + Lance la commande , récupère et affiche la sortie standard. + (aussi bien que cette aide est affichée) + +* Divers : + +option (défaut : ':') (prend éventuellement l'option en paramètre) + permet de parser une ligne d'option, afin de modifier les options + au cours du programme. La touche tabulation permet de faire une + complétion automatique. +config (pas de défaut) + permet de modifier l'ensemble de la configuration à l'aide d'un + menu. Encore un peu incomplet. Bugue avec l'option color. +quit (défaut : q) + Quitte le programme en sauvant le .flnewsrc +QUIT (défaut : Q) + Quitte le programme sans sauver le .flnewsrc +help (défaut : h et ?) + Lance cette aide. + +'q' permet de quitter l'aide à tout moment. +'m' permet de revenir au menu. diff --git a/help/Help_m b/help/Help_m new file mode 100644 index 0000000..8da885e --- /dev/null +++ b/help/Help_m @@ -0,0 +1,16 @@ +m: menu + + + Aide de flrn + ------------ + +Tapez sur le chiffre correspondant à la section de votre choix. + + 0. Présentation de flrn + 1. Description simple des commandes usuelles + 2. Poster des messages + 3. Options du .flrn + 4. Description de toutes les commandes + +'q' permet de quitter l'aide à tout moment. +'m' permet de revenir à ce menu. diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..5871924 --- /dev/null +++ b/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 0000000..e285dbd --- /dev/null +++ b/man/Makefile @@ -0,0 +1,4 @@ +all: flrn.1 + +%.1: %.1.iso zapaccents + sed -f zapaccents $< > $@ diff --git a/man/flrn.1.iso b/man/flrn.1.iso new file mode 100644 index 0000000..7375e98 --- /dev/null +++ b/man/flrn.1.iso @@ -0,0 +1,225 @@ +.TH FLRN 1 "Jan 1998" +.SH NOM +.I flrn +- Forum Like rn + +Attention, c'est encore un programme en développement. Il peut y avoir de gros +changement entre deux versions, et il n'est pas exempt de bugs. +.SH SYNTAXE +.B flrn +[\fB-v|--version\fR] +[\fB-h|--help\fR] +[\fB-d|--debug\fR] +[\fB-c|--co\fR] +[\fB-s|--show-config\fR] + +.B flrn +\fB\fR + +.SH DESCRIPTION +.B Flrn +est un lecteur de news dont l'interface est largement inspirée de celles de +forum, trn, mutt et slrn. Il est assez configurable, mais la configuration par +défaut est assez proche de forum. On a la valeur par défaut de toutes les +variables en utilisant '\fI flrn -s\fR' + +Il y a une aide accessible par la commande 'h' dans flrn. Elle est souvent +plus à jour que cette page. + +Si on lui passe un nom sur la ligne de commande, flrn va dans le premier +groupe où l'on trouve ce sous mot, en cherchant en priorité dans les groupes +auxquels on est abonné. + +.SS Description des options de ligne de commande +.TP 6 +.B \fB-d\fR +L'option \fB-d\fR affiche sur la sortie d'erreur des message de mis au point. +Elle ne devrait pas servir en usage normal. +.IP \fB-c\fR +L'option \fB-c\fR donne une estimation du nombre de messages à lire dans les +groupes où l'on est abonné. Cela renvoie toujours un majorant du nombre de +messages à lire. +.IP \fB-h\fR +L'option \fB-h\fR affiche la liste des options reconnues. +.IP \fB-v\fR +Affiche le numéro de version. +.IP \fB-s\fR +Donne un fichier de configuration contenant les valeurs par défaut de toutes +les variables. + + +.SS Options de configuration +Se reporter à l'aide interne pour avoir une description à jour. +On peut maintenant modifier les options pendant l'utilisation de flrn avec +la commande '\config'. + +La forme générale des options est inspiree de \fBmutt\fR. +Il y a six commandes : +.B set +.B color +.B mono +.B regcolor +.B header +.B bind + +.TP 8 +.B set = +Permet de modifier la valeur d'une variable +On a les racourcis suivants : +"set " est équivalent à "set variable=1" ou "set variable=yes". +De même, "set no" est équivalent à "set =no" ou +"set =0". + +.IP "\fBcolor \fR" +.IP "\fBmono \fR" +.IP "\fBregcolor \fR" +Permet de définir les couleurs. + est un une suite de mots séparés par des virgules (sans espace) +parmis : normnal, header, status, quoted, error, aff_fin et sig ou le mot all. + et sont des couleurs. On a le droit à la couleur default, et +aux couleurs std-normal, std-header ... et std. std- représente la couleur +du dernier color type fait (ou la couleur par défaut si c'est le premier). +Std correspond a la couleur du dernier type utilisé dans cette commande. + est une suite de mots clefs parmis : bold, blink, underline, +normal, reverse, std et std-*. + est une suite de flags, parmis : case, exclude, line, 1...9. On peut +mettre 0 ou - si l'on en veut aucun. + est une expression régulière. + +Par exemple : +"color header std std bold" ne passe les headers en gras, sans rien changer +d'autre. + +.IP "\fBheader list|weak|hide ...\fR" +Avec list donne la liste des headers à afficher (nom ou numero, cf l'aide +interactive pour la correspondance numero-nom). +Avec weak, on donne ceux à ne pas réafficher pour les pages d'un +messages après la première. +Avec hide on donne ceux a ne pas afficher quand on a utilise le mot réservé +other. + +.IP "\fBbind []\fR" +Permet de changer la fonction d'une touche. +Pour l'instant, on ne peut mettre pour touche un caractère n'étant pas un +caractère spécial du .flrn comme '" :=^I' et correspondant à +un caractère imprimable, ou utiliser la forme \0x20, \040 ou \32. +On ne peut pas mettre la touche entre guillements pour l'instant. + +.SS Les variables : +Voivi une liste partielle des variables. Une liste à jour completée +des valeurs par défaut est obtenue avec ./flrn -s. + +.TP 6 +.RS +.B Abonnement +.RE +.TP 4 +.I default_subscribe +Définit le comportement par défaut lors de la création d'un nouveau groupe. +Cela s'applique pour les groupes ne correspondant ni à \fIauto_subscribe\fR +ni à \fIauto_ignore\fR. +.IP \fIauto_subscribe=\fR +Abonne automatiquement aux nouveaux groupes correspondant à l'expression +régulière donnée. +.IP \fIauto_ignore=\fR +Désabonne automatiquement des groupes qui correspondent. +.IP \fIsubscribe_first\fR +Choisis si l'on doit ignorer un groupe correspondant à la fois à +\fIauto_subscribe\fR et \fIauto_ignore\fR. + +.TP 6 +.RS +.B Affichage +.RE +.TP 4 +.I skip_line= +Donne le nombre de lignes blanches à laisser sous la barre de status. +.IP \fIdate_in_summary \fR +Affiche la date dans les résumés. +.IP \fIordered_summary \fR +Affiche les résumés dans l'ordre (c'est-à-dire sans tenir compre des +discussions). +.IP \fIduplicate_subject \fR +Affiche je sujet à chaque ligne d'un résumé, même si c'est le même que la +ligne d'au dessus. +.IP \fIcolor\fR +Permet d'utiliser la couleur. +.TP 6 +.RS +.B Commandes +.RE +.TP 4 +.I space_is_return +Empeche de changer de groupe en utilisant la touche espace. +.I return_dont_scroll +Passe au message suivant avec retrn au lieu de scroller. +.IP \fIcbreak\fR +Passe en mode cbreak. Sans cette option, on est en mode émulation d'un +nocbreak. +.IP \fInew_mode\fR +Sans cette option, on retrouve le mode de saisis des commandes des premières +releases de flrn. Avec ses bugs et ses limitations. +.IP \fIthreaded_space\fR +Permet de lire les message en suivant les discussions. +.IP \fIcool_arrows\fR +N'affiche pas de message d'erreur quand on essaie d'utiliser une flèche ne +correspondant à aucun message. +.IP \fIuse_regexp\fR +Permet d'utiliser des expressions régulières en arguemnt des commandes L et G. +Les expressions utilisées sont les expressions régulières étendues de rx. +.IP \fIzap_change_group\fR +Dit à flrn de changer de groupe quand on marque tous les messages d'un groupe +comme lus. +(cf egrep(1)). +.IP \fIquit_if_nothing\fR +Quitte flrn tout de suite s'il n'y a rien à lire. +.TP 6 +.RS +.B Poster +.RE +.TP 4 +.I include_in_edit +Insère automatiquement le message auquel on répond dans l'éditeur. +.IP \fIauto_edit\fR +Lance automatiquement l'éditeur lorsce que l'on poste un message. +.IP \fIpost_name=\fR +Définit le nom réel à mettre dans le champ From. +.IP \fIindex_string=\fR +Donne la chaine ajouté au début de chaque ligne du message auquel on répond. +.IP \fIsmart_quote\fR +Met ">" à la place de \fIindex_string\fR si la ligne commence par ">" et que +index_string commence aussi par ">". +.TP 6 +.RS +.B Les autres +.RE +.TP 4 +.I use_mailbox +Utilise le format d'une mailbox pour sauver les messages. +.IP \fIserver=\fR +Utilise la machine correspondant à ce nom comme serveur. +.IP \fIport=\fR +Utilise le port donné plutôt que le port nntp. + + +.SH FICHIERS +Flrn utilise deux fichiers dans le home dir de l'utilisateur: +.TP 10 +.B "\fB.flrn\fR" +C'est le fichier de configuration. +.IP "\fB.flnewsrc\fR" +C'est un fichier avec la même syntaxe qu'un .newsrc qui est utilisé par +flrn pour marquer les messages lus. + +.SH VOIR AUSSI +Pour les expressions régulières, voir +regex(7) ou regcomp(3) ou encore la doc de rx ou egrep(1). + +.SH BUGS +Il y en a encore plein. Si vous en trouvez, dites-le nous. + +.SH AUTEURS +Damien (Damien.Masse@rezo.ens.fr) +.br +Jo (Joel-Yann.Fourre@rezo.ens.fr) + diff --git a/man/zapaccents b/man/zapaccents new file mode 100644 index 0000000..aacce79 --- /dev/null +++ b/man/zapaccents @@ -0,0 +1,8 @@ +1,$s/é/e/g +1,$s/è/e/g +1,$s/à/a/g +1,$s/ù/u/g +1,$s/ê/e/g +1,$s/ë/e/g +1,$s/ô/o/g +1,$s/ç/c/g diff --git a/src/ChangeLog b/src/ChangeLog new file mode 100644 index 0000000..3879dc6 --- /dev/null +++ b/src/ChangeLog @@ -0,0 +1,222 @@ +ChangeLog Flrn + +---------------------------- +Version 0.2.3 +---------------------------- + +. bugfixes d'affichage et de mode nocbreak +. option scroll_after_end +. patch pour être plus ansi... bugfixes. +. Préparation des kill-files. +. Ajout des noms up, left... pour pouvoir binder des touches. +. Ajout de la possibilité de binder MC et CG +. Déplacement du code des résumés. +. Bugfix des menus. +. Ajout de commandes pour les résumés. +. Possibilité d'avoir les résumés sous forme de menus. +. modif de SUIV pour qu'il cherche de nouveaux messages et corrige les oublis + dus à une mauvaise synchro du serveur. +. bindings par défaut de macros +. rajout de bindings de touches dans le pager (et bientot dans le menu) +. bugfixes dans flrn_inter.c et post.c -> moins de segfault +. bugfix dans l'affichage des menus +. modif dans Aff_file + +---------------------------- +Version 0.2.2 +---------------------------- + +. ajout des couleurs FIELD_SUMMARY, modification de Aff_summary_line pour + l'utiliser +. nettoyage de group.c +. nettoyage, ajout de CMD_NEED_GROUP +. modification de l'historique. +. bugfixes... +. ajout des couleurs FIELD_FILE +. ajout de la ligne de résumé à hist_menu +. découpage de la fonction de résumé +. ajout de Screen_refresh() avant cree_liste_end. +. introduction de bugs nouveaux, et bugfixes en tout genre. +. élimination des articles bidon surnuméraires... +. entrée dans un groupe en deux temps. +. cree_liste_xover appelé au besoin par flrn_filter. +. Modification de cree_liste_xover et cree_liens pour pouvoir les rappeler +. Possibilité de mettre des filtres pour les résumés +. Adaptation du code d'affichage pour Aff_file +. Ajout de %g %G et %C aux formats... +. suppression des messages dupliqués dans l'historique +. début de flrn_pager +. suppression de messages de debug +. bugfixes avec les articles exterieurs. +. supersedes ajouté. Also-Control refusé +. code des tags complet -- marche avec les articles exterieurs. +. Bugfixes et optimisations des tags +. La structure des groupe est conservée +. Ajout de libere_liste et d'un flag a detruit_liste +. Bugfixes dans main.c +. Ajout d'un historique. Et donc de hist-{prev|suiv|menu}. +. Les tags peuvent faire changer de groupe +. Ajout d'un Libere_menu_noms qui libère la mémoire des noms du menu... + +---------------------------- +Version 0.2.1 +---------------------------- + +. Modification du code pour le rendre plus portable. +. Modification de configure.in pour ajouter des checks. +. Ajout du cancel (grassouille) +. On modifie un peu le real pour éviter les ",444,," +. On modifie la structure du code... +. début de la possibilité de taguer des messages +. début de attribution +. ajout de bindings par défaut pour certaines commandes +. bugfix dans l'encodage des headers +. bugfix de configure.in si gccmakedep et makedepend manquent +. passage de DOMAIN et DEFAULT_HOST dans config.h.in + ajout des options a configure pour les changer +. mise à jour du configure pour détecter correctement rx +. modifs dans post.c + +---------------------------- +Version 0.2.0 +---------------------------- + +. bugfixes dans flrn_color +. changement pour l'affichage initial... (voir avec Jo) +. bugfix dans change_group +. bugfixes du code de regcolor, ajout de l'option mono + changement de syntaxe de color +. utilisation du texte décoré dans les headers +. utilisation du nouveau scrolling dans tty_display pour le corps du message +. modification des scrolling pour pouvoir gérer du texte décoré +. nouveau code de mise en relief (flrn_color.c) +. ajout de mail-ans +. Modification de bind. Possibilité de d'utiliser \0x20 pour la touche + Possibilité de donner des arguments. +. Nouveau code des options. Passage de Int_options+String_options a + All_options. +. Mise a jour du configure.in pour chercher sendmail +. Possibilité d'envoyer des mails. +. Ajout d'une commande \config +. Ajout de free_options() pour faciliter la verification des allocations + mémoire. +. Empeche l'envoie d'un message vide. +. Bugfix dans list qui mettait etat_loop.hors_struct a 1... +. Bugfixes, memory leaks +. Modif dans l'affichage si beaucoup de lignes blanches en fin de messages. +. Modif dans les changements de groupe : si on veut aller à un article + inexistant, on ne change pas groupe +. ajout de header hide, et de others dans header list +. Bugfixes du Makefile.in, la dépendance est correcte maintenant pour les + pages de man sous sunos/solaris. + +---------------------------- +Version 0.2pre4 +---------------------------- + +. Modifications des headers : on peut voir des headers inconnus +. Modifs des flèches, mais c'est encore crade +. Ajout des pipes/filtres/shell escapes +. Bugfixes dans Launch_Editor +. Nettoyage du code : on vire les refresh intempestifs. +. Scrolling dans l'affichage du message +. Commande prem_grp : un début d'ordre dans le .flnewsrc + +---------------------------- +Version 0.2pre3 (si je trouve Jo) +---------------------------- + +. Parsing de la date (tiré de mutt) pour changement en local +. Decodage du header subject seulement si XOVER utilisé +. Formattage automatique des paragraphes des posts... (Beurk !) +. Petit début de menus interactifs +. Petit bugfix dans art_group.c +. Modification des commandes explicites. +. Bugfix dans new_header (on oubliait reponse_a_checked) +. Petit bugfix dans cherche_newnews (à vérifier) + +--------------------------- +Version 0.2pre2 +--------------------------- + +. Passage a XOVER s'il est fiable +. Préparation pour le passage a xover +. création automatique de parents exterieurs. +. modification de la sémantique de parent <=0 +. bugfix dans flrn_tcp, on il manquait un test de EINTR. +. bugfix du calcul de decalage de date, non ? +. modif/bugfix des commandes explicites +. ajout de free_one_article +. suppression de GROUP_ZAPPED, Aff_thread +. modification du hack pour '-' +. nettoyage du code... + +--------------------------- +Version 0.2pre1 +--------------------------- + +. bugfix sur le calcul du decalage de date. Grrr... +. bugfix pour do_zap_group si on n'est dans aucun groupe +. regroupement des appels slang sur un seul fichier +. ajout de l'option quit_if_nothing +. ajout de la commande swap-grp +. modif de la commande goto pour choisir le numéro d'arrivée +. ajout de la commande get-father +. bugfix dans la gestion des crossposts +. nouvelle option zap_change_group +. bugfixes dans le post et ajout du post d'un fichier +. bugfixes dans flrn_tcp.c (pour l'heure) +. changement complet de la saisie des commandes +. reprise complete de flrn_inter.c et nombreux bugfixes +. debug de la gestion des threads cycliques +. et plein d'autres changements... + +----------------------------- +Preparation des versions 0.2.x +----------------------------- + +. bugfixes sur flrn_inter.c +. bugfixes/ameliorations sur flrn_regexp + +----------------------------- +version 0.1.1 +----------------------------- + +. abandon des regexp de slang, et passage a rx +. bugfixes +. Creation de site_config.h +. Suppression des options obscoletes name_news et restrict +. Passage de toutes les options chaines en char * +. Correction d'un bug avec auto_ignore/auto_subscribe +. Ajout des options index_string et smart_quote +. Ajout de '~E' + +----------------------------- +version 0.1.0 +----------------------------- + +. changement de color en set_color +. completion automatiques des options +. crosspost +. bugfixes + +----------------------------- +version 0.1pre4 +----------------------------- +... + +----------------------------- +version 0.1pre3 +----------------------------- +refonte du post et des headers par Damien +----------------------------- +. debug de next_in_thread en cas de cycles +. Possibilite de threader ' ' +. Possibilite de threader 'r' +. Ajout des commandes 'l' et 'L' +. Correction d'un bug ou l'on pouvait se retrouver dans un groupe au lancement +sans y etre abonne. +. Ajout de la possibilité de sauver comme un folder mail +----------------------------- +version 0.1pre2 +----------------------------- diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..734c3ea --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,80 @@ +#------------------------------------------------------------------------------ +# +# Makefile pour flrn +# Une tentative désespérée de faire sérieux en faisant un joli makefile. +# +# damien@naoned.ens.fr 21/11/97 +#------------------------------------------------------------------------------ + +# C compiler + +CC = @CC@ +DEBUGFLAGS = +CPPFLAGS = $(DEBUGFLAGS) @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +MISCLIBS = @LIBS@ +MAKEDEPEND = @MAKEDEPEND@ +INSTALL = @INSTALL@ + +# define it to .iso if man accects 8 bit pages. +MANISO = @MANISO@ + +# Quelques répertoires + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +datadir = @datadir@ +mandir = @mandir@ +flrndir = $(datadir)/flrn +helpdir = $(flrndir)/help + +#Sources + +C_FILES = flrn_tcp.c options.c flrn_files.c group.c art_group.c tty_display.c tty_keyboard.c flrn_inter.c post.c flrn_string.c main.c rfc2047.c flrn_help.c flrn_regexp.c flrn_slang.c flrn_xover.c flrn_menus.c flrn_color.c compatibility.c flrn_shell.c flrn_pager.c flrn_format.c flrn_filter.c + +#Headers + +HDRS = flrn.h options.h flrn_config.h flrn_glob.h extern.h group.h art_group.h post.h flrn_string.h rfc2047.h pathdef.h version.h site_config.h flrn_slang.h flrn_menus.h flrn_filter.h flrn_pager.h + +#Objets + +OBJS = art_group.o compatibility.o flrn_color.o flrn_files.o flrn_format.o flrn_help.o flrn_inter.o flrn_menus.o flrn_pager.o flrn_regexp.o flrn_shell.o flrn_slang.o flrn_string.o flrn_tcp.o flrn_xover.o group.o main.o options.o post.o rfc2047.o tty_display.o tty_keyboard.o flrn_filter.o + +# Suite a modifier au cas où + +all: flrn ../man/flrn.1$(MANISO) + +flrn: $(OBJS) + $(CC) $(LDFLAGS) -o flrn $(OBJS) $(MISCLIBS) + +clean: + rm -f *.o core + +install: installbin installhelp installman + +installbin: flrn + $(INSTALL) -d -m 755 $(bindir) + $(INSTALL) -s -m 755 flrn $(bindir)/flrn + +installhelp: + $(INSTALL) -d -m 755 $(helpdir) + for i in ../help/* ; do $(INSTALL) -m 644 $$i $(helpdir); done + +installman: ../man/flrn.1$(MANISO) + $(INSTALL) -d -m 755 $(mandir)/man1 + $(INSTALL) -m 644 ../man/flrn.1$(MANISO) $(mandir)/man1/flrn.1 + +depend: pathdef.h + $(MAKEDEPEND) $(CPPFLAGS) $(C_FILES) + +../man/flrn.1: ../man/flrn.1.iso + cd ../man; make + +pathdef.h: Makefile + rm -f pathdef.h + -@echo 'Building pathdef.h' + -@echo '/* This file is automatically created by Makefile' >> pathdef.h + -@echo ' * DO NOT EDIT! Change Makefile only. */' >> pathdef.h + -@echo '#define PATH_HELP_DIRECTORY "$(helpdir)/"' >> pathdef.h diff --git a/src/art_group.c b/src/art_group.c new file mode 100644 index 0000000..32ef7eb --- /dev/null +++ b/src/art_group.c @@ -0,0 +1,900 @@ +/* flrn v 0.1 */ +/* art_group.c 25/11/97 */ +/* */ +/* Gestion des articles au sein d'un groupe. */ +/* */ +/* */ + +#include +#include +#include +#include +#include + +#include + +#include "flrn.h" +#include "art_group.h" +#include "group.h" + +const Known_Headers Headers[NB_KNOWN_HEADERS] = { + { "From:", 5 }, + { "References:", 11 }, + { "Subject:", 8 }, + { "Date:", 5 }, + { "Newsgroups:", 11 }, + { "Followup-To:", 12 }, + { "Organization:", 13 }, + { "Lines:", 6 }, + { "Sender:", 7 }, + { "Reply-To:", 9 }, + { "Expires:", 8 }, + { "X-Newsreader:", 13 }, + { "To:", 3 }, + { "Cc:", 3 }, + { "Bcc:", 4 }, + { "Xref:", 5 }, +}; + + +Article_List *Article_courant; +Article_List *Article_deb; +Article_List *Article_exte_deb; +Article_List Article_bidon = {NULL, NULL, NULL, NULL, NULL, NULL, -10, -10, + FLAG_READ, NULL, NULL,NULL}; +/* Est-il preferable de considerer l'article bidon comme lu ? */ +/* On va reserver le numero -1 pour les articles extérieurs au newsgroup */ +Last_headers_cmd Last_head_cmd; + +long Article_deb_key=1; + +#define HASH_SIZE 1024 +time_t Date_groupe; + +/* libere la memoire d'un article */ +void free_article_headers(Article_Header *headers) { + int i; + if (!headers) return; + for (i=0; ik_headers[i]) + free(headers->k_headers[i]); + if (headers->reponse_a) free(headers->reponse_a); + free(headers); +} + +static void free_one_article(Article_List *article,int flag) { + if (!article) return; + free_article_headers(article->headers); + if (flag) { + if (article->msgid) free(article->msgid); + free(article); + } else { + article->headers=NULL; + } +} + +/* Cette fonction relie l'article pere et l'article fils */ +static void relie_article(Article_List *pere, Article_List *fils) { + Article_List *temp; + + if ((fils==NULL) || (pere==NULL)) return; + fils->pere=pere; + fils->parent=pere->numero; + fils->frere_suiv=NULL; + if (pere->prem_fils==NULL) { + pere->prem_fils=fils; + fils->frere_prev=NULL; + /* Je renonce provisoirement à la recherche de cousins */ + /* (surtout qu'il faudrait noter dans ce cas à quel profondeur est */ + /* le cousin. */ + } else { + temp=pere->prem_fils; + while (temp->frere_suiv) + temp=temp->frere_suiv; + temp->frere_suiv=fils; + fils->frere_prev=temp; + } + return; +} + +/* on crée les liens à partir des headers References + * on met des messages exterieurs pour les parents abscents */ +int cree_liens() { + int hash; + Article_List *Hash_table[HASH_SIZE]; + Article_List *Hash_table_small[HASH_SIZE]; + /* pour du debug */ + int good=0, bad=0, vbad=0; + Article_List *creation; + Article_List *creation2; + char *bufptr, *buf2, *buf=NULL; + + memset(Hash_table,0, sizeof(Hash_table)); + memset(Hash_table_small,0, sizeof(Hash_table)); + /* première passe pour mettre en place les tables de hash */ + for(creation=Article_deb; creation; creation=creation->next) { + hash=0; for(bufptr=creation->msgid; *bufptr; bufptr++) hash += *bufptr; + hash %= HASH_SIZE; + creation->prev_hash=Hash_table[hash]; + Hash_table[hash]=creation; + } + /* faut les mettre après, puisqu'il pourrait y avoir collision */ + for(creation=Article_exte_deb; creation; creation=creation2) { + hash=0; for(bufptr=creation->msgid; *bufptr; bufptr++) hash += *bufptr; + hash %= HASH_SIZE; + bufptr=creation->msgid; + creation->prev_hash=Hash_table[hash]; + Hash_table[hash]=creation; + for (creation2=creation->prev_hash; creation2; + creation2=creation2->prev_hash) { + if (strcmp(bufptr, creation2->msgid)==0) { + /* on a un article a virer - il ne peut pas encore y avoir de + * ref dessus */ + Hash_table[hash]=creation->prev_hash; + if (creation->prev) creation->prev->next=creation->next; + else Article_exte_deb=creation->next; + if (creation->next) creation->next->prev=creation->prev; + creation2=creation->next; + free_one_article(creation,1); + creation=NULL; + break; + } + } + if (creation) creation2=creation->next; + } + /* deuxième passe pour faire les liens */ + for(creation2=Article_deb; creation2; creation2=creation2->next) { + /* Hash_table_small sert a recher en priorité avant l'article */ + hash=0; for(bufptr=creation2->msgid; *bufptr; bufptr++) hash += *bufptr; + hash %= HASH_SIZE; + Hash_table_small[hash]=creation2; + if (!creation2->headers || + !(bufptr=creation2->headers->k_headers[REFERENCES_HEADER])) + continue; + if ((creation2->parent > 0)) continue; + /* on peut eventuellement remplacer un lien vers un article + * exterieur bidon */ + buf2=strrchr(bufptr, '<'); + if (!buf2) continue; + if (buf2) { + buf = safe_strdup(buf2); + buf2=strchr(buf, '>'); + if (buf2) buf2[1]='\0'; + else { if (debug) fprintf(stderr,"J'ai un header bugue...\n"); + free(buf); + continue; + } + hash=0; for(bufptr=buf; *bufptr; bufptr++) hash += *bufptr; + hash %= HASH_SIZE; + /* maintenant, on recherche le pere + * en premier lieu avant grace a Hash_table_small */ + for (creation=Hash_table_small[hash]; creation; + creation=creation->prev_hash) + if (strcmp(buf, creation->msgid)==0) { + relie_article(creation, creation2); + good++; /* pour des stats -- ca m'a deja permis de trouver un bug */ + break; + } + /* on n'a pas trouvé, on cherche après */ + if(!creation2->parent) + for (creation=Hash_table[hash]; creation!=Hash_table_small[hash]; + creation=creation->prev_hash) + if (strcmp(buf, creation->msgid)==0) { + relie_article(creation, creation2); + bad++; + break; + } + /* on n'a pas trouvé, ben on crée un père bidon */ + if (!creation2->parent) { + /* on crée un article bidon exterieur */ + creation = safe_calloc (1,sizeof(Article_List)); + creation->numero=-1; /* c'est un article bidon ou ext */ + if (Article_exte_deb) { + creation->next=Article_exte_deb; + } + Article_exte_deb=creation; + creation->msgid = safe_strdup(buf); + /* on le met dans la table de hash, a la fin */ + creation->prev_hash = Hash_table[hash]; + Hash_table[hash] = creation; + relie_article(creation, creation2); + vbad++; + } + free(buf); + } /* if(buf2) */ + } + if(debug) { + fprintf(stderr,"\n %d %d %d\n", + good,bad,vbad); + } + /* TODO réécrire ça... */ + creation=Article_courant; + save_etat_loop(); + Article_courant=Article_deb; + while(Article_courant) { + apply_kill(0); + Article_courant=Article_courant->next; + } + Article_courant=creation; + restore_etat_loop(); + return 0; +} + +Article_Header *new_header() { + return (Article_Header *) safe_calloc(1,sizeof(Article_Header)); +} + +/* Fonction pour libérer Last_head_cmd */ +static void Free_Last_head_cmd () { + Header_List *tmp,*tmp2; + + if (Last_head_cmd.Article_vu==NULL) { + Last_head_cmd.headers=NULL; + return; + } + Last_head_cmd.Article_vu=NULL; + tmp=Last_head_cmd.headers; + while (tmp) { + tmp2=tmp->next; + free(tmp->header); + free(tmp); + tmp=tmp2; + } + Last_head_cmd.headers=NULL; +} + +/* Cette fonction crée la structure headers d'un article. Elle renvoie */ +/* un pointeur sur la structure. */ +/* Si rech_pere==0, on ne recherche pas de reponse_a. reponse_a pourra */ +/* alors etre obtenu par ajoute_reponse_a. */ +/* Si others=1, on detruit et refait les autres headers... */ +Article_Header *cree_header(Article_List *article, int rech_pere, int others) { + int res, code, flag=2; /* flag & 1 : debut de ligne */ + /* & 2 : fin de ligne */ + int header_courant=NULL_HEADER, i; + Article_Header *creation; + Header_List **actuel=NULL; + char *num; + + /* Ecriture de la commande */ + num=safe_malloc(260*sizeof(char)); /* ca peut aussi etre une reference */ + /* on utilise en fait toujours le message id puisqu'on peut être + * appelé sur des artciles extérieurs de numéro > 0 au groupe... */ + if (!article->msgid) sprintf(num,"%d",article->numero); else + strcpy(num,article->msgid); + res=write_command(CMD_HEAD, 1, &num); + free(num); + if (res<0) return NULL; + code=return_code(); + if ((code<0) || (code>300)) return NULL; /* Erreur, ou l'article n'est */ + /* pas encore disponible. */ + + free_article_headers(article->headers); + article->headers=creation=new_header(); + if (others) { + Free_Last_head_cmd(); + Last_head_cmd.Article_vu=article; + } + + /* Resultats */ + do { + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) return creation; + flag=(flag & 2 ? 1 : 0); + if (res>1) { + flag|=2*(tcp_line_read[res-2]=='\r'); + if (flag & 2) tcp_line_read[res-2]='\0'; + } else continue; /* ca signifie tcp_line_read='\n' */ +/* rfc2047_decode(tcp_line_read,tcp_line_read,MAX_READ_SIZE); */ + + if ((flag & 1) && (!isblank(tcp_line_read[0]))) + header_courant=NULL_HEADER; + /* On a affaire à un */ + /* nouveau header */ + else { + if (header_courant!=NULL_HEADER) { + if (header_courant!=UNKNOWN_HEADER) { + creation->k_headers[header_courant]= + safe_realloc(creation->k_headers[header_courant], + (strlen(creation->k_headers[header_courant])+ + strlen(tcp_line_read)+2)*sizeof(char)); + if (flag & 1) strcat(creation->k_headers[header_courant], "\n"); + strcat(creation->k_headers[header_courant], tcp_line_read); + } else { /* others==1 */ + (*actuel)->header= + safe_realloc((*actuel)->header, + (strlen((*actuel)->header)+ + strlen(tcp_line_read)+2)*sizeof(char)); + if (flag & 1) strcat((*actuel)->header, "\n"); + strcat((*actuel)->header, tcp_line_read); + } + } + continue; + } + for (i=0; ik_headers[i]) { + if (debug) fprintf(stderr, "Double header %s ???", + Headers[i].header); + header_courant=NULL_HEADER; + } else { + header_courant=i; + creation->k_headers[i]=safe_malloc(strlen(tcp_line_read+Headers[i].header_len)*sizeof(char)); + strcpy(creation->k_headers[i], tcp_line_read+Headers[i].header_len+1); + } + break; + } + } + if ((i==NB_KNOWN_HEADERS) && (!(flag & 1) || (tcp_line_read[0]!='.'))) + if (others) { + header_courant=UNKNOWN_HEADER; + if (actuel) { + (*actuel)->next=safe_malloc(sizeof(Header_List)); + actuel=&((*actuel)->next); + } else { + actuel=&(Last_head_cmd.headers); + (*actuel)=safe_malloc(sizeof(Header_List)); + } + (*actuel)->header=safe_strdup(tcp_line_read); + } else header_courant=NULL_HEADER; + } while (!(flag & 1) || (tcp_line_read[0]!='.')); + + if (others) (*actuel)->next=NULL; + + /* tous les headers sont valides */ + /* on décode tout ! */ + for (i=0; ik_headers_checked[i]=1; + if(creation->k_headers[i]) { + rfc2047_decode(creation->k_headers[i],creation->k_headers[i], + strlen(creation->k_headers[i])); + creation->k_headers[i]= + safe_realloc(creation->k_headers[i],strlen(creation->k_headers[i])+1); + } + } + creation->all_headers=1; + actuel=&(Last_head_cmd.headers); + while(actuel && (*actuel)) { + rfc2047_decode((*actuel)->header,(*actuel)->header, + strlen((*actuel)->header)); + (*actuel)->header= + safe_realloc((*actuel)->header,strlen((*actuel)->header)+1); + actuel=&(*actuel)->next; + } + + if (creation->k_headers[LINES_HEADER]) + creation->nb_lines=atoi(creation->k_headers[LINES_HEADER]); + + /* Recherche de reponse a */ + if (rech_pere) ajoute_reponse_a(article); + + /* Parsing de la date */ + if (creation->k_headers[DATE_HEADER]) + creation->date_gmt=parse_date(creation->k_headers[DATE_HEADER]); + + return creation; +} + + +/* Ajoute le champ reponse_a à la structure article_headers */ +void ajoute_reponse_a(Article_List *article) { + + if (!article->pere) return; + if (!article->headers) return; + + if (!article->pere->headers || + (article->pere->headers->k_headers_checked[FROM_HEADER]==0)) + cree_header(article->pere,0,0); + if (article->pere->headers && article->pere->headers->k_headers[FROM_HEADER]) { + article->headers->reponse_a=safe_malloc( + (strlen(article->pere->headers->k_headers[FROM_HEADER])+1)*sizeof(char)); + strcpy(article->headers->reponse_a, + article->pere->headers->k_headers[FROM_HEADER]); + } + article->headers->reponse_a_checked=1; + + return; +} + +/* Ajouter un message localement à la liste des messages d'un newsgroup */ +/* On ne se casse surtout pas la tête. */ +/* On renvoie NULL en cas d'echec, l'article cree en cas de succes. */ +/* maintenant plus : si exte=1, on ajoute le message dans la liste exterieur */ +Article_List *ajoute_message (char *msgid, int exte) { + Article_List *creation, *parcours, *last=NULL; + char *buf, *buf2; + char save; + int res,code; + + /* j'espere que stat va marcher */ + buf=strchr(msgid,'>'); + if (buf) *(++buf)='\0'; else return NULL; + res=write_command(CMD_STAT, 1, &msgid); + if (res<1) return NULL; + res=read_server(tcp_line_read,3, MAX_READ_SIZE-1); + code=strtol(tcp_line_read, &buf, 0); + if (code>400) return NULL; + creation=safe_calloc(1,sizeof(Article_List)); + if (exte) creation->numero=-1; else creation->numero=strtol(buf,NULL,0); + creation->msgid=safe_strdup(msgid); + creation->headers=cree_header(creation, 0, 1); + if (creation->headers==NULL) { + free(creation->msgid); + free(creation); + return NULL; + } + + if (exte) parcours=Article_exte_deb; else parcours=Article_deb; + if ((parcours!=NULL) && (parcours!=&Article_bidon)) { + while (parcours && (exte || (parcours->numeronumero))) { + last=parcours; + if (exte && (strcmp(parcours->msgid, creation->msgid)==0)) { + if (debug) fprintf(stderr, "Un père déjà ajouté ?\n"); break; } + /* Théoriquement, c'est possible, mais rare */ + parcours=parcours->next; + } + if (parcours && (exte || (parcours->numero==creation->numero))) { + free_one_article(creation,1); + return (exte ? parcours : 0); + /* On va se servir de ce retour si exte=1 pour refaire */ + /* la liaison pere-fils */ + } + /* Ici, si exte=1, parcours==NULL et Article_exte_deb!=NULL */ + if (parcours!=Article_deb) { + last -> next=creation; + creation->prev=last; + } else Article_deb=creation; + if (parcours!=NULL) { + creation->next=parcours; + parcours->prev=creation; + } + } else if (exte) Article_exte_deb=creation; else Article_deb=creation; + if ((!exte) && (Newsgroup_courant->maxnumero)) { + Newsgroup_courant->max=creation->numero; + Aff_newsgroup_courant(); + } + if (exte) return creation; + /* On s'occupera a cote de l'ajout de l'exterieur */ + if (creation->headers->k_headers[REFERENCES_HEADER]) { + buf=strrchr(creation->headers->k_headers[REFERENCES_HEADER], '<'); + buf2=strchr(buf, '>'); + save=*(++buf2); + *buf2='\0'; + parcours=creation->prev; + while (parcours) { + if (strcmp(parcours->msgid,buf)==0) break; + parcours=parcours->prev; + } + if (parcours==NULL) { + parcours=creation->next; + while (parcours) { + if (strcmp(parcours->msgid, buf)==0) break; + parcours=parcours->next; + } + } + if (parcours==NULL) { + parcours=Article_exte_deb; + while (parcours) { + if (strcmp(parcours->msgid, buf)==0) break; + parcours=parcours->next; + } + } + if (parcours!=NULL) relie_article(parcours, creation); + } + return creation; +} + +/* Essaie d'ajout direct d'un message localement */ +/* On ne se casse surtout pas la tête. */ +/* On part cette fois du numéro du message, et non de son message-ID */ +/* Enfin... d'une suite de numéro, on rend le premier qui marche... */ +/* On renvoie NULL en cas d'echec, l'article cree en cas de succes. */ +/* Beaucoup de code vient de ajoute_message... C'est dommage, mais tant pis */ +/* On devra corriger ça plus tard... */ +Article_List *ajoute_message_par_num (int min, int max) { + Article_List *creation, *parcours, *last=NULL; + char *buf, *buf2; + char save; + int i,res,code; + + buf2=safe_malloc(16); + for (i=min;i<=max;i++) { + /* j'espere que stat va marcher */ + sprintf(buf2,"%d",i); + res=write_command(CMD_STAT, 1, &buf2); + if (res<1) return NULL; + res=read_server(tcp_line_read,3, MAX_READ_SIZE-1); + code=strtol(tcp_line_read, &buf, 0); + if ((code>200) && (code<300)) break; + } + free(buf2); + if (i>max) return NULL; + buf=strchr(buf,'<'); if (buf==NULL) return NULL; /* Beurk !!! */ + buf2=strchr(buf,'>'); if (buf2) *(buf2+1)='\0'; + creation=safe_calloc(1,sizeof(Article_List)); + creation->numero=i; + creation->msgid=safe_strdup(buf); + creation->headers=cree_header(creation, 0, 1); + if (creation->headers==NULL) { + free(creation->msgid); + free(creation); + return NULL; + } + + parcours=Article_deb; + if ((parcours!=NULL) && (parcours!=&Article_bidon)) { + while (parcours && (parcours->numeronumero)) { + last=parcours; + parcours=parcours->next; + } + if (parcours && (parcours->numero==creation->numero)) { + free_one_article(creation,1); + return 0; /* Théoriquement, on ne peut arriver ici */ + } + if (parcours!=Article_deb) { + last -> next=creation; + creation->prev=last; + } else Article_deb=creation; + if (parcours!=NULL) { + creation->next=parcours; + parcours->prev=creation; + } + } else Article_deb=creation; + if (Newsgroup_courant->maxnumero) { + Newsgroup_courant->max=creation->numero; + Aff_newsgroup_courant(); + } + if (creation->headers->k_headers[REFERENCES_HEADER]) { + buf=strrchr(creation->headers->k_headers[REFERENCES_HEADER], '<'); + buf2=strchr(buf, '>'); + save=*(++buf2); + *buf2='\0'; + parcours=creation->prev; + while (parcours) { + if (strcmp(parcours->msgid,buf)==0) break; + parcours=parcours->prev; + } + if (parcours==NULL) { + parcours=creation->next; + while (parcours) { + if (strcmp(parcours->msgid, buf)==0) break; + parcours=parcours->next; + } + } + if (parcours==NULL) { + parcours=Article_exte_deb; + while (parcours) { + if (strcmp(parcours->msgid, buf)==0) break; + parcours=parcours->next; + } + } + if (parcours!=NULL) relie_article(parcours, creation); + } + return creation; +} + + +/* Libère la mémoire de la liste courante... */ +/* On fait cela avant chaque changement de newsgroup. */ +/* Est-ce ce qu'il faut faire ? J'en sais rien. */ +/* Peut-etre plutot garder la structure de chaque newsgroup une fois */ +/* qu'elle a ete créée... */ +/* En tout cas, on met a jour la liste des messages lus */ +/* flag = 0 on garde des infos... */ +void detruit_liste(int flag) { + Range_List *msg_lus=Newsgroup_courant->read; + Range_List *tmprange; + Article_List *tmparticle=NULL; + int lus_index=0; + int min=1; + int max=1; + + Article_deb_key++; + if (!msg_lus) { + msg_lus = safe_calloc(1,sizeof(Range_List)); + Newsgroup_courant->read = msg_lus; + } + /* attention au premier article... */ + if(Article_deb && (Article_deb->numero==1) && + !(Article_deb->flag & FLAG_READ)) + min=0; + + tmparticle=Article_courant=Article_deb; + for (;Article_courant; Article_courant=tmparticle) { + tmparticle=Article_courant->next; + if ((Article_courant->flag & FLAG_READ) && (!min)) + { + min = Article_courant->numero; + } + if ((!(Article_courant->flag & FLAG_READ)) && min) + { + msg_lus->min[lus_index] = min; + msg_lus->max[lus_index] = Article_courant->numero -1; + lus_index++; + if (lus_index >= RANGE_TABLE_SIZE) + { + if (!(msg_lus->next)) + msg_lus->next = safe_calloc(1,sizeof(Range_List)); + msg_lus=msg_lus->next; + lus_index=0; + } + min=0; + } + max = Article_courant->numero; + free_one_article(Article_courant,flag); + } + if (min) { + msg_lus->min[lus_index] = min; + msg_lus->max[lus_index] = Newsgroup_courant->max; + /* Peut être (hélas !) different du max */ + /* du groupe... */ + /* Dans ce cas, on préférera peut-être */ + /* le max du groupe, je sais pas... :-( */ + lus_index++; + } + /* on met a 0 la fin de la table */ + for (; lus_index< RANGE_TABLE_SIZE; lus_index++) + msg_lus->min[lus_index] = msg_lus->max[lus_index] =0; + tmprange=msg_lus->next; /* liste a liberer */ + msg_lus->next=NULL; + while (tmprange) { + msg_lus=tmprange; + tmprange=msg_lus->next; + free(msg_lus); + } + /* On libere aussi la structure des Article_exte_deb, mais sans question */ + /* de lecture... */ + Article_courant=tmparticle=Article_exte_deb; + for (; Article_courant; Article_courant=tmparticle) { + tmparticle=Article_courant->next; + free_one_article(Article_courant,flag); + } + if(flag) + Article_deb=Article_courant=Article_exte_deb=NULL; + else { + Newsgroup_courant->Article_deb=Article_deb; + Newsgroup_courant->Article_exte_deb=Article_exte_deb; + } + Free_Last_head_cmd(); +} + +void libere_liste() +{ + Article_List *tmparticle=NULL; + Article_courant=tmparticle=Article_deb; + for (; Article_courant; Article_courant=tmparticle) { + tmparticle=Article_courant->next; + free_one_article(Article_courant,1); + } + Article_courant=tmparticle=Article_exte_deb; + for (; Article_courant; Article_courant=tmparticle) { + tmparticle=Article_courant->next; + free_one_article(Article_courant,1); + } + Article_deb=Article_courant=Article_exte_deb=NULL; +} + +/* donne le prochain article de la thread... + * on ne peut pas boucler. + * On fait un parcours en profondeur */ +Article_List * raw_next_in_thread(Article_List *start, int *level) +{ + Article_List *famille; + Article_List *famille2; + int mylevel=0; + int collision_vue=0; + + if (level) mylevel=*level; + famille=start; + /* on commence a descendre suivant les fils */ + if(famille->prem_fils) { + mylevel ++; + famille=famille->prem_fils; + while(famille->frere_prev) /* on prend le premier fils */ + famille=famille->frere_prev; + if (level) *level=mylevel; + return famille; + } + famille2=famille; + while(mylevel >=1) { + /* on regarde les frères dans l'ordre */ + if(famille->frere_suiv) { + famille=famille->frere_suiv; + if (level) *level=mylevel; + return famille; + } + /* on remonte si on n'est pas trop haut */ + if (mylevel && famille->pere) famille=famille->pere; + else return NULL; + if(famille2) famille2=famille2->pere; + if(famille2) famille2=famille2->pere; + mylevel --; + /* On detecte les collisions + * On veut les voir deux fois pour etre sur de ne rien oublier */ + if(famille==famille2) { + if (collision_vue) return NULL; else collision_vue=1; + } + } + return NULL; +} + +/* donne l'article suivant de la thread ayant le flag flag a la valeur set + * On se limite entre deb et fin + * modifie *level si level!=NULL; + * set vaut 0 ou flag... */ +Article_List * next_in_thread(Article_List *start, long flag, int *level, + int deb, int fin, int set) +{ + Article_List *famille; + Article_List *famille2; + int mylevel=1<<30; + int mylevel2; + + if (level) mylevel=*level; + mylevel2=mylevel; + if(fin ==0) fin=1<<30; + famille=start; + famille2=raw_next_in_thread(start,&mylevel2); + do { + famille = raw_next_in_thread(famille,&mylevel); + if (famille2) famille2 = raw_next_in_thread(famille2,&mylevel2); + if (famille2) famille2 = raw_next_in_thread(famille2,&mylevel2); + if ((famille) && ((famille->flag & flag)==set) && + (famille->numero<=fin) && (famille->numero>=deb)) { + if (level) *level=mylevel; + return famille; + } + } while(famille && (famille != famille2)); + if (famille && (famille == famille2)) { + /* on a un cycle !!! */ + /* on essaie de trouver des freres pour en sortir */ + while(!(famille->frere_suiv)){ + famille = famille->pere; + if (famille==famille2) return NULL; + } + /* y'a un embranchement */ + /* le frere ne peut pas etre dans le cycle, donc on en est sorti */ + famille=famille->frere_suiv; + famille2=famille; + do { + if ((famille) && ((famille->flag & flag)==set) && + (famille->numero<=fin) && (famille->numero>=deb)) { + if (level) *level=mylevel; + return famille; + } + famille = raw_next_in_thread(famille,&mylevel); + } while (famille != famille2); + return NULL; + } + /* y'a plus rien a donner */ + return NULL; +} + +/* Trouve l'ancetre de la thread */ +/* si flag=1, on admet d'aller hors de la liste principale */ +Article_List * root_of_thread(Article_List *article, int flag) { + Article_List *racine=article; + Article_List *racine2=article; + + if(racine->pere && (flag || (racine->pere->numero!=-1))) + racine2=racine->pere; + while((racine2->pere && (flag || (racine2->pere->numero!=-1))) + &&(racine2!=racine)) { + racine=racine->pere; + racine2=racine2->pere; + if(racine2->pere && (flag || (racine2->pere->numero!=-1))) + racine2=racine2->pere; + } + return racine2; +} + +/* Parse le champ Xref, et marque les copies comme lues */ +void article_read(Article_List *article) +{ + Newsgroup_List *mygroup; + char *newsgroup; + char *num; + char *buf; + int numero; + if ((article->headers==NULL) || + (article->headers->k_headers_checked[XREF_HEADER]==0)) + cree_header(article,0,0); + article->flag |= FLAG_READ; + if (article->headers->k_headers[XREF_HEADER]==NULL) return; + /* Eviter un segfault quand le serveur est Bipesque */ + if (rindex(article->headers->k_headers[XREF_HEADER],':') == + index(article->headers->k_headers[XREF_HEADER],':')) return; + buf=safe_strdup(article->headers->k_headers[XREF_HEADER]); + newsgroup=strtok(buf," "); /* magog.ens.fr */ + while((newsgroup=strtok(NULL," :"))) + { + num=strtok(NULL,": "); + if (!num) {free(buf); return;} + numero=atoi(num); + if (strcmp(newsgroup,Newsgroup_courant->name)==0) continue; + for(mygroup=Newsgroup_deb;mygroup; mygroup=mygroup->next){ + if (strcmp(mygroup->name,newsgroup)==0) { + add_read_article(mygroup,numero); break; + } + } + } + free(buf); return; +} + +/* Recherche d'un article par numero... retour : article obtenu */ +/* et point de depart (c'est mieux) */ +/* flags : si non trouvé : -1 : prendre le precedent */ +/* 0 : renvoyer NULL, 1 : prendre le suivant */ +/* retour : 0 : OK -1 : renvoye le proche -2 : rien a renvoyer */ +int Recherche_article (int num, Article_List **retour, int flags) { + Article_List *parcours=*retour; + + if (parcours==NULL) parcours=Article_deb; else + if (parcours->numero==-1) parcours=parcours->prem_fils; + /* On ne recherche que dans le newsgroup */ + if (parcours==NULL) return -2; + if (parcours->numero==num) { + *retour=parcours; + return 0; + } + if (parcours->numeronext && (parcours->next->numero<=num)) + parcours=parcours->next; + if ((parcours->numero==num) || (flags==-1)) { + *retour=parcours; + return -(parcours->numero!=num); + } + if (flags==1) { + *retour=parcours->next; + return (*retour ? -1 : -2); + } + *retour=NULL; + return -2; + } + while (parcours->prev && (parcours->prev->numero>=num)) + parcours=parcours->prev; + if ((parcours->numero==num) || (flags==1)) { + *retour=parcours; + return -(parcours->numero!=num); + } + if (flags==-1) { + *retour=parcours->prev; + return (*retour ? -1 : -2); + } + *retour=NULL; + return -2; +} + +/* Détermine si on est le propriétaire d'un article */ +/* retour : 1 si oui, 0 si non, -1 si bug */ +int Est_proprietaire(Article_List *article) { + char *la_chaine, *ladresse, *buf; + + if (!article->headers) { + cree_header(article,0,0); + if (!article->headers) return -1; + } + la_chaine=article->headers->k_headers[SENDER_HEADER]; + if (!la_chaine) la_chaine=article->headers->k_headers[FROM_HEADER]; + if (!la_chaine) return -1; + /* On veut une adresse EXACTE */ + ladresse=safe_malloc(257+strlen(flrn_user->pw_name)); + Get_user_address(ladresse); + buf=strstr(la_chaine,ladresse); + if (buf==NULL) { + free(ladresse); + return 0; + } + if ((buf!=la_chaine) && (isalnum(*buf-1))) { + free(ladresse); + return 0; + } + if (isalnum(*(buf+strlen(ladresse)))) { + free(ladresse); + return 0; + } + free(ladresse); + return 1; /* C'est bon */ +} + diff --git a/src/art_group.h b/src/art_group.h new file mode 100644 index 0000000..472968b --- /dev/null +++ b/src/art_group.h @@ -0,0 +1,102 @@ +/* flrn v 0.1 */ +/* art_group.h 25/11/97 */ +/* */ +/* Headers pour la gestion d'une liste d'article au sein du newsgroup. */ + +#ifndef FLRN_ART_GROUP_H +#define FLRN_ART_GROUP_H + +#include + +typedef struct Flrn_known_headers { + char *header; + int header_len; +} Known_Headers; + +extern const Known_Headers Headers[]; + +#define UNKNOWN_HEADER -2 +#define NULL_HEADER -1 +#define FROM_HEADER 0 +#define REFERENCES_HEADER 1 +#define SUBJECT_HEADER 2 +#define DATE_HEADER 3 +#define NEWSGROUPS_HEADER 4 +#define FOLLOWUP_TO_HEADER 5 +#define ORGANIZATION_HEADER 6 +#define LINES_HEADER 7 +#define SENDER_HEADER 8 +#define REPLY_TO_HEADER 9 +#define EXPIRES_HEADER 10 +#define X_NEWSREADER_HEADER 11 +#define TO_HEADER 12 +#define CC_HEADER 13 +#define BCC_HEADER 14 +#define XREF_HEADER 15 + + +#define NB_KNOWN_HEADERS 16 + +typedef struct Flrn_art_header +{ + char *k_headers[NB_KNOWN_HEADERS]; + int k_headers_checked[NB_KNOWN_HEADERS]; + char *reponse_a; + int nb_lines, all_headers, reponse_a_checked; + time_t date_gmt; + /* + int avec_xover; + */ +} Article_Header; + +typedef struct Flrn_art_list +{ + struct Flrn_art_list *next, *prev, *pere; + struct Flrn_art_list *prem_fils, *frere_prev, *frere_suiv; + int numero; + int parent; + + +#define FLAG_READ 1 + /* Utilise en interne, il faut le mettre a 0 avant de l'utiliser */ +#define FLAG_DISPLAYED 2 + /* utilise en interne par distribue + * il faut le mettre a 0 apres l'avoir utilise */ +#define FLAG_TAGGED 4 + /* Flag a utiliser avec distribue_action + * utilise par le resume + * Il doit en etat normal etre a 0 */ +#define FLAG_ACTION 8 + /* Flag disant d'appliquer le kill_file */ +#define FLAG_NEW 16 + int flag; + + char *msgid; + Article_Header *headers; + struct Flrn_art_list *prev_hash; +} Article_List; + +extern Article_List *Article_courant; +extern Article_List *Article_deb; +/* entier incrémenté à chaque fois que la liste d'article est blastée */ +extern long Article_deb_key; +extern Article_List *Article_exte_deb; +extern Article_List Article_bidon; +extern time_t Date_groupe; + + +/* pour les autre headers */ +typedef struct Flrn_header_list { + struct Flrn_header_list *next; + char *header; + int num_af; /* utile lors de l'affichage */ +} Header_List; + +typedef struct Flrn_last_headers_cmd { + Article_List *Article_vu; + Header_List *headers; +} Last_headers_cmd; + +extern Last_headers_cmd Last_head_cmd; + +#endif diff --git a/src/compatibility.c b/src/compatibility.c new file mode 100644 index 0000000..0b24f99 --- /dev/null +++ b/src/compatibility.c @@ -0,0 +1,15 @@ +#include +#include + +#include "config.h" + +#ifndef HAVE_SNPRINTF +int snprintf(char *out, int len, char *fmt, ...) { + va_list ap; + int res; + va_start(ap, fmt); + res=vsprintf (out, fmt, ap); + va_end(ap); + return res; +} +#endif diff --git a/src/compatibility.h b/src/compatibility.h new file mode 100644 index 0000000..13b9111 --- /dev/null +++ b/src/compatibility.h @@ -0,0 +1,150 @@ +/* On definit ce qui pourait nous manquer */ + +#include +#include +#include + +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifndef __GNUC__ +/* on veut pas d'erreurs sur inline */ +# define inline +#endif + +#ifndef HAVE_SNPRINTF +extern int snprintf(char * /*out*/, int /*len*/, char * /*fmt*/, ...); + +#endif + +#ifndef HAVE_ISBLANK +static inline int isblank(int c) { + return (c==' ') || (c=='\t'); +} +#endif + +#ifndef HAVE_ISDIGIT +static inline int isdigit(int c) { + return (c>='0') && (c<='9'); +} +#endif + +#ifdef HAVE_REGCOMP +# ifdef HAVE_REGDEF_H +# include +# endif +# include +#else +# include "rxposix.h" +#endif + +#include +#include +#ifndef HAVE_STRSPN +static inline size_t strspn(const char *s, const char *accept) { + int n=0; + while (s[n] && (index(accept, s[n]))) n++; + return n; +} +#endif + +/* Les fonctions qui suivent ne sont pas vraiment la pour la compatibilite. */ +/* Plutot pour eviter les OS qui louzent... */ +#ifndef DEBUG_MALLOC +static inline void *safe_malloc(size_t s) { +#else +#define safe_malloc(a) __safe_malloc(a,__PRETTY_FUNCTION__,__FILE__,__LINE__) +static inline void *__safe_malloc(size_t s, char *f, char *fi, int l) { +#endif + void *res; + if (s==0) return NULL; + if ((res=malloc(s))==NULL) { + fprintf(stderr, "Mémoire insuffisante !\n"); + exit(-1); + } else { +#ifdef DEBUG_MALLOC + fprintf(stderr,"%8lx malloc %ld, %s :%s, %d\n",(long) res,(long) s, + f,fi,l); +#endif + return res; + } + return NULL; +} + +#ifndef DEBUG_MALLOC +static inline void *safe_calloc(size_t s, size_t t) { +#else +#define safe_calloc(a,b) __safe_calloc(a,b,__PRETTY_FUNCTION__,__FILE__,__LINE__) +static inline void *__safe_calloc(size_t s, size_t t, char *f, char *fi, int l) +{ +#endif + void *res; + if (s==0) return NULL; + if ((res=calloc(s,t))==NULL) { + fprintf(stderr, "Mémoire insuffisante !\n"); + exit(-1); + } else { +#ifdef DEBUG_MALLOC + fprintf(stderr,"%8lx calloc %ld * %ld, %s :%s, %d\n",(long) res,(long) s, + (long t),f,fi,l); +#endif + return res; + } + return NULL; +} + +#ifndef DEBUG_MALLOC +static inline void *safe_realloc(void *ptr, size_t s) { +#else +#define safe_realloc(ptr,s) __safe_realloc(ptr,s,\ + __PRETTY_FUNCTION__,__FILE__,__LINE__) +static inline void *__safe_realloc(void *ptr, size_t s, + char *f, char *fi, int l) { +#endif + void *res; + if (s==0) { + free(ptr); + return NULL; + } + if (ptr==NULL) return safe_malloc(s); else + if ((res=realloc(ptr,s))==NULL) { + fprintf(stderr, "Mémoire insuffisante !\n"); + exit(-1); + } else { +#ifdef DEBUG_MALLOC + fprintf(stderr,"%8lx free *realloc* (%8lx) %ld, %s :%s, %d\n", + (long) ptr, (long) res, + (long) s, __PRETTY_FUNCTION__,__FILE__,__LINE__); + fprintf(stderr,"%8lx malloc *realloc* (%8lx) %ld, %s :%s, %d\n", + (long) res, (long) ptr, + (long) s, __PRETTY_FUNCTION__,__FILE__,__LINE__); +#endif + return res; + } + return NULL; +} + +#ifndef DEBUG_MALLOC +static inline void *safe_strdup(const char *s) { +#else +#define safe_strdup(s) __safe_strdup(s,__PRETTY_FUNCTION__,__FILE__,__LINE__) +static inline void *__safe_strdup(const char *s, char *f, char *fi, int l) { +#endif + char *res; +#ifdef DEBUG_MALLOC + { char blah[1024]="*safe_strdup* "; + strcat(blah,f); + res=__safe_malloc(strlen(s)+1,blah,fi,l); + } +#else + res=safe_malloc(strlen(s)+1); +#endif + strcpy(res,s); + return res; +} + +#ifdef DEBUG_MALLOC +#define free(a) (fprintf(stderr,"%8lx free, %s :%s, %d\n",(long) a,\ + __PRETTY_FUNCTION__,__FILE__,__LINE__), free(a)) +#endif diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..4623394 --- /dev/null +++ b/src/config.h @@ -0,0 +1,88 @@ +/* src/config.h. Generated automatically by configure. */ +/* src/config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to the path of sendmail */ +#define SENDMAIL "/usr/sbin/sendmail" + +/* Définissez ceci si gethostname */ +/* n'existe pas ou si vous voulez */ +/* imposer le nom de la machine...*/ +/* ou utilisez l'option --with-default-host de ./configure */ +/* #undef DEFAULT_HOST */ + +#define DOMAIN "ens.fr" + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if you have the strftime function. */ +#define HAVE_STRFTIME 1 + +/* Define to `unsigned' if doesn't define. */ +/* #undef size_t */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the inet_aton function. */ +#define HAVE_INET_ATON 1 + +/* Define if you have the isblank function. */ +#define HAVE_ISBLANK 1 + +/* Define if you have the isdigit function. */ +#define HAVE_ISDIGIT 1 + +/* Define if you have the regcomp function. */ +#define HAVE_REGCOMP 1 + +/* Define if you have the header file. */ +/* #undef HAVE_REGDEF_H */ + +/* Define if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define if you have the strspn function. */ +#define HAVE_STRSPN 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the strtol function. */ +#define HAVE_STRTOL 1 + +/* Define if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the nsl library (-lnsl). */ +/* #undef HAVE_LIBNSL */ + +/* Define if you have the socket library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 0000000..4c7abb1 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,87 @@ +/* src/config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to the path of sendmail */ +#undef SENDMAIL + +/* Définissez ceci si gethostname */ +/* n'existe pas ou si vous voulez */ +/* imposer le nom de la machine...*/ +/* ou utilisez l'option --with-default-host de ./configure */ +#undef DEFAULT_HOST + +#undef DOMAIN + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you have the strftime function. */ +#undef HAVE_STRFTIME + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if you have the gethostname function. */ +#undef HAVE_GETHOSTNAME + +/* Define if you have the inet_aton function. */ +#undef HAVE_INET_ATON + +/* Define if you have the isblank function. */ +#undef HAVE_ISBLANK + +/* Define if you have the isdigit function. */ +#undef HAVE_ISDIGIT + +/* Define if you have the regcomp function. */ +#undef HAVE_REGCOMP + +/* Define if you have the header file. */ +#undef HAVE_REGDEF_H + +/* Define if you have the header file. */ +#undef HAVE_ERRNO_H + +/* Define if you have the select function. */ +#undef HAVE_SELECT + +/* Define if you have the snprintf function. */ +#undef HAVE_SNPRINTF + +/* Define if you have the socket function. */ +#undef HAVE_SOCKET + +/* Define if you have the strdup function. */ +#undef HAVE_STRDUP + +/* Define if you have the strspn function. */ +#undef HAVE_STRSPN + +/* Define if you have the strstr function. */ +#undef HAVE_STRSTR + +/* Define if you have the strtol function. */ +#undef HAVE_STRTOL + +/* Define if you have the header file. */ +#undef HAVE_CTYPE_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the nsl library (-lnsl). */ +#undef HAVE_LIBNSL + +/* Define if you have the socket library (-lsocket). */ +#undef HAVE_LIBSOCKET diff --git a/src/extern.h b/src/extern.h new file mode 100644 index 0000000..33bd52d --- /dev/null +++ b/src/extern.h @@ -0,0 +1,242 @@ +#ifndef FLRN_EXTERN_H +#define FLRN_EXTERN_H + +#include +#include +#include + +#include "group.h" +#include "art_group.h" +#include "flrn_string.h" +#include "flrn_glob.h" +#include "flrn_menus.h" +#include "flrn_filter.h" + +/* flrn_tcp.c */ +extern int connect_server (char * /*host*/, int /*port*/); +extern void quit_server (void); +extern int read_server (char * /*ligne*/, int /*deb*/, int /*max*/); + /* appel pour post */ +extern int raw_write_server (char * /*buf*/, unsigned int /*len*/); +extern int write_command (int /*num_com*/, int /*num_para*/, char ** /*param*/); +extern int reconnect_after_timeout(int /*refait_commande*/); +extern int discard_server(void); +extern int return_code (void); +extern int adjust_time(void); + +/* flrn_files.c */ +extern FILE *open_flrnfile (char * /*file*/,char * /*mode*/, int); +extern void rename_flnewsfile (char * /*old_link*/,char * /*new_link*/); +extern FILE *open_postfile (char * /*file*/,char * /*mode*/); +extern int stat_postfile (char * /*file*/,struct stat * /*mode*/); +extern FILE *open_flhelpfile (char /*ext*/); +extern void Copy_article (FILE * /*dest*/, Article_List * /*article*/, + int /*copie_head*/, char * /*avant*/); +extern int init_kill_file(void); + +/* flrn_shell.c */ +extern int Launch_Editor (int /*flags*/); +extern int Pipe_Msg_Start(int /*flagin*/, int /*flagout*/, char * /*cmd*/); +extern int Pipe_Msg_Stop(int /*fd*/); + +/* options.c */ +extern void init_options(void); +extern void parse_options_line(char * /*ligne*/, int /*flag*/); +extern void dump_variables(FILE * /*file*/); +extern void options_comp(char * /*option*/, int /*len*/); +extern void free_options(void); +extern void menu_config_variables(void); + +/* group.c */ +extern void init_groups(void); +extern void free_groups(int /*save_flnewsrc*/); +extern void new_groups(int /*opt_c*/); +extern Newsgroup_List *cherche_newsgroup(char * /*name*/, int /*exact*/); +extern Newsgroup_List *cherche_newsgroup_re (char * /*name*/, regex_t /*reg*/); +extern Liste_Menu *menu_newsgroup_re (char * /*name*/, regex_t /*reg*/, + int /*avec_reg*/); +extern void zap_newsgroup(Newsgroup_List * /*group*/); +extern int NoArt_non_lus(Newsgroup_List * /*group*/); +extern int cherche_newnews(void); +extern void add_read_article(Newsgroup_List * /*group*/, int /*numero*/); + +/* art_group.c */ +extern int va_dans_groupe(void); +extern int cree_liens(void); +extern Article_Header *cree_header(Article_List * /*article*/, + int /*rech_pere*/, int /*others*/); +extern void ajoute_reponse_a(Article_List * /*article*/); +extern Article_List *ajoute_message(char * /*msgid*/, int /*exte*/); +extern Article_List *ajoute_message_par_num(int , int); +extern void detruit_liste(int); +extern void libere_liste(void); +extern void free_article_headers(Article_Header *); +extern Article_List *next_in_thread(Article_List * /*start*/, + long /*flag*/, int * /*level*/, + int /*deb*/, int /*fin*/, int /*set*/); +extern Article_List *root_of_thread(Article_List * /*article*/, int /*flag*/); +extern void article_read(Article_List * /*article*/); +extern int Recherche_article (int /*num*/, Article_List ** /*retour*/, + int /*flags*/); +extern int ajoute_exte_article(Article_List * /*fils*/); +extern Article_Header *new_header(void); +extern int Est_proprietaire(Article_List * /*article*/); + +/* flrn_format */ +extern time_t parse_date(char * /*s*/); +extern char *vrai_nom(char * /*nom*/); +extern char *local_date (char * /*date*/); +extern int str_estime_len (char *, int , int ); +extern int to_make_len (char *, int , int ); +extern void Copy_format (FILE * /*tmp_file*/, char * /*chaine*/, + Article_List * /*article*/); + +/* flrn_xover */ +extern int get_overview_fmt(void); +extern int cree_liste_xover(int /*n1*/, int /*n2*/, Article_List **); +extern int cree_liste_noxover(int /*n1*/, int /*n2*/, + Article_List * /*article*/); +extern int cree_liste(int, int * ); +extern int cree_liste_end(void); + + +/* tty_display.c */ +extern int Init_screen(void); +extern void sig_winch(int ); +extern int Aff_article_courant(void); +extern void Aff_newsgroup_name(void); +extern void Aff_newsgroup_courant(void); +extern char * Prepare_summary_line(Article_List * /*article*/, + char * /*prev_subject*/, int /*level*/, char * /*buf*/, int /*buflen*/); +extern int Aff_summary_line(Article_List * /*article*/,int * /*row*/, + char * /*prev_subject*/, int /*level*/); +extern Article_List * Menu_summary (int /*deb*/, int /*fin*/, int /*thread*/); +extern int Aff_fin(const char * /*str*/); +extern int Aff_error(const char * /*str*/); +extern int Aff_file(FILE * /*file*/, char *, char *); +extern int Liste_groupe(int /*n*/, char * /*mat*/); + +/* tty_keyboard.c */ +extern int Init_keyboard(void); +extern int Attend_touche(void); +extern int getline(char * /*buf*/, int /*buffsize*/, int /*row*/, int /*col*/); +extern int magic_getline(char * /*buf*/, int /*buffsize*/, int /*row*/, + int /*col*/, char * /*magic*/, int /*flag*/); + +/* flrn_inter.c */ +int fonction_to_number(char *); +int call_func(int, char *); +extern int loop(char * /*opt*/); +extern void aff_opt_c(void); +extern void init_Flcmd_rev(void); +extern int Comp_cmd_explicite(char * /*str*/, int /*len*/); +extern int Bind_command_explicite(char * /*nom*/, int /*key*/, char * /*arg*/); +extern void save_etat_loop(void); +extern void restore_etat_loop(void); + + +/* post.c */ +extern void Get_user_address(char * /*str*/); +extern int cancel_message (Article_List * /*origine*/); +extern int post_message (Article_List * /*origine*/, char * /*name_file*/, + int /*flag*/); + +/* flrn_string.c */ +extern Lecture_List *alloue_chaine(void); +extern int free_chaine(Lecture_List * /*chaine*/); +extern int ajoute_char(Lecture_List ** /*chaine*/, int /*chr*/); +extern int enleve_char(Lecture_List ** /*chaine*/); +extern char get_char(Lecture_List * /*chaine*/, int /*n*/); +extern int str_cat (Lecture_List ** /*chaine1*/, char * /*chaine*/); +extern int str_ch_cat(Lecture_List ** /*chaine1*/, Lecture_List * /*chaine2*/, + int /*place*/, char /*chr*/); + +/* flrn_menus.c */ +extern void *Menu_simple (Liste_Menu * /*debut_menu*/, Liste_Menu * /*actuel*/, + void action(void *,char *,int), + int action_select(void *, char **, int, char *, int)); +extern void Libere_menu (Liste_Menu * /*debut*/); +extern void Libere_menu_noms (Liste_Menu * /*debut*/); +extern Liste_Menu *ajoute_menu(Liste_Menu * /*base*/, char * /*nom*/, + void * /*lobjet*/); +extern int Bind_command_menu(char *, int, char *); + +/* rfc2047.c */ +extern void rfc2047_encode_string (char *, unsigned char *, size_t); +extern void rfc2047_decode (char *, const char *, size_t); + +/* flrn_help.c */ +extern void Aide (void); + +/* flrn_pager.c */ +extern int Page_message (int /*num_elem*/, int /*short_exit*/, int /*key*/, + int /*act_row*/, int /*row_deb*/, char * /*exit_chars*/, char *); +extern int Bind_command_pager(char *, int, char *); +extern void init_Flcmd_pager_rev(void); + + +/* flrn_regexp.c */ +extern char * reg_string (char * /*pattern*/, int /*flag*/); + +/* flrn_filter.c */ +extern int check_article(Article_List *, flrn_filter *, int); +extern flrn_filter *new_filter(void); +extern int parse_filter(char *, flrn_filter *); +extern int parse_filter_flags(char *, flrn_filter *); +extern int parse_filter_action(char *, flrn_filter *); +extern void free_filter(flrn_filter *); +extern int parse_kill_file(FILE *); +extern void apply_kill(int); + + + +/* flrn_color.c */ +extern int parse_option_color(int /*func*/, char * /*line*/); +extern void Init_couleurs(void); +extern int Aff_color_line(int /*to_print*/, unsigned short * /*format_line*/, + int * /*format_len*/, int /*field*/, char * /*line*/, int /*len*/, + int /*bol*/, int /*def_color*/); + +/* Fonctions liées avant à slang... Dans flrn_slang.c */ +extern void Screen_suspend(void); +extern void Screen_resume(void); +extern void Screen_get_size(void); +extern int Screen_init_smg(void); +extern void Reset_screen(void); +extern void Get_terminfo(void); +extern void Screen_write_char(char /*c*/); +extern void Screen_write_string(char * /*s*/); +extern void Screen_write_nstring(char * /*s*/, int /*n*/); +extern void Cursor_gotorc(int /*r*/, int /*c*/); +extern void Screen_erase_eol(void); +extern void Screen_erase_eos(void); +extern void Screen_refresh(void); +extern void Screen_touch_lines (int /*d*/, unsigned int /*n*/); +extern void Set_Use_Ansi_Colors(int /*n*/); +extern int Get_Use_Ansi_Colors(void); +extern void Color_set(int /*n*/, char * /*s1*/, char * /*s2*/, char * /*s3*/); +extern void Color_add_attribute(int /*n*/, FL_Char_Type /*a*/); +extern void Mono_set (int /*n*/, char * /*s*/, FL_Char_Type /*a*/); +extern void Screen_set_color(int /*n*/); +extern int Put_tty_single_input (int /*a*/, int /*b*/, int /*c*/); +extern void React_suspend_char (int /*a*/); +extern int init_clavier(void); +extern void Set_Ignore_User_Abort (int /*n*/); +extern int Get_Ignore_User_Abort (void); +extern int Set_Abort_Signal(void (* /*a*/)(int)); +extern int Keyboard_getkey(void); +extern void Reset_keyboard(void); +extern void free_text_scroll(void); +extern void Init_Scroll_window(int /*num*/, int /*beg*/, int /*nrw*/); +extern File_Line_Type *Ajoute_line(char * /*buf*/); +extern File_Line_Type *Change_line(int, char * /*buf*/); +extern File_Line_Type *Ajoute_form_Ligne(char * /*buf*/, int /*field*/); +extern File_Line_Type *Ajoute_color_Line(unsigned short * /*buf*/, int /*len*/); +extern char *Read_Line(char * /*out*/, File_Line_Type * /*line*/); +extern void Retire_line(File_Line_Type * /*line*/); +extern int Do_Scroll_Window(int /*n*/, int /*ob_update*/); +extern int Number_current_line_scroll(void); +extern void Screen_write_color_chars(unsigned short * /*s*/,unsigned int /*n*/); +extern int parse_key_name(char *); + +#endif diff --git a/src/flrn.h b/src/flrn.h new file mode 100644 index 0000000..5e9e0f5 --- /dev/null +++ b/src/flrn.h @@ -0,0 +1,16 @@ +/* flrn v 0.3 */ +/* flrn.h 22/06/98 */ +/* */ +/* Inclusion des fichiers de headers principaux... */ + + +#ifndef FLRN_FLRN_H +#define FLRN_FLRN_H + +#include "config.h" /* Defines de configure */ +#include "flrn_config.h" /* Defines autres */ +#include "compatibility.h" /* La compatibilité */ +#include "flrn_glob.h" /* Variables externes */ +#include "extern.h" /* Fonctions externes */ + +#endif diff --git a/src/flrn_color.c b/src/flrn_color.c new file mode 100644 index 0000000..a8ca0e3 --- /dev/null +++ b/src/flrn_color.c @@ -0,0 +1,420 @@ +/* flrn, essai de syntax highlighting pas trop bugué */ + +#include +#include +#include +#include +#include + +#define DEF_FILD_NAMES + +#include "flrn.h" +#include "flrn_slang.h" +#include "options.h" + +/* I want to use this code and the old one together */ +#define START_COLOR_NUM 10 +#if START_COLOR_NUM <= NROF_FIELDS +#error START_COLOR_NUM too small +#endif + + +/* structure pour stoquer les couleurs */ +/* attributs sert pour la couleur */ +struct Obj_color_struct { + char fg[15]; + char bg[15]; + FL_Char_Type attributs; + FL_Char_Type attributs_mono; +}; + +/* les couleurs par défaut des champs */ +struct Obj_color_struct Colors[NROF_FIELDS] = + { {"default","default",0,0}, /* Normal */ + {"green","default",0,0}, /* Header */ + {"default","default",FL_REV_MASK | FL_BOLD_MASK, + FL_REV_MASK | FL_BOLD_MASK},/* Status */ + {"brightred", "default",0,0}, /* Error */ + {"magenta","default",0,0}, /* Quoted */ + {"default","default",FL_BOLD_MASK, + FL_BOLD_MASK}, /* Aff_fin */ + {"blue", "default",0,0}, /* signature */ + {"default","default",0,0}, /* File */ + {"default","default",0,0}, /* summary */ + }; + + + +/* le délimiteur pour les options */ +static char *delim = "=: \t\n"; + +/* une limite sur le numéro de () dans les regexp */ +#define REG_MAX_SUB 10 + +/* la structure pour un objet a highlighter */ +struct Highlight { + regex_t regexp; + int flags; + struct Obj_color_struct colors; /* la couleur à utiliser */ + int pat_num; /* le numéro du pattern de la regexp a colorier */ + int col_num; /* le numéro de la couleur */ + int field_mask; /* les valeurs de field possibles */ + struct Highlight *next; +} *highlight_first=NULL, *highlight_last=NULL; + +/* les flags correspondant*/ +#define HIGH_FLAGS_EXCLUDE 1 +/* avec line, ne rien matcher d'autre sur la ligne */ +#define HIGH_FLAGS_LINE 2 +/* highlighter toute la ligne */ +#define HIGH_FLAGS_CASE 4 +/* etre case sensitive */ +#define NUM_FLAGS 3 +static char *flags_names[]={"exclude","line","case"}; + + +/* vire tout, libère la mémoire */ +void free_highlights() { + struct Highlight *current=highlight_first; + while(current) { + regfree(¤t->regexp); + highlight_first=current; + current=current->next; + free(highlight_first); + } + highlight_first=highlight_last=NULL; +} + +static int parse_std(char *buf, int field) { + int i; + if (strcasecmp(buf,"std")==0) + return field; + if (strncasecmp(buf,"std-",4)==0) + for (i=0;ifield_mask |= 1<field_mask == 0) { new_pat->field_mask = ~0;} + + buf = strtok(NULL,delim); + if (func == 1) { + if (buf == NULL) { free(new_pat); return -1;} + /* le deuxième champ, ce sont les flags */ + for (i=0;iflags |= 1<pat_num = strtol(buf2,NULL,0); + if (new_pat->pat_num >= REG_MAX_SUB) { free(new_pat); return -3; } + } + buf = strtok(NULL,delim); + } + if (func !=3) { + if (buf == NULL) { free(new_pat); return -1;} + /* le 2/3eme champ : fg */ + strncpy(new_pat->colors.fg, buf, 14); + buf = strtok(NULL,delim); + if (buf == NULL) { free(new_pat); return -1;} + /* le 3/4eme champ : bg */ + strncpy(new_pat->colors.bg, buf, 14); + buf = strtok(NULL,delim); + if ((i=parse_std(new_pat->colors.fg,field)) >=0 ) + strncpy(new_pat->colors.fg,Colors[i].fg,14); + if ((i=parse_std(new_pat->colors.bg,field)) >=0 ) + strncpy(new_pat->colors.bg,Colors[i].bg,14); + } + /* le 2/4/5eme champ : attr */ + while(buf) { + buf2=strchr(buf,','); + if (buf2) {*buf2=0; buf2++;} + if ((i=parse_std(buf,field)) >=0 ) { + new_pat->colors.attributs|=Colors[i].attributs; + new_pat->colors.attributs_mono|=Colors[i].attributs_mono; + } + else + if (strcasecmp(buf,"bold")==0) { + new_pat->colors.attributs|=FL_BOLD_MASK; + new_pat->colors.attributs_mono|=FL_BOLD_MASK; + } + else + if (strcasecmp(buf,"blink")==0) { + new_pat->colors.attributs|=FL_BLINK_MASK; + new_pat->colors.attributs_mono|=FL_BLINK_MASK; + } + else + if (strcasecmp(buf,"underline")==0) { + new_pat->colors.attributs|=FL_ULINE_MASK; + new_pat->colors.attributs_mono|=FL_ULINE_MASK; + } + else + if (strcasecmp(buf,"reverse")==0) { + new_pat->colors.attributs|=FL_REV_MASK; + new_pat->colors.attributs_mono|=FL_REV_MASK; + } + else + if (strcasecmp(buf,"-") && strcasecmp(buf,"normal")) + return -5; + buf=buf2; + } + + if (func ==1 ) { + buf = strtok(NULL,"\n"); + if (buf == NULL) { free(new_pat); return -1;} + buf += strspn(buf,delim); + if (*buf == '"') { + char *buf2; + buf ++; + buf2 = rindex(buf,'"'); + if(buf2) *buf2='\0'; + } + if (*buf=='\0') { free(new_pat); return -4;} + + /* le 6eme champ : regexp */ + res = regcomp(&new_pat->regexp,buf,REG_EXTENDED| + ((new_pat->flags&HIGH_FLAGS_LINE)?REG_NOSUB:0) | + ((new_pat->flags&HIGH_FLAGS_CASE)?0:REG_ICASE)); + if (res !=0) { + free(new_pat); return -4; + } + /* + new_pat->colors.attributs_mono=new_pat->colors.attributs; + */ /* Je veux pouvoir avoir des trucs différents grace a std-... */ + if (highlight_first) { + highlight_last->next=new_pat; + highlight_last=new_pat; + } else { + highlight_first = highlight_last=new_pat; + } + } else { + for (i=0;ifield_mask & (1<field_mask & (1<colors.fg,14); + strncpy(Colors[i].bg,new_pat->colors.bg,14); + Colors[i].attributs= new_pat->colors.attributs; + } else { + Colors[i].attributs_mono= new_pat->colors.attributs_mono; + } + } + } + free(new_pat); + } + return 0; +} + +void new_init_color() { + int field=START_COLOR_NUM; + struct Highlight *current=highlight_first; + + if (Options.color==0) Set_Use_Ansi_Colors(0); + else if (Options.color==1) Set_Use_Ansi_Colors(1); + while(current) { + if (Get_Use_Ansi_Colors()) { + Color_set(field, NULL, current->colors.fg, current->colors.bg); + Color_add_attribute(field, current->colors.attributs); + } + else + Mono_set(field, NULL, current->colors.attributs_mono); + current->col_num = field; + current=current->next; field++; + } +} + +/* Initialise les couleurs */ +void Init_couleurs() { + int field; + + new_init_color(); /* on initialise les nouvelles couleurs */ + if (Options.color==0) Set_Use_Ansi_Colors(0); + else if (Options.color==1) Set_Use_Ansi_Colors(1); + for (field=0; field 0) && current) { + /* est-ce que la regexp match et field est bon ? */ + if ((current->field_mask & (1<regexp,line,REG_MAX_SUB,pmatch,bol?0:REG_NOTBOL)==0)) + { + if (current->flags & HIGH_FLAGS_LINE) { + /* on garde que le premier match ligne */ + if ((!matched_line) && (bol)) { + color_line=current->col_num; + excl_line = (current->flags & HIGH_FLAGS_EXCLUDE)?1:0; + matched_line=1; + } + } else { + /* on garde un autre match si il est entièrement avant */ + if ((pmatch[current->pat_num].rm_so != -1) && + (((color==-1) && (pmatch[current->pat_num].rm_so <= prefix_len)) + || (pmatch[current->pat_num].rm_eo <= prefix_len))) { + if(color != -1) { + high[high_num*3]=color; + high[high_num*3+1]=prefix_len; + high[high_num*3+2]=match_len; + high_num++; + } + color=current->col_num; + prefix_len= pmatch[current->pat_num].rm_so; + match_len = ((pmatch[current->pat_num].rm_eo pat_num].rm_eo:len) - prefix_len; + } + } + } + current=current->next; + } + /* s'il y avait le flag EXCLUDE sur la ligne, on ignore les autre matchs */ + if (excl_line) prefix_len = len; + /* il faut afficher */ + if (to_print) { + Screen_set_color(color_line); + Screen_write_nstring(line,prefix_len); + if (prefix_len < len) { + if (color != -1); + Screen_set_color(color); + Screen_write_nstring(line+prefix_len,match_len); + } + } + /* on stoque la ligne */ + if (format_len) *format_len=0; + if (format_line) { + int i; + for (i=0;i commande */ +extern int Flcmd_rev[MAX_FL_KEY]; + +#include "flrn_inter.h" + +#ifdef IN_FLRN_INTER_C + +#include "flrn_slang.h" + +Flcmd Flcmds[NB_FLCMD] = { + { "prec", 'p' , '-', 2, &do_deplace }, +#define FLCMD_PREC 0 + { "suiv", '\r', '\n', 2, &do_deplace }, +#define FLCMD_SUIV 1 + { "art", 'v', 0, 6, &do_deplace }, +#define FLCMD_VIEW 2 + { "goto", 'g', 0, 15, &do_goto }, +#define FLCMD_GOTO 3 + { "GOTO", 'G', 0, 15, &do_goto }, +#define FLCMD_GGTO 4 + { "unsu", 'u', 0, 5|CMD_NEED_GROUP, &do_unsubscribe }, +#define FLCMD_UNSU 5 + { "abon", 'a', 0, 5|CMD_NEED_GROUP, &do_abonne }, +#define FLCMD_ABON 6 + { "omet", 'o', 0, 2|CMD_NEED_GROUP, &do_omet }, +#define FLCMD_OMET 7 + { "OMET", 'O', 0, 2|CMD_NEED_GROUP, &do_omet }, +#define FLCMD_GOMT 8 + { "zap" , 'z', 0 ,13|CMD_NEED_GROUP, &do_zap_group }, +#define FLCMD_ZAP 9 + { "help" , 'h', fl_key_nm_help ,1, &do_help }, +#define FLCMD_HELP 10 + { "quit" , 'q', 0 ,0, &do_quit }, +#define FLCMD_QUIT 11 + { "QUIT" , 'Q', 0 ,0, &do_quit }, +#define FLCMD_GQUT 12 + { "KILL_THR" , 'J', 0 ,2|CMD_NEED_GROUP, &do_kill }, +#define FLCMD_GKIL 13 + { "KILL" , 'K', 0 ,2|CMD_NEED_GROUP, &do_kill }, +#define FLCMD_KILL 14 + { "kill" , 'k', 0 ,2|CMD_NEED_GROUP, &do_kill }, +#define FLCMD_PKIL 15 + { "summary" , 'r', 0 ,6|CMD_NEED_GROUP, &do_summary }, +#define FLCMD_SUMM 16 + { "post", 'm', 0, 1|CMD_NEED_GROUP, &do_post }, +#define FLCMD_POST 17 + { "repond", 'R', 0, 3|CMD_NEED_GROUP, &do_post }, +#define FLCMD_ANSW 18 + { "view", 'V', 0, 2|CMD_NEED_GROUP, &do_launch_pager }, +#define FLCMD_PAGE 19 + { "save", 's', 0, 15|CMD_NEED_GROUP, &do_save }, +#define FLCMD_SAVE 20 + { "SAVE", 'S', 0, 15|CMD_NEED_GROUP, &do_save }, +#define FLCMD_GSAV 21 + { "list", 'l', 0, 13, &do_list }, +#define FLCMD_LIST 22 + { "LIST", 'L', 0, 13, &do_list }, +#define FLCMD_GLIS 23 + { "summ-fil", 't', 0, 2|CMD_NEED_GROUP, &do_summary }, +#define FLCMD_THRE 24 + { "summ-thr", 'T', 0, 2|CMD_NEED_GROUP, &do_summary }, +#define FLCMD_GTHR 25 + { "option", 0, fl_key_nm_opt, 1, &do_opt }, +#define FLCMD_OPT 26 + { "up", FL_KEY_UP, 0, 2, &do_deplace }, +#define FLCMD_UP 27 + { "down", FL_KEY_DOWN, 0, 2, &do_deplace }, +#define FLCMD_DOWN 28 + { "left", FL_KEY_LEFT, 0, 2, &do_deplace }, +#define FLCMD_LEFT 29 + { "right", FL_KEY_RIGHT, 0, 2, &do_deplace }, +#define FLCMD_RIGHT 30 + { "next", ' ', 0, 2, &do_deplace }, +#define FLCMD_SPACE 31 + { "nxt-thr", 'N', 0, 2|CMD_NEED_GROUP, &do_neth }, +#define FLCMD_NETH 32 + { "swap-grp", 0, 0, 15|CMD_NEED_GROUP, &do_swap_grp }, +#define FLCMD_SWAP_GRP 33 + { "prem-grp", 0, 0, 13|CMD_NEED_GROUP, &do_prem_grp }, +#define FLCMD_PREM_GRP 34 + { "pipe", '|', 0, 15|CMD_NEED_GROUP, &do_pipe }, +#define FLCMD_PIPE 35 + { "PIPE", 0, 0, 15|CMD_NEED_GROUP, &do_pipe }, +#define FLCMD_GPIPE 36 + { "filter", '%', 0, 15|CMD_NEED_GROUP, &do_pipe }, +#define FLCMD_FILTER 37 + { "FILTER", 0, 0, 15|CMD_NEED_GROUP, &do_pipe }, +#define FLCMD_GFILTER 38 + { "shell", 0, 0, 13, &do_pipe }, +#define FLCMD_SHELL 39 + { "shin", '!', 0, 13, &do_pipe }, +#define FLCMD_SHELLIN 40 + { "config", 0, 0, 1,&do_opt_menu }, +#define FLCMD_OPTMENU 41 + { "mail-ans", 0, 0, 3|CMD_NEED_GROUP,&do_post }, +#define FLCMD_MAIL 42 + { "tag", '"', 0, 2|CMD_NEED_GROUP,&do_tag }, +#define FLCMD_TAG 43 + { "gotag", '\'', 0, 2,&do_goto_tag }, +#define FLCMD_GOTO_TAG 44 + { "cancel", 'e', 0, 2|CMD_NEED_GROUP, &do_cancel }, +#define FLCMD_CANCEL 45 + { "hist-prev", 'B', 0, 2, &do_back }, +#define FLCMD_HPREV 46 + { "hist-suiv", 'F', 0, 2, &do_next }, +#define FLCMD_HSUIV 47 + { "hist-menu", 'H', 0, 2, &do_hist_menu }, +#define FLCMD_HMENU 48 + { "supersedes", 0, 0, 3|CMD_NEED_GROUP, &do_post }, +#define FLCMD_SUPERSEDES 49 + { "sum-search" , 0, 0 ,14|CMD_NEED_GROUP, &do_summary }, +#define FLCMD_SUMM_SEARCH 50 + { "menu-summary" , 0, 0 ,6|CMD_NEED_GROUP, &do_summary }, +#define FLCMD_MENUSUMM 51 + { "menu-fil" , 0, 0 ,6|CMD_NEED_GROUP, &do_summary }, +#define FLCMD_MENUTHRE 52 + { "menu-thr" , 0, 0 ,6|CMD_NEED_GROUP, &do_summary }, +#define FLCMD_MENUGTHR 53 + { "menu-search" , '/', 0 ,14|CMD_NEED_GROUP, &do_summary }, +#define FLCMD_MENUSUMMS 54 +}; + +#define CMD_DEF_PLUS (sizeof(Cmd_Def_Plus)/sizeof(Cmd_Def_Plus[0])) +struct cmd_predef { + int key; + int cmd; + char *args; +} Cmd_Def_Plus[] = { + { 'b', FLCMD_LEFT, NULL }, + { 'f', FLCMD_RIGHT, NULL }, + { 'n', FLCMD_SUIV, NULL }, + { 'P', FLCMD_HPREV, NULL }, + { '[', FLCMD_LEFT, NULL }, + { ']', FLCMD_RIGHT, NULL }, + { '(', FLCMD_UP, NULL }, + { ')', FLCMD_DOWN, NULL }, + { 2 , FLCMD_PIPE, "urlview" }, +}; + +#else + +extern Flcmd Flcmds[NB_FLCMD]; +/* On fait les defines suivant pour les menus */ +#define FLCMD_PREC 0 +#define FLCMD_SUIV 1 +#define FLCMD_QUIT 11 +#define FLCMD_UP 27 +#define FLCMD_DOWN 28 + +#endif + +#endif diff --git a/src/flrn_files.c b/src/flrn_files.c new file mode 100644 index 0000000..4dff1ba --- /dev/null +++ b/src/flrn_files.c @@ -0,0 +1,212 @@ +/* flrn v 0.1 */ +/* flrn_files.c 22/06/98 */ +/* */ +/* Des routines de gestion des fichiers (.flrn(rc?) et .flnewsrc) */ +/* Ce fichier doit se limiter à la gestion de fichiers. */ +/* */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "flrn.h" +#include "group.h" +#include "art_group.h" +#include "options.h" + +/* La socket avec le serveur pour la fermer apres un fork */ +extern int tcp_fd; + +/* Ouverture du fichier de config. Renvoie un FILE dessus. Dans le cas */ +/* ou ce fichier n'existe pas, le crée et copie dedans un config */ +/* minimale (à condition de trouver le home directory). */ +/* file, si non NULL, donne le nom du fichier à partir de HOME */ +/* mode a r pour le lire, w+ pour le creer */ +/* flag =1, créer un fichier vide, =2 créer un fichier avec du pipo, + * flag=0 ne pas créer de fichier */ + +FILE *open_flrnfile (char *file,char *mode, int flag) +{ + char *home, name[MAX_PATH_LEN]; + int home_found = 1; + FILE *config_file; + + if (NULL == (home = getenv ("FLRNHOME"))) + home = getenv ("HOME"); + + if (home == NULL) + { + strcpy(name,"./"); + home_found = 0; + } + else + { + strncpy(name, home, MAX_PATH_LEN-1); + name[MAX_PATH_LEN-2]='\0'; /* oui, je sais, precaution ridicule */ + strcat(name, "/"); + } + if (file) + strncat(name,file,MAX_PATH_LEN-strlen(name)-1); + else + strncat(name,DEFAULT_CONFIG_FILE,MAX_PATH_LEN-strlen(name)-1); + + name[MAX_PATH_LEN-1]='\0'; /* Décidément, j'insiste */ + + if (debug) fprintf(stderr,"On essaie d'ouvrir %s\n",name); + if (!(config_file=fopen(name,mode))) { + if (flag) + fprintf(stderr,"Le fichier %s ne peut être ouvert\n",name); + if (home_found && flag) { + if (debug) fprintf(stderr,"On va essayer de le créer\n"); + config_file=fopen(name,"w+"); + if (config_file == NULL) { + perror("fopen"); + sleep(1); + return NULL; + } + if (flag == 2) { + fprintf(config_file,"# Fichier de configuration de flrn\n"); + fprintf(config_file,"# Créé automatiquement\n"); + } + fseek(config_file,0L,SEEK_CUR); /* Pour l'effet de synchronisation */ + } else { + return NULL; + } + } + return config_file; +} + +/* Renommage du .flnewsrc.new lors de la sauvegarde */ +void rename_flnewsfile (char *old_link,char *new_link) +{ + char *home, name1[MAX_PATH_LEN], name2[MAX_PATH_LEN]; + int home_found = 1, res; + + if (NULL == (home = getenv ("FLRNHOME"))) + home = getenv ("HOME"); + + if (home == NULL) + { + if (debug) fprintf(stderr,"HOME semble non défini... On essaie un chemin local\n"); + strcpy(name1,"./"); + home_found = 0; + } + else + { + strncpy(name1, home, MAX_PATH_LEN-1); + name1[MAX_PATH_LEN-2]='\0'; /* oui, je sais, precaution ridicule */ + strcat(name1, "/"); + } + strcpy(name2, name1); + if (old_link) + strncat(name1,old_link,MAX_PATH_LEN-strlen(name1)); + else + strncat(name1,DEFAULT_FLNEWS_FILE,MAX_PATH_LEN-strlen(name1)); + if (new_link) + strncat(name2,new_link,MAX_PATH_LEN-strlen(name2)); + else + strncat(name2,DEFAULT_FLNEWS_FILE,MAX_PATH_LEN-strlen(name2)); + + name1[MAX_PATH_LEN-1]=name2[MAX_PATH_LEN-1]='\0'; + res=rename(name1, name2); + if (res<0) fprintf(stderr, "Echec dans le renommage du .flnewsrc.new.\n"); +} + +/* Ouverture du fichier temporaire de post. On renvoie NULL si echec */ +/* note : on ne teste pas la longueur de file, qui ne doit JAMAIS etre */ +/* defini par l'utilisateur */ +FILE *open_postfile (char *file,char *mode) { + char name[MAX_PATH_LEN]; + char *home; + FILE *tmppostfile; + + if (NULL == (home = getenv ("FLRNHOME"))) + home = getenv ("HOME"); + if (home==NULL) return NULL; + strncpy(name,home,MAX_PATH_LEN-2-strlen(TMP_POST_FILE)); + strcat(name,"/"); if (file) strcat(name, file); else + strcat(name,TMP_POST_FILE); + tmppostfile=fopen(name,mode); + return tmppostfile; +} + +int stat_postfile(char *file,struct stat *st) { + char name[MAX_PATH_LEN]; + char *home; + if (NULL == (home = getenv ("FLRNHOME"))) + home = getenv ("HOME"); + if (home==NULL) return -1; + strncpy(name,home,MAX_PATH_LEN-2-strlen(TMP_POST_FILE)); + strcat(name,"/"); if (file) strcat(name, file); else + strcat(name,TMP_POST_FILE); + return stat(name,st); +} + +/* Ouverture d'un fichier d'aide en mode read_only. Pas a se poser de */ +/* questions. */ +FILE *open_flhelpfile (char ext) +{ + char name[MAX_PATH_LEN]; + + sprintf(name,"%s%s%c", DEFAULT_HELP_DIRECTORY, DEFAULT_HELP_FILES, ext); + return fopen(name,"r"); +} + + +/* Copie le contenu d'un article dans un fichier */ +void Copy_article (FILE *dest, Article_List *article, int copie_head, char *avant) { + int res, code; + char *num, *buf; + int flag=2; /* flag & 1 : debut de ligne */ + /* & 2 : fin de ligne */ + + num=safe_malloc(12*sizeof(char)); + sprintf(num, "%d", article->numero); + res=write_command(copie_head ? CMD_ARTICLE : CMD_BODY, 1, &num); + free(num); + if (res<0) return; + code=return_code(); + if ((code<0) || (code>300)) return; + do { + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + buf=tcp_line_read; + if (res<1) return; + if (res==1) { flag=2; fprintf(dest, "\n"); continue; } /* On a lu '\n' */ + if (flag & 2) { + if (strncmp(tcp_line_read, ".\r\n", 3)==0) return; + if (*buf=='.') buf++; + flag=1; + } else flag=0; + flag|=2*(tcp_line_read[res-2]=='\r'); + if ((flag & 1) && avant) { + if ((*avant=='>') && Options.smart_quote && (*buf=='>')) + fprintf(dest,"%c",'>'); else + fprintf(dest, "%s", avant); + } + if (flag & 2) tcp_line_read[res-2]='\0'; + fprintf(dest, "%s", buf); + if (flag & 2) fprintf(dest, "\n"); + } while (1); +} + +/* TODO à écrire correctement */ +int init_kill_file() { + FILE *blah = open_flrnfile(".flrnkill","r",0); + if (blah) { + fprintf (stderr,"Kill-file trouvé... Attention, le code est incomplet.\n"); + sleep(1); + if (!parse_kill_file(blah)){ + fprintf(stderr,"Kill-file invalide, ignoré !\n"); + sleep(1); + } + fclose(blah); + } + return 0; +} diff --git a/src/flrn_filter.c b/src/flrn_filter.c new file mode 100644 index 0000000..be50021 --- /dev/null +++ b/src/flrn_filter.c @@ -0,0 +1,290 @@ +#include "flrn.h" +#include "flrn_filter.h" +#include "art_group.h" + +/* on met ici le contenu du kill_file */ +static flrn_kill *flrn_kill_deb; + +/* regarde si condition est vérifié */ +/* renvoie -1, s'il manque des headers + * 0 si ça matche + * 1 si ça ne matche pas + * flag=1 dit d'appeler cree_header au besoin */ +int check_article(Article_List *article, flrn_filter *filtre, int flag) { + flrn_condition *regexp=filtre->condition; + + /* en premier, on regarde si les flags de l'article sont bons */ + if ((article->flag & filtre->cond_mask) != filtre->cond_res) + return 1; + + if (article->headers==NULL) { + int first, last; + first=last=article->numero; + if (article->prev && article->prev->headers==NULL) { + first -=50; + if (first <1) first=1; + } + if (article->next && article->next->headers==NULL) { + last +=50; + } + if (overview_usable) if(cree_liste_xover(first, last, &Article_deb)) + cree_liens(); + } + while(regexp) { + if ((article->headers == NULL) || + (article->headers->k_headers_checked[regexp->header_num]==0)) { + if (flag) cree_header(article,0,0); + else return -1; + } + if(regexec(regexp->condition, + article->headers->k_headers[regexp->header_num], + 0,NULL,0)!=0) + return 1; + regexp=regexp->next; + } + return 0; +} + +void filter_do_action(flrn_filter *filt) { + flrn_action *act; + if (filt->flag ==0) { + Article_courant->flag |= filt->action.flag; + return; + } + act=filt->action.action; + if (!act) return; + while(act) { + call_func(act->command_num,act->arg); + act=act->next; + } +} + +flrn_filter *new_filter() { + return (flrn_filter *)safe_calloc(1, sizeof(flrn_filter)); +} + +int parse_filter_flags(char * str, flrn_filter *filt) { + if (strcmp(str,"unread") ==0 ) { filt->cond_mask |= FLAG_READ; + filt->cond_res &= ~FLAG_READ; + } else + if (strcmp(str,"read") ==0 ) { + filt->cond_res |= FLAG_READ; + filt->cond_mask |= FLAG_READ; + } else + if (strcmp(str,"all") ==0 ) + filt->cond_res = filt->cond_mask = 0; + else return -1; + return 0; +} + +int parse_filter_action(char * str, flrn_filter *filt) { + flrn_action *act,*a2; + char *buf=str, *buf2; + filt->flag=1; + buf2=strchr(buf,' '); + if (buf2) *(buf2++)='\0'; + act=safe_calloc(1,sizeof(flrn_action)); + act->command_num=fonction_to_number(str); + if (act->command_num == -1) { + free(act); return -2;} + if (buf2) {act->arg=safe_strdup(buf2);} + if (filt->action.action==NULL) filt->action.action=act; + else { + a2=filt->action.action; + while(a2->next) a2=a2->next; + a2->next=act; + } + return 0; +} + +int parse_filter_toggle_flag(char * str, flrn_filter *filt) { + if (filt->flag) return -1; + if (strcmp(str,"read") ==0 ) + filt->action.flag = FLAG_READ; + else + if (strcmp(str,"action") ==0 ) /* pour la mise au point */ + filt->action.flag = FLAG_ACTION; + else return -1; + filt->cond_mask |= filt->action.flag; + filt->cond_res &= ~filt->action.flag; + return 0; +} + +int parse_filter(char * str, flrn_filter *start) { + flrn_condition *cond, *c2; + char *buf; + int i; + cond=safe_calloc(1,sizeof(flrn_condition)); + for (i=0;iheader_num=i; break; + } + } + if (i == NB_KNOWN_HEADERS) { free(cond) ; return -1; } + buf = str + Headers[cond->header_num].header_len; + while(*buf==' ') buf++; + cond->condition = safe_malloc(sizeof(regex_t)); + if (regcomp(cond->condition,buf,REG_EXTENDED|REG_NOSUB|REG_ICASE)) + {free(cond) ; return -2;} + if (start->condition==NULL) + start->condition=cond; + else { + c2=start->condition; + while(c2->next) c2=c2->next; + c2->next=cond; + } + return 0; +} + +static void free_condition( flrn_condition *cond) { + if (cond->condition) + regfree(cond->condition); + cond->condition=NULL; +} + +static void free_action( flrn_action *act) { + if (act->arg) free(act->arg); + free(act); +} + +void free_filter(flrn_filter *filt) { + flrn_condition *cond,*cond2; + flrn_action *act,*act2; + cond=filt->condition; + while(cond) { + cond2=cond->next; + free_condition(cond); + cond=cond2; + } + if (filt->flag) { + act=filt->action.action; + while(act) { + act2=act->next; + free_action(act); + act=act2; + } + } + free(filt); +} + +void free_kill(flrn_kill *kill) { + flrn_kill *k2=kill; + while(kill) { + k2=kill->next; + if (kill->filter) + free_filter(kill->filter); + if (kill->newsgroup_cond) { + regfree(kill->newsgroup_cond); + free(kill->newsgroup_cond); + } + free(kill); + kill=k2; + } +} + +flrn_filter * parse_kill_block(FILE *fi) { + char buf1[MAX_BUF_SIZE]; + char *buf2; + flrn_filter *filt = NULL; + int out=0; + + while (fgets(buf1,MAX_BUF_SIZE,fi)) { + if(buf1[0]=='\n') {return filt;} + buf2=strchr(buf1,'\n'); + if (buf2) *buf2=0; + else out=1; + if(filt==NULL) { + filt = new_filter(); + /* par défaut, on s'applique aux messages non lus */ + filt->cond_mask = FLAG_READ; + filt->cond_res = 0; + } + if (buf1[0]=='*') out=parse_filter(buf1+1,filt); + else if (buf1[0]=='F') out=parse_filter_flags(buf1+1,filt); + else if (buf1[0]=='C') out=parse_filter_action(buf1+1,filt); + else if (buf1[0]=='T') out=parse_filter_toggle_flag(buf1+1,filt); + else if (buf1[0]=='#') out=0; + else out=-1; + if(out) { free_filter(filt); return NULL; } + } + return filt; +} + +int parse_kill_file(FILE *fi) { + char buf[MAX_BUF_SIZE]; + char *buf2,*buf1=buf; + flrn_kill *kill, *k2=flrn_kill_deb; + int out=0; + + while(fgets(buf,MAX_BUF_SIZE,fi)) { + buf1=buf; + buf2=strchr(buf1,'\n'); + if (buf2) *buf2=0; + else out=1; + if (buf1[0]=='\n') continue; /* skip empty_lines */ + if (buf1[0]=='#') continue; /* skip comments */ + if (buf1[0]!=':') out=1; + else { + buf1++; + buf2=strrchr(buf1,':'); + if (!buf2) out=1; + else { + kill = safe_calloc(1,sizeof(flrn_kill)); + if (!flrn_kill_deb) flrn_kill_deb =kill; + else k2->next=kill; + k2=kill; + *(buf2++)='\0'; + /* buf2 pointe sur les flags */ + kill->newsgroup_cond = safe_malloc(sizeof(regex_t)); + if (regcomp(kill->newsgroup_cond,buf1,REG_EXTENDED|REG_NOSUB)) + out=1; + else { + kill->filter=parse_kill_block(fi); + if (!kill->filter) out=1; + } + } + } + if (out) { + if (flrn_kill_deb) + free_kill(flrn_kill_deb); + flrn_kill_deb=NULL; + return 0; + } + } + return 1; +} + +static int check_group(flrn_kill *kill) { + if (kill->Article_deb_key && + (Newsgroup_courant->article_deb_key == kill->Article_deb_key)) + return 1; + if (regexec(kill->newsgroup_cond, + Newsgroup_courant->name,0,NULL,0)==0) { + kill->Article_deb_key=Newsgroup_courant->article_deb_key; + return 1; + } + return 0; +} + +/* flag = 1 -> appel cree_header */ +/* on s'applique a Article_courant. Il faut le FLAG_NEW */ +void apply_kill(int flag) { + flrn_kill *kill=flrn_kill_deb; + int res; + int all_bad=1; + + if (!(Article_courant->flag & FLAG_NEW)) + return; + + while (kill) { + if (check_group(kill)) { + if ((res=check_article(Article_courant,kill->filter,flag)==0)) { + Article_courant->flag &= ~FLAG_NEW; + filter_do_action(kill->filter); + } else if (res <0) all_bad=0; + } + kill=kill->next; + } + if (all_bad || flag) + Article_courant->flag &= ~FLAG_NEW; +} diff --git a/src/flrn_filter.h b/src/flrn_filter.h new file mode 100644 index 0000000..61ccaea --- /dev/null +++ b/src/flrn_filter.h @@ -0,0 +1,39 @@ +/* liste chainée de regexp à matcher sur les headers */ +#ifndef FLRN_FILTER_H +#define FLRN_FILTER_H + +typedef struct _flrn_condition { + int flags; + int header_num; + regex_t *condition; + struct _flrn_condition *next; +} flrn_condition; + +/* liste chainée d'actions */ +typedef struct _flrn_action { + long command_num; + char *arg; + struct _flrn_action *next; +} flrn_action; + +/* ben la totale */ +typedef struct _flrn_filter { + long cond_mask; /* masque sur les flag de l'article */ + long cond_res; + flrn_condition *condition; + int flag; /* est-ce flag ou action ? */ + union { + long flag; + flrn_action *action; + } action; +} flrn_filter; + +/* les kill-files */ +typedef struct _flrn_kill { + regex_t *newsgroup_cond; + long Article_deb_key; /* on reprend ce moyen rapide de validation */ + flrn_filter * filter; + struct _flrn_kill *next; +} flrn_kill; + +#endif diff --git a/src/flrn_format.c b/src/flrn_format.c new file mode 100644 index 0000000..6c61803 --- /dev/null +++ b/src/flrn_format.c @@ -0,0 +1,546 @@ +/* flrn v 0.3 */ +/* flrn_format.c 22/06/98 */ +/* */ +/* Formatage des headers... Sert pour le parsing de la date, les */ +/* attributions, etc etc... */ +/* */ + +#include +#include +#include +#include + +#include "flrn.h" +#include "art_group.h" +#include "options.h" + + +/* Parsing de la date */ +/* Pompé sur mutt... */ +const char *Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec", "ERR" }; + +static struct tz_t +{ + char *tzname; + unsigned char zhours; + unsigned char zminutes; + unsigned char zoccident; /* west of UTC? */ + unsigned char xxx; /* unused */ +} +TimeZones[] = +{ + { "sst", 11, 0, 1, 0 }, /* Samoa */ + { "pst", 8, 0, 1, 0 }, + { "mst", 7, 0, 1, 0 }, + { "pdt", 7, 0, 1, 0 }, + { "cst", 6, 0, 1, 0 }, + { "mdt", 6, 0, 1, 0 }, + { "cdt", 5, 0, 1, 0 }, + { "est", 5, 0, 1, 0 }, + { "ast", 4, 0, 1, 0 }, /* Atlantic */ + { "edt", 4, 0, 1, 0 }, + { "wgt", 3, 0, 1, 0 }, /* Western Greenland */ + { "wgst", 2, 0, 1, 0 }, /* Western Greenland DST */ + { "aat", 1, 0, 1, 0 }, /* Atlantic Africa Time */ + { "egt", 1, 0, 1, 0 }, /* Eastern Greenland */ + { "egst", 0, 0, 0, 0 }, /* Eastern Greenland DST */ + { "gmt", 0, 0, 0, 0 }, + { "utc", 0, 0, 0, 0 }, + { "wat", 0, 0, 0, 0 }, /* West Africa */ + { "wet", 0, 0, 0, 0 }, /* Western Europe */ + { "bst", 1, 0, 0, 0 }, /* British DST */ + { "cat", 1, 0, 0, 0 }, /* Central Africa */ + { "cet", 1, 0, 0, 0 }, /* Central Europe */ + { "met", 1, 0, 0, 0 }, /* this is now officially CET */ + { "west", 1, 0, 0, 0 }, /* Western Europe DST */ + { "cest", 2, 0, 0, 0 }, /* Central Europe DST */ + { "eet", 2, 0, 0, 0 }, /* Eastern Europe */ + { "ist", 2, 0, 0, 0 }, /* Israel */ + { "sat", 2, 0, 0, 0 }, /* South Africa */ + { "ast", 3, 0, 0, 0 }, /* Arabia */ + { "eat", 3, 0, 0, 0 }, /* East Africa */ + { "eest", 3, 0, 0, 0 }, /* Eastern Europe DST */ + { "idt", 3, 0, 0, 0 }, /* Israel DST */ + { "msk", 3, 0, 0, 0 }, /* Moscow */ + { "adt", 4, 0, 0, 0 }, /* Arabia DST */ + { "msd", 4, 0, 0, 0 }, /* Moscow DST */ + { "gst", 4, 0, 0, 0 }, /* Presian Gulf */ + { "smt", 4, 0, 0, 0 }, /* Seychelles */ + { "ist", 5, 30, 0, 0 }, /* India */ + { "ict", 7, 0, 0, 0 }, /* Indochina */ +/*{ "cst", 8, 0, 0, 0 },*/ /* China */ + { "hkt", 8, 0, 0, 0 }, /* Hong Kong */ +/*{ "sst", 8, 0, 0, 0 },*/ /* Singapore */ + { "wst", 8, 0, 0, 0 }, /* Western Australia */ + { "jst", 9, 0, 0, 0 }, /* Japan */ +/*{ "cst", 9, 30, 0, 0 },*/ /* Australian Central Standard Time */ + { "kst", 10, 0, 0, 0 }, /* Korea */ + { "nzst", 12, 0, 0, 0 }, /* New Zealand */ + { "nzdt", 13, 0, 0, 0 }, /* New Zealand DST */ + { NULL, 0, 0, 0, 0 } +}; + +static int mutt_check_month (const char *s) +{ + int i; + + for (i = 0; i < 12; i++) + if (strncasecmp (s, Months[i], 3) == 0) + return (i); + return (-1); /* error */ +} + +static const char *uncomment_timezone (char *buf, size_t buflen, const char *tz) +{ + char *p; + size_t len; + + if (*tz != '(') + return tz; /* no need to do anything */ + tz++; + while (isblank(*tz)) tz++; + if ((p = strpbrk (tz, " )")) == NULL) + return tz; + len = p - tz; + if (len > buflen - 1) + len = buflen - 1; + memcpy (buf, tz, len); + buf[len] = 0; + return buf; +} + +/* converts struct tm to time_t, but does not take the local timezone into + * account unless ``local'' is nonzero */ +time_t mutt_mktime (struct tm *t, int local) +{ + time_t g; + + static int AccumDaysPerMonth[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + /* Compute the number of days since January 1 in the same year */ + g = AccumDaysPerMonth [t->tm_mon % 12]; + + /* The leap years are 1972 and every 4. year until 2096, + * but this algoritm will fail after year 2099 */ + g += t->tm_mday; + if ((t->tm_year % 4) || t->tm_mon < 2) + g--; + t->tm_yday = g; + + /* Compute the number of days since January 1, 1970 */ + g += (t->tm_year - 70) * 365; + g += (t->tm_year - 69) / 4; + /* Compute the number of hours */ + g *= 24; + g += t->tm_hour; + + /* Compute the number of minutes */ + g *= 60; + g += t->tm_min; + + /* Compute the number of seconds */ + g *= 60; + g += t->tm_sec; + +/* if (local) + g += mutt_compute_tz (g, t); */ + + return (g); +} + +/* Cette fonction modifiant la chaine de caractère date, on recopie date d'abord + */ +time_t parse_date (char *s) +{ + char *t, *ns; + struct tm tm; + int hour, min, sec; + int i; + int tz_offset = 0; + int zhours = 0; + int zminutes = 0; + int zoccident = 0; + const char *ptz; + char tzstr[128]; + int count=0; + + /* kill the day of the week, if it exists. */ + ns=safe_strdup(s); + if ((t = strchr (ns, ','))) + t++; + else + t = ns; + while (isblank(*t)) t++; + memset (&tm, 0, sizeof(tm)); + while ((t = strtok (t, " \t")) != NULL) + { + switch (count) + { + case 0: /* day of the month */ + if (!isdigit (*t)) { + free(ns); + return (0); + } + tm.tm_mday = atoi (t); + if (tm.tm_mday > 31) { + free(ns); + return (0); + } + break; + + case 1: /* month of the year */ + if ((i = mutt_check_month (t)) < 0) { + free(ns); + return (0); + } + tm.tm_mon = i; + break; + + case 2: /* year */ + tm.tm_year = atoi (t); + if (tm.tm_year >= 1900) + tm.tm_year -= 1900; + break; + + case 3: /* time of day */ + if (sscanf (t, "%d:%d:%d", &hour, &min, &sec) == 3) + ; + else if (sscanf (t, "%d:%d", &hour, &min) == 2) + sec = 0; + else + { + if (debug) fprintf(stderr, "parse_date: beurk : %s\n",t); + free(ns); + return 0; + } + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + break; + + case 4: /* timezone */ + /* sometimes we see things like (MST) or (-0700) so attempt to + * compensate by uncommenting the string if non-RFC822 compliant + */ + ptz = uncomment_timezone (tzstr, sizeof (tzstr), t); + if (*ptz == '+' || *ptz == '-') + { + if (ptz[1] && ptz[2] && ptz[3] && ptz[4]) + { + zhours = (ptz[1] - '0') * 10 + (ptz[2] - '0'); + zminutes = (ptz[3] - '0') * 10 + (ptz[4] - '0'); + if (ptz[0] == '-') + zoccident = 1; + } + } + else + { + for (i = 0; TimeZones[i].tzname; i++) + if (!strcasecmp (TimeZones[i].tzname, ptz)) + { + zhours = TimeZones[i].zhours; + zminutes = TimeZones[i].zminutes; + zoccident = TimeZones[i].zoccident; + break; + } + /* ad hoc support for the European MET (now officially CET) TZ */ + if (strcasecmp (t, "MET") == 0) + { + if ((t = strtok (NULL, " \t")) != NULL) + { + if (!strcasecmp (t, "DST")) + zhours++; + } + } + } + tz_offset = zhours * 3600 + zminutes * 60; + if (!zoccident) + tz_offset = -tz_offset; + break; + } + count++; + t = 0; + } + free(ns); + if (count < 4) /* don't check for missing timezone */ + { + if (debug) fprintf(stderr, "Erreur de date...\n"); + return (0); + } + return (mutt_mktime (&tm,0) + tz_offset); +} + +/* fin de la partie prise sur mutt */ + + +/* Détermination du real name a partir d'une chaine type From */ +char *vrai_nom (char *nom) { + char *result, *buf1, *buf2; + + result=safe_malloc((strlen(nom)+1)*sizeof(char)); + memset(result, 0, strlen(nom)+1); + *result='\0'; + buf1=strchr(nom,'('); + if (buf1) { + buf1++; + buf2=strrchr(buf1, ')'); + if (buf2==NULL) buf2=index(buf1, '\0'); + strncpy(result, buf1, buf2-buf1); + } else { + buf1=strchr(nom, '<'); + if (buf1==NULL) { strcpy(result,nom); return result; } + strncpy(result, nom, buf1-nom); + buf2=strrchr(nom, '>'); + if (buf2!=NULL) strcat(result, buf2+1); + } + return result; +} + +/* Écriture de la date a partir d'une chaine de date */ +char *local_date (char *date) { + char *result; + char *mydate=date; + char mon[3]; + struct tm tim; + + result=safe_malloc(13*sizeof(char)); + while(*mydate && !isdigit(*mydate)) mydate++; + + if(sscanf(mydate,"%d %s %d %d:%d:%d", &(tim.tm_mday), mon, &(tim.tm_year), + &(tim.tm_hour), &(tim.tm_min), + &(tim.tm_sec)) >= 5) + { + sprintf(result, "%3s %2d %2d:%2d", mon, tim.tm_mday, tim.tm_hour, + tim.tm_min); + } else + strcpy(result,"???"); + return result; +} + + + +/* Fonctions pour estimer la taille d'une chaine a afficher (avec les tab) */ +int str_estime_len (char *la_chaine, int tmp_col, int tai_chaine) { + int i; + + if (tai_chaine==-1) tai_chaine=strlen(la_chaine); + for (i=0;iheaders==NULL) return; /* Beurk ! */ + copy_bout(NULL,NULL); + ptr_att=att; + while (ptr_att && (*ptr_att)) { + buf=strchr(ptr_att,'%'); + if (buf==NULL) { + copy_bout(tmp_file,ptr_att); + break; + } else { + *buf='\0'; + copy_bout(tmp_file,ptr_att); + ptr_att=(++buf); + switch (*buf) { + case '%' : copy_bout(tmp_file,"%"); + ptr_att++; + break; + case 'n' : { char *vrai_n; + vrai_n=vrai_nom(article->headers->k_headers + [FROM_HEADER]); + copy_bout(tmp_file,vrai_n); + free(vrai_n); + ptr_att++; + break; + } + case 'i' : { char *msgid=safe_strdup(article->msgid); + copy_bout(tmp_file,msgid); + ptr_att++; + free(msgid); + break; + } + case 'C' : { char *num=safe_malloc(sizeof(int)*3+2); + snprintf(num,sizeof(int)*3+1,"%d",article->numero); + copy_bout(tmp_file,num); + free(num); + ptr_att++; + break; + } + case 'g' : { char *str=safe_strdup(Newsgroup_courant->name); + copy_bout(tmp_file, (tmp=strrchr(str,'.'))?tmp+1: + str); + free(str); + ptr_att++; + break; + } + case 'G' : { char *str=safe_strdup(Newsgroup_courant->name); + copy_bout(tmp_file,str); + free(str); + ptr_att++; + break; + } + default : { char *str=safe_malloc(3); + sprintf(str,"%%%c",*buf); + if (debug) fprintf(stderr, "Mauvaise attribution : %s\n", + Options.attribution); + copy_bout(tmp_file,str); + free(str); + ptr_att++; + break; + } + } + } + } + copy_bout(tmp_file,NULL); + free(att); +} + +/* prépare une ligne de résumé */ +char * Prepare_summary_line(Article_List *article, char *previous_subject, + int level, char *out, int outlen) { + int deb_num, deb_nom, tai_nom, deb_sub, tai_sub, deb_dat, deb_rep; + char *buf, buf2[15]; + char *subject; + char *out_ptr=out; + + out[0]='\0'; + if ((article->headers==NULL) || + (article->headers->k_headers_checked[FROM_HEADER] == 0) || + (article->headers->k_headers_checked[SUBJECT_HEADER] == 0) || + ((article->headers->k_headers_checked[DATE_HEADER] == 0) && + Options.date_in_summary)) + cree_header(article,0,0); + if (article->headers==NULL) return NULL; + deb_num=0; deb_nom=7; + deb_rep=outlen-8; + if(Options.date_in_summary) + deb_dat=deb_rep-13; else deb_dat=deb_rep; + tai_nom=(deb_dat-deb_nom)/3; + deb_sub=deb_nom+tai_nom+2; + tai_sub=deb_dat-deb_sub-1; + if ((tai_nom<=0) || (tai_sub<=0)) return NULL; /* TROP PETIT */ + + sprintf(buf2,"%c%5d", (article->flag&FLAG_READ)?' ':'*',article->numero); + sprintf(out_ptr,"%-*.*s ",deb_nom,deb_nom,buf2); + out_ptr+=deb_nom; + buf=vrai_nom(article->headers->k_headers[FROM_HEADER]); + sprintf(out_ptr,"%-*.*s",tai_nom+2,tai_nom,buf); + out_ptr+=tai_nom+2; + free(buf); + subject=article->headers->k_headers[SUBJECT_HEADER]; + if (!strncasecmp(subject,"Re: ",4)) subject +=4; + if (previous_subject && !strncasecmp(previous_subject,"Re: ",4)) + previous_subject +=4; + if (article->headers->k_headers[SUBJECT_HEADER]) { + if(previous_subject && !strcmp(subject,previous_subject)) + { + if (level && (level < tai_sub-1)) { + sprintf(out_ptr,"%*s%-*s",level,"",tai_sub-level+1,"."); + } + } else + sprintf(out_ptr,"%-*.*s",tai_sub+1,tai_sub,subject); + } else sprintf(out_ptr,"%*s",tai_sub,""); + out_ptr += tai_sub+1; + if(Options.date_in_summary) { + if (article->headers->date_gmt) + buf=safe_strdup(ctime(&article->headers->date_gmt)+4); + else + buf=local_date(article->headers->k_headers[DATE_HEADER]); + sprintf(out_ptr,"%-*.*s",13,12,buf); + out_ptr += 13; + free(buf); + } + if (article->parent!=0) { + if (article->parent>0) { + sprintf(out_ptr,"[%5d]", article->parent); + } + else sprintf(out_ptr,"%-7s","[ ? ]"); + } + else sprintf(out_ptr,"%7.7s",""); + return out; +} diff --git a/src/flrn_glob.h b/src/flrn_glob.h new file mode 100644 index 0000000..9aad8cf --- /dev/null +++ b/src/flrn_glob.h @@ -0,0 +1,48 @@ +/* flrn v 0.1 : variables globales */ + +#ifndef FLRN_FLRN_GLOB_H +#define FLRN_FLRN_GLOB_H + +#include + +#include "flrn_config.h" + +/* l'erreur entre notre heure et celle du serveur */ +extern int Date_offset; + +/* Ligne de lecture, et la socket (pour flrn_shell.c) */ +extern char tcp_line_read[MAX_READ_SIZE]; +extern int tcp_fd; + +/* User */ +extern struct passwd *flrn_user; + +/* Mode debug */ +extern int debug; + +/* Stockage de certains signaux */ +volatile int sigwinch_received; /* Pour stoper les posts */ + +/* Variables globales de gestion clavier/ecran */ +/* Les attributs SLtt_Char_Type */ +typedef unsigned long FL_Char_Type; + +extern int KeyBoard_Quit; /* ^C */ +extern int Screen_Rows, Screen_Cols; /* Taille de l'écran */ +extern int Screen_Tab_Width; /* Taille du tab (8) */ + +/* Gestion des scrolls */ +typedef struct _File_Line_Type +{ + struct _File_Line_Type *next; + struct _File_Line_Type *prev; + unsigned short *data; + unsigned long data_len; +} +File_Line_Type; + +extern File_Line_Type *Text_scroll; + +extern int overview_usable; + +#endif diff --git a/src/flrn_help.c b/src/flrn_help.c new file mode 100644 index 0000000..fd40c82 --- /dev/null +++ b/src/flrn_help.c @@ -0,0 +1,38 @@ +/* flrn v 0.1 */ +/* fl_help.c 10/12/97 */ +/* */ +/* Gestion hyper-torchante de l'aide. */ +/* */ +/* */ + +#include +#include +#include + +#include "flrn.h" + +static char hl_chars[]="0123456789mq"; +static char hl_mesgs[]="Aide de flrn, à vous [0-9qm] :"; + +void Aide () { + FILE *file; + char key='m'; + + while (key!='q') { + file=open_flhelpfile(key); + key='\0'; + if (file==NULL) { + Aff_error("Erreur : ne trouve pas le fichier d'aide cherché."); + if (key=='m') return; + } else { + key=Aff_file(file,hl_chars,hl_mesgs); + fclose(file); + } + if (key==0) Aff_fin(hl_mesgs); + while ((key==0) || (!strchr(hl_chars,key))) { + key=(char)Attend_touche(); + if (KeyBoard_Quit) key='q'; + if (key=='h') key='m'; + } + } +} diff --git a/src/flrn_inter.c b/src/flrn_inter.c new file mode 100644 index 0000000..fd92d45 --- /dev/null +++ b/src/flrn_inter.c @@ -0,0 +1,2051 @@ +/* flrn v 0.1 */ +/* flrn_inter.c 27/11/97 */ +/* */ +/* La boucle principale de flrn. */ +/* */ +/* */ + +#define IN_FLRN_INTER_C +#include +#include +#include +#include +#include +#include + +#include "flrn.h" +#include "flrn_slang.h" +#include "options.h" +#include "group.h" +#include "flrn_menus.h" +#include "flrn_filter.h" + +/* On va définir ici des structures et des variables qui seront utilisées */ +/* pour loop, et les fonctions qui y sont associés. Il faudrait en fait */ +/* que flrn_inter se limite à ses fonctions... */ + +/* Ces variables etaient auparavant locales à loop */ +struct etat_var { + int hors_struct; /* 1 : hors_limite + 2 et 3 : end_of_group ( => hors_limite ) + 4 et 5 : under_group ( => hors_limite ) + 8 et 11 : hors_newsgroup ( => end_of_groupe ) */ + int etat, num_message; + /* Le système num_message est probablement insuffisant pour des messages */ + /* explicites... */ + Newsgroup_List *Newsgroup_nouveau; /* Pour les changement de newsgroup */ + int num_futur_article; + Article_List *Article_nouveau; /* utilisé si num_futur_article == -1 */ +} etat_loop, etat_save; + +/* Ces variables correspondent simplement aux arguments des fonctions */ +typedef struct Num_lists +{ + struct Num_lists *next; + int flags; /* 0 : rien 1 : num1 2 : num1-num2 + 4 : _num1 8 : num1> */ + int num1, num2; +} Numeros_List; +Numeros_List Arg_do_funcs={NULL, 0, 0, 0}; + +typedef int (*Action)(Article_List *article, void * flag); +int distribue_action(Numeros_List *num, Action action, Action special, + void * flag); + + +#define MAX_CHAR_STRING 100 +char Arg_str[MAX_CHAR_STRING]; + +/* le tableau touche -> commande */ +int Flcmd_rev[MAX_FL_KEY]; +/* pour les macros */ + +#define MAX_FL_MACRO 256 +struct { + int cmd; + char *arg; +} Flcmd_macro[MAX_FL_MACRO]; + +int Flcmd_num_macros=0; + +#define NUM_SPECIAL_TAGS 256 +#define MAX_TAGS 256+NUM_SPECIAL_TAGS +Flrn_Tag tags[MAX_TAGS]; +int max_tags_ptr=0,tags_ptr=0; + +/* Cette horrible structure sert a stocker une action et son argument en un */ +/* seul pointeur... */ +typedef struct Action_Beurk { + Action action; + void *flag; +} Action_with_args; + + +int parse_arg_string(char *str,int command); +/* On prédéfinit ici les fonctions appelés par loop... A l'exception de */ +/* get_command, elles DOIVENT être de la forme int do_* (int res) */ +int get_command(int key); +int do_deplace(int res); +int do_goto(int res); /* renvoie change */ +int do_unsubscribe(int res); +int do_abonne(int res); +int do_omet(int res); +int do_kill(int res); /* tue avec les crossposts */ +int do_zap_group(int res); /* la touche z... a revoir */ +int do_help(int res); +int do_quit(int res); /* cette fonction est inutile */ +int do_summary(int res); /* Doit faire à la fois r, t, T */ +int do_save(int res); +int do_pipe(int res); +int do_launch_pager(int res); +int do_list(int res); +int do_post(int res); +int do_opt(int res); +int do_opt_menu(int res); +int do_neth(int res); +int do_get_father(int res); +int do_swap_grp(int res); +int do_prem_grp(int res); +int do_goto_tag(int res); +int do_tag(int res); +int do_back(int res); +int do_cancel(int res); + + +/* Ces fonctions sont appelés par les do_* */ +int change_group(Newsgroup_List **newgroup,int flags, char *gpe_tab); +int prochain_non_lu(int force_reste, Article_List **debut); +int prochain_newsgroup(); +void Get_option_line(char *argument); +/* les trois fonctions suivantes sont inutilisées... + * mais on les laisse tant que la bonne version 'est pas écrite */ +void zap_thread(Article_List *article,int all, int flag, int zap); +int next_thread(int flags); + +static int push_tag(); + +/* aff_opt_c : affiche simplement une liste de messages non lus vers stdout */ +/* A la demande de Sbi, j'affiche aussi le nombre total de messages */ +/* non lus... */ +void aff_opt_c() { + int res, nb_non_lus=0; + + Newsgroup_courant=Newsgroup_deb; + while (Newsgroup_courant) { + if (Newsgroup_courant->flags & GROUP_UNSUBSCRIBED) { + Newsgroup_courant=Newsgroup_courant->next; + continue; + } + res=NoArt_non_lus(Newsgroup_courant); + if (res==-2) + fprintf(stdout, "Mauvais newsgroup : %s\n", Newsgroup_courant->name); + if (res>0) { + fprintf(stdout, "%s : %d article%s non lu%s\n", Newsgroup_courant->name,res, (res==1 ? "" : "s"), (res==1 ? "" : "s")); + nb_non_lus+=res; + } + Newsgroup_courant=Newsgroup_courant->next; + } + if (nb_non_lus==0) fprintf(stdout, "Rien de nouveau.\n"); else + fprintf(stdout, " Il y a au total %d article%s non lu%s.\n",nb_non_lus,(nb_non_lus==1 ? "" : "s"), (nb_non_lus==1 ? "" : "s")); +} + +/* affiche un message + * type = 0 => info, sinon, erreur */ +static void Aff_message(int type, int num) +{ + switch (num) { + /* Message d'information */ + case 1 : Aff_error("Rien de nouveau."); break; + case 2 : Aff_error("Fin du newsgroup."); break; + case 3 : Aff_error("Message(s) inexistant(s)."); break; + case 4 : + case 5 : Aff_error("Les messages sont marqués non lus."); break; + case 6 : Aff_error("Post envoyé."); break; + case 7 : Aff_error("Post annulé."); break; + case 8 : Aff_error("Article(s) sauvé(s)."); break; + case 9 : Aff_error("Vous êtes abonnés à ce newsgroup."); break; + case 10 : Aff_error("Pas d'autre thread non lu."); break; + case 11 : Aff_error("Tous les articles sont marqués lus."); + break; + case 12 : Aff_error("Pas d'autres newsgroups."); break; + case 13 : Aff_error("Xref non trouvé."); break; + case 14 : Aff_error("(continue)"); break; + case 15 : Aff_error("Tag mis"); break; + case 16 : Aff_error("Cancel annulé"); break; + case 17 : Aff_error("Article(s) cancelé(s)"); break; + /* Message d'erreur */ + case -1 : Aff_error("Vous n'êtes abonné à aucun groupe."); + break; + case -2 : Aff_error("Ce newsgroup est vide."); + break; + case -3 : Aff_error("Vous n'êtes dans aucun newsgroup."); + break; +/* case -4 : Aff_error("Post refusé."); + break; */ + case -5 : Aff_error("Pas d'article négatif."); + break; + case -6 : Aff_error("Echec de la sauvegarde."); + break; + case -7 : Aff_error("Newsgroup inconnu et supprimé."); + break; + case -8 : Aff_error("Newsgroup non trouvé."); + break; + case -9 : Aff_error("Commande inconnue..."); + break; + case -10 : Aff_error("Regexp invalide..."); break; + case -11 : Aff_error("Echec du pipe..."); break; + case -12 : Aff_error("L'article n'est plus dans le newsgroup cherché..."); + break; + case -13 : Aff_error("Tag invalide."); break; + case -14 : Aff_error("Cancel refusé."); break; + case -15 : Aff_error("Historique vide."); break; + default : Aff_error("Erreur non reconnue !!!"); + break; + } + +} + +/* return 1 if 'q' was pressed */ +int loop(char *opt) { + int res=0, quit=0, key; + int to_build=0; /* il faut appeler cree_liste */ + int change; + + etat_loop.hors_struct=11; + etat_loop.etat=etat_loop.num_message=0; + etat_loop.Newsgroup_nouveau=NULL; + /* On cherche un newsgroup pour partir */ + Newsgroup_courant=NULL; + Last_head_cmd.Article_vu=NULL; + if (opt) { + Newsgroup_courant=Newsgroup_deb; + while (Newsgroup_courant) { + if (strstr(Newsgroup_courant->name, opt) && + !(Newsgroup_courant->flags & GROUP_UNSUBSCRIBED)) break; + Newsgroup_courant=Newsgroup_courant->next; + } + if (Newsgroup_courant==NULL) { /* Deuxieme passe */ + Newsgroup_courant=Newsgroup_deb; + while (Newsgroup_courant) { + if (strstr(Newsgroup_courant->name, opt)) break; + Newsgroup_courant=Newsgroup_courant->next; + } + } + if (Newsgroup_courant==NULL) /* Troisieme passe */ + Newsgroup_courant=cherche_newsgroup(opt,0); + if (Newsgroup_courant==NULL) { + Aff_error("Newsgroup non trouvé"); + sleep(1); + quit=1; + } else { + to_build=1; + etat_loop.hors_struct=0; + } + etat_loop.Newsgroup_nouveau=Newsgroup_courant; + } + else { + res=prochain_newsgroup(&(etat_loop.Newsgroup_nouveau)); + if (res<0) { + etat_loop.etat=1; etat_loop.num_message=1; + Article_deb=&Article_bidon; + } else { + to_build=1; + etat_loop.hors_struct=0; + Newsgroup_courant=etat_loop.Newsgroup_nouveau; + } + etat_loop.Newsgroup_nouveau=NULL; + } + if ((Newsgroup_courant==NULL) && (Options.quit_if_nothing)) + return 0; + + /* Maintenant on cherche quelque chose à lire */ + + while ((Newsgroup_deb) && (!quit)) { + Aff_newsgroup_name(); + if (to_build) { + res = etat_loop.num_futur_article>0?etat_loop.num_futur_article: + (Newsgroup_courant->read?Newsgroup_courant->read->max[0]:1); + res=cree_liste(res, &to_build); + if (res==-2) { + zap_newsgroup(Newsgroup_courant); + Newsgroup_courant=NULL; + Article_deb=&Article_bidon; + etat_loop.etat=2; etat_loop.num_message=-7; + } + etat_loop.hors_struct=(res<0)?11:0; + if (res==0) { + Article_deb=&Article_bidon; + etat_loop.etat=2; etat_loop.num_message=-2; + } + } + Aff_newsgroup_courant(); + if (debug) fprintf(stderr, "Liste créée\n"); + if (etat_loop.num_futur_article==-1) { + Article_courant=etat_loop.Article_nouveau; + etat_loop.etat=0; + } else { + Article_courant=Article_deb; + } + if (Article_courant) { + if (etat_loop.hors_struct==1) etat_loop.hors_struct=0; + if (!(etat_loop.hors_struct & 8)) + if (etat_loop.num_futur_article==0) + change=-prochain_non_lu(etat_loop.num_message==1,&Article_courant); + else { + if (etat_loop.num_futur_article !=-1) { + Arg_do_funcs.num1=etat_loop.num_futur_article; + Arg_do_funcs.num2=0; + Arg_do_funcs.flags=1; + if (Arg_do_funcs.next) Arg_do_funcs.next->flags=0; + do_deplace(FLCMD_VIEW); + } + change=0; + } + else change=0; + /* change=1 : il n'y a rien a lire */ + if ((etat_loop.Newsgroup_nouveau) && (change==1)) { + change=0; /* On veut rester sur ce newsgroup */ + etat_loop.hors_struct|=3; /* Fin du conti */ + etat_loop.etat=1; etat_loop.num_message=2; + } + if (change==2) { /* On doit reconstruire le groupe ??? */ + change=1; + etat_loop.Newsgroup_nouveau=Newsgroup_courant; + } + while ((!change) && (!quit)) { + if ((etat_loop.hors_struct & 8) && (etat_loop.etat==0)) { + etat_loop.etat=2; etat_loop.num_message=-3; + } + if (debug) fprintf(stderr, "etat %d num_message %d\n", etat_loop.etat, etat_loop.num_message); + key=0; + if (etat_loop.etat==0) { key=Aff_article_courant(); + push_tag(); + etat_loop.hors_struct&=8; + if (etat_loop.hors_struct) etat_loop.hors_struct|=3; + /* ceci revient a ne garder qu'hors_newsgroup */ + } + if ((etat_loop.etat==1) || (etat_loop.etat==2)) + Aff_message(etat_loop.etat-1, etat_loop.num_message); + etat_loop.etat=0; etat_loop.num_message=0; + etat_loop.num_futur_article=0; + etat_loop.Newsgroup_nouveau=NULL; + if (to_build) { + /* on finit la liste... */ + Aff_fin("Patientez..."); /* Pour le voir un peu quand même */ + Screen_refresh(); + cree_liste_end(); + to_build=0; + } + if (key<0) key=0; /* Aff_article_courant a renvoyé une erreur */ + res=get_command(key); + if (debug) fprintf(stderr, "retour de get_command : %d\n", res); + if ((res >0) && (res & FLCMD_MACRO)) { + int num_macro= res ^FLCMD_MACRO; + res = Flcmd_macro[num_macro].cmd; + res = parse_arg_string(Flcmd_macro[num_macro].arg,res); + } + if (res==-2) etat_loop.etat=3; else + if (res==FLCMD_UNDEF) + { etat_loop.etat=2; etat_loop.num_message=-9; } + else { + if ((Flcmds[res].flags & CMD_NEED_GROUP) && + (etat_loop.hors_struct & 8)) { + etat_loop.etat=2; etat_loop.num_message=-3; change=0; + } else + change=(*Flcmds[res].appel)(res); + } + quit=((res==FLCMD_QUIT) || (res==FLCMD_GQUT)); + /* si on change de groupe VERS un article non existant, on ne */ + /* change pas de groupe */ + if ((change) && (etat_loop.num_futur_article!=0) && + (etat_loop.num_futur_articlemin) + && (etat_loop.num_futur_article!=-1)) { + change=0; etat_loop.hors_struct|=1; + etat_loop.etat=2; etat_loop.num_message=-12; + } + } + } else change=1; + if (to_build) { + /* on finit la liste... */ + Aff_fin("Patientez..."); /* Pour le voir un peu quand même */ + Screen_refresh(); + cree_liste_end(); + } + to_build=change; + if ((change==1) && (etat_loop.Newsgroup_nouveau==NULL)) { + etat_loop.num_futur_article=0; + res=prochain_newsgroup(&(etat_loop.Newsgroup_nouveau)); + if (res==-1) { + to_build=0; + etat_loop.etat=1; etat_loop.num_message=1; + if (!(etat_loop.hors_struct & 8) && Article_courant) { + if (Newsgroup_courant->flags & GROUP_UNSUBSCRIBED) { + detruit_liste(0); + Newsgroup_courant=NULL; + Article_deb=&Article_bidon; + } else + if (prochain_non_lu(0,&Article_courant)==0) { + etat_loop.etat=0; + } else etat_loop.hors_struct=3; + } + } else if (res==-2) { + to_build=0; + etat_loop.etat=2; etat_loop.num_message=-20; + } + } + if (to_build==1) { + if (Article_deb && (Article_deb!=&Article_bidon)) detruit_liste(0); + Newsgroup_courant=etat_loop.Newsgroup_nouveau; + } + if (Newsgroup_courant==NULL) etat_loop.hors_struct=11; + change=0; + } + if (Newsgroup_courant && Article_deb && (Article_deb!=&Article_bidon)) detruit_liste(0); + return (res==FLCMD_QUIT); +} + +void init_Flcmd_rev() { + int i; + for (i=0;i= MAX_FL_KEY)) return FLCMD_UNDEF; + return Flcmd_rev[key]; +} + +int fonction_to_number(char *nom) { + int i; + for (i=0;i= MAX_FL_KEY) + return -2; + /* les commandes non rebindables... Je trouve ca moche */ + for (i=0;inumero>-2)) { + res=Article_courant->numero; + if (res==-1) res=Article_courant->prem_fils->numero; + } + if ((res>0) && (Article_deb)) { + parcours=Article_deb; + while (parcours && (parcours->numeronext; + if (parcours && (parcours->numero==res)) { + parcours=root_of_thread(parcours,0); + res=parcours->numero; + } + } + } + return res; +} + + +/* Parsing des numeros d'articles + * Arg_do_funcs.flag est a 0 s'il n'y a rien a parser... + * On arrete des qu'il y a un problème. + * On a deux règles spéciales : 0 est l'article courant, + * 1 le premier article du groupe */ +static void Parse_nums_article(char *str, char **sortie, int flags) { + char *ptr=str, *ptr2, *ptrsign; + int reussi=1; + char save_char='\0'; + Numeros_List *courant=&Arg_do_funcs; + /* on conserve le debut de la liste. Pour les commandes explicites */ + while (courant->flags != 0) courant = courant->next; + + while (ptr && reussi) { + ptr2=strchr(ptr,','); + if (ptr2!=NULL) *ptr2='\0'; + if (*ptr=='\0') { + courant->flags=0; + } else { + ptrsign=strchr(ptr,'-'); + if (ptrsign!=NULL) + { + *ptrsign='\0'; + courant->flags=2; + courant->num1=Decode_numero(ptr,1); + if (courant->num1==-1) reussi=0; + *ptrsign='-'; + courant->num2=Decode_numero(ptrsign+1,(Newsgroup_courant ? Newsgroup_courant->max : 1)); + if (courant->num2==-1) reussi=0; + } else { /* ce n'est pas '-' */ + ptrsign=strchr(ptr,'_'); + if (ptrsign!=NULL) { save_char='_'; courant->flags=4; *ptrsign=','; } + else { + ptrsign=strchr(ptr,'>'); + if (ptrsign!=NULL) + { save_char='>'; courant->flags=8; *ptrsign=','; } + else courant->flags=1; + } + courant->num1=Decode_numero(ptr, 0); + if (courant->num1==-1) reussi=0; + if (ptrsign!=NULL) *ptrsign=save_char; + } + } + if (Article_courant && (courant->num1==0)) + courant->num1=Article_courant->numero; + if (Article_deb && (courant->num1==1)) + courant->num1=Article_deb->numero; + if (Article_courant && (courant->num2==0)) + courant->num2=Article_courant->numero; + if (Article_deb && (courant->num2==1)) + courant->num2=Article_deb->numero; + if (reussi) { + if (ptr2) { *ptr2=','; ptr=ptr2+1; } else ptr=NULL; + if (courant->next) courant=courant->next; else + { courant->next=safe_calloc(1,sizeof(Numeros_List)); + courant=courant->next; + } + courant->flags=0; + } + } + if (!reussi) + courant->flags=0; + *sortie=ptr; +} + + +/* appelé par get_command_explicite et get_command_nocbreak */ +int parse_arg_string(char *str,int command) +{ + int flag; + if (str) while (*str==' ') str++; + if ((str==NULL) || (str[0]=='\0')) return command; + flag=Flcmds[command].flags%4; + if (flag==0) return command; + Parse_nums_article(str, &str, flag); + if (str) strncpy(Arg_str, str, MAX_CHAR_STRING-1); + return command; +} + +/* Prend une commande en nocbreak */ +/* renvoie -2 si rien */ +/* asked est le nom de la touche tapée si on n'est pas en mode nocbreak */ +/* asked peut eventuellement ne pas etre fl_key_nocbreak, auquel cas */ +/* elle provient de l'interruption de aff_article_courant... */ +int get_command_nocbreak(int asked) { + char cmd_line[MAX_CHAR_STRING]; + char *str=cmd_line; + int res; + + /* Dans le cas où asked='\r', et vient donc directement d'une interruption */ + /* on ne prend pas de ligne de commande : on l'a déjà... */ + if (asked=='\r') return Flcmd_rev['\r']; + if (asked) Screen_write_char(asked); + if (asked && (asked!=fl_key_nocbreak)) *(str++)=asked; + *str='\0'; + str=cmd_line; + if ((res=getline(str,MAX_CHAR_STRING,Screen_Rows-1,9+(asked && (asked==fl_key_nocbreak))))<0) + return -2; + while(*str==fl_key_nocbreak) str++; + if (str[0]=='\0') return Flcmd_rev['\r']; + if (isdigit(str[0]) || (str[0]=='<')) { + Parse_nums_article(str, &str, 2); + return FLCMD_VIEW; + } + if (str[0]==fl_key_explicite) { + res=Lit_cmd_explicite(str+1); + str=strchr(str,' '); + } + else { + res=Lit_cmd_key(str[0]); + str++; + } + if (res==FLCMD_UNDEF) { + strncpy(Arg_str, cmd_line, MAX_CHAR_STRING-1); + return res; + } + return parse_arg_string(str,res); +} + +/* Completion automagique sur les commandes explicites */ +int Comp_cmd_explicite(char *str, int len) +{ + char *guess=NULL; + int prefix_len=0; + int match=0; + int i,j; + for (i=0;i1) { + if (prefix_len < len) {strncpy(str, guess, prefix_len); + str[prefix_len]='\0'; + } + } + return 1; +} + +/* Prend une commande explicite */ +/* returne -2 si rien */ +int get_command_explicite(char *start) { + int res=0; + char cmd_line[MAX_CHAR_STRING], *str=cmd_line; + int prefix_len=0; + cmd_line[0]='\0'; + if (start) { + prefix_len = strlen(start); + strncpy (cmd_line, start, MAX_CHAR_STRING-2); + } + strcat(cmd_line,"\\"); + prefix_len++; + do { + Cursor_gotorc(Screen_Rows-1,9); Screen_erase_eol(); + Screen_write_string(str); + if ((res=magic_getline(str+prefix_len,MAX_CHAR_STRING-prefix_len, + Screen_Rows-1,9+prefix_len,"\011",0))<0) + return -2; + if (res>0) Comp_cmd_explicite(str+prefix_len,MAX_CHAR_STRING-prefix_len); + } while (res!=0); + res=Lit_cmd_explicite(str+prefix_len); + str=strchr(str,' '); + if (res==FLCMD_UNDEF) { + strcpy(Arg_str,"\\"); + strncat(Arg_str, cmd_line+prefix_len, MAX_CHAR_STRING-prefix_len-2); + } + return parse_arg_string(str,res); +} + +/* Prend une commande avec le nouveau mode */ +int get_command_newmode(int key) { + int res; + char cmd_line[MAX_CHAR_STRING]; + char *str=cmd_line; + + cmd_line[0]=key; cmd_line[1]='\0'; + Screen_write_char(key); + /* On appelle magic_getline avec flag=1 */ + if ((res=magic_getline(str,MAX_CHAR_STRING,Screen_Rows-1,9,"1234567890,<>._-",1))<0) + return -2; + Parse_nums_article(str, &str, 0); + if (res==fl_key_explicite) res=get_command_explicite(cmd_line); else { + res=Lit_cmd_key(res); + if (res==FLCMD_SUIV) res=FLCMD_VIEW; + } + return res; +} + +/* Prend la chaine argument pour les appels qui en ont besoin en mode cbreak */ +/* On ajoute key dans le cas ou isdigit(key) ou key=='<' */ +/* Renvoie -1 si annulation */ +static int get_str_arg(int res, int key) { + int col=9, ret; + char cmd_line[MAX_CHAR_STRING]; + char *str=cmd_line; + + Aff_fin("A vous : "); + Screen_write_string(Flcmds[res].nom); + Screen_write_char(' '); + col+=1+strlen(Flcmds[res].nom) /* +1 */; + if ((isdigit(key) || (key=='<'))) { str[0]=key; + str[1]='\0'; Screen_write_char(key); } else + str[0]=0; + ret=getline(str, MAX_CHAR_STRING, Screen_Rows-1, col); + if (ret<0) return -1; + if ((!Options.new_mode) && (Flcmds[res].flags & 2)) + Parse_nums_article(str,&str,0); + if (str) strcpy(Arg_str, str); else Arg_str[0]='\0'; + return 0; +} + + +/* Prend une commande pour loop... Renvoie le code de la commande frappe */ +/* Renvoie -1 si commande non défini */ +/* -2 si rien */ +/* -3 si l'état est déjà défini... */ +int get_command(int key_depart) { + int key, res, res2; + + Arg_do_funcs.flags=0; + Arg_str[0]='\0'; + + Aff_fin("A vous : "); + if (!Options.cbreak) return get_command_nocbreak(key_depart); + if (key_depart) key=key_depart; else key=Attend_touche(); + if (key==fl_key_nocbreak) return get_command_nocbreak(fl_key_nocbreak); + if (key==fl_key_explicite) return get_command_explicite(NULL); + else { + /* Beurk pour '-' et ',' */ + if (index(",-",key)) return Flcmd_rev[key]; + if (!Options.new_mode) + if ((isdigit(key)) || (key=='<')) res=FLCMD_VIEW; else + res=Lit_cmd_key(key); + else if (strchr("0123456789<>.,_",key)==NULL) res=Lit_cmd_key(key); + else res=get_command_newmode(key); + } + if (res==FLCMD_UNDEF) return FLCMD_UNDEF; + if (res & FLCMD_MACRO) return res; + /* Testons si on a besoin d'un (ou plusieurs) parametres */ + if (((Options.new_mode) && (Flcmds[res].flags & 8) && (Arg_str[0]=='\0')) || + ((!Options.new_mode) && (Flcmds[res].flags & 4))) { + res2=get_str_arg(res,(!Options.new_mode ? key : '\0')); + if (res2==-1) return -2; + } + return res; +} + +static int tag_article(Article_List *art, void * flag) +{art->flag |= *(int *)flag; return 0;} + +/* do_deplace : deplacement */ +int do_deplace(int res) { + Article_List *parcours, *parcours2; + int parc_eq_cour=0, peut_changer, ret=0; + + /* est-ce mieux avec ou sans ??? */ + /* ou avec max_tags_ptr=tags_ptr; ? */ + tags_ptr=max_tags_ptr; + + peut_changer=(etat_loop.hors_struct & 2) && (!Options.space_is_return); + + if (etat_loop.hors_struct & 8) { + if (!peut_changer) { + etat_loop.etat=2; + etat_loop.num_message=-3; + } + return (peut_changer); + } + if ((Article_courant==&Article_bidon) && (!peut_changer)) { + etat_loop.etat=1; etat_loop.num_message=3; return 0; + } + parcours=Article_courant; + /* dans le cas ou on est hors limite et num1=Article_courant->numero */ + /* on reaffiche Article_courant, a condition que ce ne soit ni - ni '\n' */ + /* ni un ' ' (sauf cas particuliers) */ + if (Article_courant && (Arg_do_funcs.flags==0)) { + parc_eq_cour=1; + if ((etat_loop.hors_struct & 1) && (res!=FLCMD_SPACE) && + ((res!=FLCMD_PREC) || !(etat_loop.hors_struct & 4)) && + ((res!=FLCMD_SUIV) || !(etat_loop.hors_struct & 2))) + { etat_loop.hors_struct=0; + etat_loop.etat=0; etat_loop.num_message=0; + return 0; + } + } else { + /* on cheche Arg_do_funcs.num1 */ + ret=Recherche_article(Arg_do_funcs.num1, &parcours, + (res==FLCMD_PREC ? -1 : (res==FLCMD_SUIV ? 1 : 0))); + if (ret==-2) { + etat_loop.hors_struct=1; + etat_loop.etat=1; etat_loop.num_message=3; return 0; + } + } + /* Si parc_eq_cour=1 et Article_courant est extérieur, on revient à son */ + /* fils A MOINS que la commande ne soit FLCMD_RIGHT ou FLCMD_VIEW */ + if (parc_eq_cour && (parcours->numero==-1) && (res!=FLCMD_RIGHT) && + (res!=FLCMD_VIEW)) + parcours=parcours->prem_fils; + + /* Je vois pas comment on peut éviter un case */ + parcours2=NULL; + switch (res) { + case FLCMD_PREC : if (ret!=-1) parcours=parcours->prev; break; + case FLCMD_SUIV : if (ret!=-1) { + parcours2=parcours->next; + if ((parcours2==NULL) && (parcours->numero>0)) { + if (parcours->numeromax) + /* On peut avoir un article juste posté que */ + /* Newnews ne detectera pas faute date... */ + parcours2=ajoute_message_par_num( + parcours->numero+1, + Newsgroup_courant->max); + if (parcours2==NULL) { + /* cherchons de nouveau messages */ + ret=cherche_newnews(); + if (ret==-2) { /* Alors pas bo */ + etat_loop.Newsgroup_nouveau=Newsgroup_courant; + return 1; + } else if (ret>0) parcours2=parcours->next; + } + } + parcours=parcours2; + } + break; + case FLCMD_UP : parcours2=parcours->frere_prev; break; + case FLCMD_LEFT : if (parcours->pere) parcours->pere->prem_fils=parcours; + parcours2=parcours->pere; break; + case FLCMD_RIGHT : parcours2=parcours->prem_fils; break; + case FLCMD_DOWN : parcours2=parcours->frere_suiv; break; + case FLCMD_SPACE : ret=-prochain_non_lu(0, &parcours); + if (ret==2) { + etat_loop.Newsgroup_nouveau=Newsgroup_courant; + return 1; + } else if (ret==1) if (peut_changer) return 1; else + parcours=NULL; + break; + } + if ((res==FLCMD_UP) || (res==FLCMD_DOWN) || (res==FLCMD_LEFT) || + (res==FLCMD_RIGHT)) + if ((parcours2) || (Options.inexistant_arrow)) parcours=parcours2; + if (parcours==NULL) { + etat_loop.hors_struct=1; + if (parc_eq_cour) if (res==FLCMD_PREC) etat_loop.hors_struct=5; else + if ((res==FLCMD_SUIV) || (res==FLCMD_SPACE)) etat_loop.hors_struct=3; + etat_loop.etat=1; + etat_loop.num_message=(etat_loop.hors_struct & 2 ? 2 : 3); + } else { + Article_courant=parcours; + etat_loop.etat=0; + } + return 0; +} + +static int my_goto_tag (int tag) { + if (tag >= MAX_TAGS) return -1; + if (tag <0 ) return -1; + if (tags[tag].article_deb_key) { /* le tag existe */ + if (Newsgroup_courant && (tags[tag].article_deb_key == Newsgroup_courant->article_deb_key)) { + Article_courant=tags[tag].article; + etat_loop.etat=0; + } else { + /* faut changer de groupe */ + etat_loop.Newsgroup_nouveau = + cherche_newsgroup(tags[tag].newsgroup_name,1); + if(etat_loop.Newsgroup_nouveau == NULL) { + etat_loop.etat=2; etat_loop.num_message=-8; + etat_loop.hors_struct|=1; + return 0; + } + /* si le pointeur est valide, c'est gagné ! */ + if (tags[tag].article_deb_key == + etat_loop.Newsgroup_nouveau->article_deb_key) { + etat_loop.Article_nouveau=tags[tag].article; + etat_loop.num_futur_article=-1; + } else + etat_loop.num_futur_article=tags[tag].numero; + return 1; + } + } else { /* le tag n'existe pas */ + etat_loop.etat=2; + etat_loop.hors_struct|=1; + etat_loop.num_message=-13; + } + return 0; +} + +int do_goto_tag(int res) { + return my_goto_tag(((unsigned char)Arg_str[0])+NUM_SPECIAL_TAGS); +} + +/* Attention, je suppose que Newsgroup_courant->name + * va rester valide */ +static int my_tag (Article_List *article,int tag) { + if (tag >= MAX_TAGS) return -1; + if (tag < 0) return -1; + tags[tag].article_deb_key = Newsgroup_courant->article_deb_key; + tags[tag].article = Article_courant; + tags[tag].numero = Article_courant->numero; +/* strcpy(tags[tag].newsgroup_name,Newsgroup_courant->name); */ + tags[tag].newsgroup_name=Newsgroup_courant->name; + return 0; +} + +static int my_tag_void(Article_List *article,void *vtag) { + return my_tag(article, (int)(long) vtag); +} + +int do_tag (int res) { + int tag; + Numeros_List *courant=&Arg_do_funcs; + tag = ((unsigned char)Arg_str[0]) +NUM_SPECIAL_TAGS; + distribue_action(courant,my_tag_void,NULL,(void *) (long) tag); + etat_loop.etat=1; etat_loop.num_message=15; + return 0; +} + +static int push_tag() { + int res; + if (tags[(tags_ptr+NUM_SPECIAL_TAGS-1)%NUM_SPECIAL_TAGS].article + ==Article_courant) + return 0; + res=my_tag(Article_courant,tags_ptr); + if (max_tags_ptr == tags_ptr) { + tags_ptr++; + tags_ptr %= NUM_SPECIAL_TAGS; + max_tags_ptr=tags_ptr; + } else { + tags_ptr++; + tags_ptr %= NUM_SPECIAL_TAGS; + } + return res; +} + +int do_next(int res) { + int ret; + ret=my_goto_tag(tags_ptr); + return ret; +} + +int do_back(int res) { + tags_ptr += NUM_SPECIAL_TAGS-2; + tags_ptr %= NUM_SPECIAL_TAGS; + if (tags[tags_ptr].article_deb_key==0) { + tags_ptr++; + tags_ptr %= NUM_SPECIAL_TAGS; + } + return my_goto_tag(tags_ptr); +} + +static int validate_tag_ptr(Flrn_Tag *tag) { + Newsgroup_List *tmp=NULL; + if (Newsgroup_courant && (Newsgroup_courant->article_deb_key == tag->article_deb_key)) + return 1; + tmp = cherche_newsgroup(tag->newsgroup_name,1); + if (tmp && (tmp->article_deb_key == tag->article_deb_key)) + return 1; + return 0; +} + +static void hist_menu_summary(void *item, char *line, int length) { + Flrn_Tag *tag = &tags[((int)(long)item)-1]; + *line='\0'; + if (validate_tag_ptr(tag)) { + Prepare_summary_line(tag->article,NULL,0,line,length); + } +} + +int do_hist_menu(int res) { + int i; + char buf[80]; + Liste_Menu *menu=NULL, *courant=NULL, *start=NULL; + int valeur; + int j, dup, bla; + + bla=(max_tags_ptr ? max_tags_ptr-1 : NUM_SPECIAL_TAGS-1); + for (i=bla; (i!=max_tags_ptr) && (tags[i].article_deb_key); + i= (i+NUM_SPECIAL_TAGS-1)%NUM_SPECIAL_TAGS) { + dup=0; + for (j=bla; j!=i; j= (j+NUM_SPECIAL_TAGS-1)%NUM_SPECIAL_TAGS) { + if ((tags[j].article_deb_key == tags[i].article_deb_key) && + ((tags[j].article == tags[i].article))) { + dup=1; + break; + } + } + if (dup) continue; + if (tags[i].numero <0) { + snprintf(buf,80,"%s:?",tags[i].newsgroup_name); + } else + snprintf(buf,80,"%s:%d",tags[i].newsgroup_name, tags[i].numero); + courant=ajoute_menu(courant,safe_strdup(buf),(void *)(long)(i+1)); + if (tags[i].article==Article_courant) start=courant; + if (!menu) menu=courant; + } + if (!menu) { + etat_loop.etat=2; etat_loop.num_message=-15; + return 0; + } + valeur = (int)(long) Menu_simple(menu, start, hist_menu_summary, NULL); + if (!valeur) { + etat_loop.etat=3; etat_loop.num_message=-15; + return 0; + } + valeur --; + Libere_menu_noms(menu); + tags_ptr=valeur; + return my_goto_tag(valeur); +} + +/* Aller dans un autre groupe */ +int do_goto (int res) { + int ret; + + etat_loop.num_futur_article=(Arg_do_funcs.flags==0 ? 0 : Arg_do_funcs.num1); + ret=change_group(&(etat_loop.Newsgroup_nouveau), (res==FLCMD_GGTO), + Arg_str); + if (ret>=0) return 1; else + if (ret==-1) etat_loop.etat=3; else + { etat_loop.etat=2; etat_loop.num_message=-8; + etat_loop.hors_struct|=1; } + return 0; +} + + +/* do_unsubscribe : pour l'instant, j'ignore les arguments... J'aimerais */ +/* bien pouvoir me desabonner a un ensemble de groupes... */ +int do_unsubscribe(int res) { + Newsgroup_courant->flags |= GROUP_UNSUBSCRIBED; + return 1; +} + +/* do_abonne : pour l'instant, j'ignore les arguments... J'aimerais */ +/* bien pouvoir m'abonner a un ensemble de groupes... */ +int do_abonne(int res) { + Newsgroup_courant->flags &= ~GROUP_UNSUBSCRIBED; + Aff_newsgroup_name(); + etat_loop.etat=1; etat_loop.num_message=9; + return 0; +} + +/* do_prem_grp : pour l'instant, place le groupe courant en première position */ +/* c'est un peu léger pour ordonner le .flnewsrc, mais bon... */ +int do_prem_grp (int res) { + if (Newsgroup_courant->prev) { + Newsgroup_courant->prev->next=Newsgroup_courant->next; + Newsgroup_courant->next=Newsgroup_deb; + Newsgroup_deb->prev=Newsgroup_courant; + Newsgroup_deb=Newsgroup_courant; + } + return 0; +} + + +/* des hack crades :-( */ +int thread_distribue(Article_List *article, void * beurk) { + Numeros_List blah; + Action_with_args *le_machin = (Action_with_args *) beurk; + blah.next=NULL; blah.flags=8; blah.num1=root_of_thread(article,0)->numero; + distribue_action(&blah,le_machin->action,NULL,le_machin->flag); + return 0; +} +int grand_distribue(Article_List *article, void * beurk) { + Numeros_List blah; + Action_with_args *le_machin = (Action_with_args *) beurk; + blah.next=NULL; blah.flags=8; blah.num1=article->numero; + distribue_action(&blah,le_machin->action,NULL,le_machin->flag); + return 0; +} + +static int omet_article(Article_List *article, void * toto) +{ article->flag &= ~FLAG_READ; return 0; } +int do_omet(int res) { + Numeros_List *courant=&Arg_do_funcs; + Action_with_args beurk; + + beurk.action=omet_article; + beurk.flag=NULL; + if (res==FLCMD_GOMT) + distribue_action(courant,grand_distribue,NULL,&beurk); + else + distribue_action(courant,omet_article,NULL,NULL); + etat_loop.etat=1; etat_loop.num_message=(res==FLCMD_GOMT ? 5 : 4); + return 0; +} + +/* La, j'ai un doute sur la semantique + * KILL tue des threads ? */ +static int kill_article(Article_List *article, void * toto) +{ article_read(article); return 0; } +int do_kill(int res) { + Numeros_List *courant=&Arg_do_funcs; + Action_with_args beurk; + + beurk.action=kill_article; + beurk.flag=NULL; + if (res==FLCMD_KILL) + distribue_action(courant,grand_distribue,NULL,&beurk); + else if (res==FLCMD_PKIL) + distribue_action(courant,kill_article,NULL,NULL); + else + distribue_action(courant,thread_distribue,NULL,&beurk); + etat_loop.etat=0; + /* Est-ce un hack trop crade ? */ + courant->flags=0; + do_deplace(FLCMD_SPACE); + return 0; +} + +/* Bon, on marque tous les articles lus, c'est pas optimal + * Mais ca permet d'avoir un message avant de quitter le groupe + * si l'on veut */ +int do_zap_group(int res) { + Numeros_List blah; + int flag=FLAG_READ; + + blah.next=NULL; blah.flags=2; blah.num1=1; + blah.num2=Newsgroup_courant->max; + distribue_action(&blah,tag_article,NULL,&flag); + Recherche_article(Newsgroup_courant->max,&Article_courant,-1); + etat_loop.hors_struct|=3; /* Fin du conti */ + etat_loop.etat=1; etat_loop.num_message=11; + return (Options.zap_change_group)?1:0; +} + +/* do_help : lance aide() */ +int do_help(int res) { + etat_loop.etat=3; + Aide(); + return 0; +} + +/* do_quit : ne fait rien */ +int do_quit(int res) { + return 0; +} + +static int check_and_tag_article(Article_List *article, void *blah) +{ + flrn_filter *arg = (flrn_filter *) blah; + if (check_article(article,blah,0)<=0) + { tag_article(article,& arg->action.flag); return 1; } + else return 0; +} + +/* do_summary Il faut toujours appeler Aff_summary + * car Aff_summary retire le FLAG_ACTION + * La, on a des comportements par défaut non triviaux, et qui + * en plus ont le mauvais gout de dependre de la commande */ +static int Do_aff_summary_line(Article_List *art, int *row, + char *previous_subject, int level, Liste_Menu **courant) { + return Aff_summary_line(art,row,previous_subject,level); +} + +static int Do_menu_summary_line(Article_List *art, int *row, + char *previous_subject, int level, Liste_Menu **courant) { + char *buf= safe_malloc(Screen_Cols-2); + Prepare_summary_line(art,previous_subject,level, buf, Screen_Cols-2); + *courant=ajoute_menu(*courant,buf,art); + return 0; +} + +static Article_List * raw_Do_summary (int deb, int fin, int thread, + int action(Article_List *, int *, char *, int, Liste_Menu **)) { + Article_List *parcours; + Article_List *parcours2; + char *previous_subject=NULL; + int level=1; + int row=0; + Liste_Menu *courant=NULL, *menu=NULL, *start=NULL; + + /* On DOIT effacer l'écran pour les commandes summary de base... */ + Cursor_gotorc(1,0); Screen_erase_eos(); + /* find first article */ + parcours=Article_deb; + while (parcours && (parcours->numeronext; + while (parcours && !(parcours->flag & FLAG_ACTION)) + parcours=parcours->next; + parcours2=parcours; + + while (parcours && (!fin || (parcours->numero<=fin))) { + if ((*action)(parcours,&row,previous_subject,level,&courant)) { + if(menu) Libere_menu_noms(menu); /* ne doit jamais arriver */ + return NULL; + } + if ((courant) && (!menu)) menu=courant; + if ((courant) && (!start) && (parcours==Article_courant)) start=courant; + if ((!Options.duplicate_subject) && (parcours->headers)) + previous_subject=parcours->headers->k_headers[SUBJECT_HEADER]; + parcours->flag &= ~FLAG_ACTION; + if (thread || !Options.ordered_summary) { + parcours=next_in_thread(parcours,FLAG_ACTION,&level,deb,fin, + FLAG_ACTION); + } + if (!parcours || !(parcours->flag & FLAG_ACTION)) { + while(parcours2 && (!(parcours2->flag & FLAG_ACTION))) + parcours2=parcours2->next; + parcours=parcours2; level=1; + } + } + /* on jouait avec un menu */ + if(menu) { + parcours=Menu_simple(menu, start, NULL, NULL); + Libere_menu_noms(menu); + return parcours; + } + return NULL; +} + +/* Gestion de la commande 'r' */ +/* on affiche les messages taggues entre deb et fin */ +/* deb=fin=0 signifie qu'on a fait r entree */ +static void Aff_summary (int deb, int fin, int thread) { + raw_Do_summary(deb,fin,thread,Do_aff_summary_line); +} + +Article_List * Menu_summary (int deb, int fin, int thread) { + return raw_Do_summary(deb,fin,thread,Do_menu_summary_line); +} +static int default_thread(Article_List *article, void *flag) +{ + Numeros_List blah; + blah.next=NULL; + blah.flags=8; + blah.num1=article->numero; + distribue_action(&blah,(Action) check_and_tag_article,NULL,flag); + return 0; +} +static int default_gthread(Article_List *article, void *flag) +{ return default_thread(root_of_thread(article,0), flag); +} +static int default_summary(Article_List *article, void *flag) +{ + Numeros_List blah; + Article_List *parcours, *parcours2; + int count=Screen_Rows - 2* Options.skip_line - 3; + blah.next=NULL; + blah.flags=2; + parcours=article; + parcours2=article; + if (check_and_tag_article(parcours,flag)) count --; + while ( count != 0) { + if ((count) && (parcours->prev)) { + parcours=parcours->prev; + if (check_and_tag_article(parcours,flag)) count --; + } + if ((count) && (parcours2->next)) { + parcours2=parcours2->next; + if (check_and_tag_article(parcours2,flag)) count --; + } + if (!parcours->prev && !parcours2->next) break; + } + return 0; +} + +int do_summary(int res) { + Numeros_List *courant=&Arg_do_funcs; + int result; + Action defact=NULL; + Action act=NULL; + flrn_filter *filt; + Article_List *ret=NULL; + char *buf=Arg_str; + + filt=new_filter(); + filt->action.flag=FLAG_ACTION; + act=check_and_tag_article; + if (Arg_str && *Arg_str) { + while(*buf==' ') buf++; + if (parse_filter_flags(Arg_str,filt)) + parse_filter(Arg_str,filt); + } + switch(res) { + case FLCMD_MENUTHRE: + case FLCMD_THRE: act=defact=default_thread; + break; + case FLCMD_MENUGTHR: + case FLCMD_GTHR: act=defact=default_gthread; + break; + default : defact=default_summary; + } + result = distribue_action(courant, act, defact, (void *)filt); + free_filter(filt); + switch(res) { + case FLCMD_MENUTHRE: + case FLCMD_MENUSUMM: + case FLCMD_MENUGTHR: + case FLCMD_MENUSUMMS: + ret=Menu_summary(0,0,((res==FLCMD_MENUTHRE)||(res==FLCMD_MENUGTHR))?1:0); + break; + default: + Aff_summary(0,0,((res==FLCMD_THRE)||(res==FLCMD_GTHR))?1:0); + } + etat_loop.etat=3; + if(ret) { + Article_courant=ret; + etat_loop.etat=0; + } + return 0; +} + +static int Sauve_article(Article_List *a_sauver, void *fichier); + +/* do_save : on va essayer de voir... */ +int do_save(int res) { + FILE *fichier; + char *name=Arg_str; + int ret, key, use_argstr=0; + struct stat status; + Numeros_List *courant=&Arg_do_funcs; + + while ((*name) && (isblank(*name))) name++; + if (name[0]=='\0') { + use_argstr=1; + name=safe_malloc(MAX_PATH_LEN*sizeof(char)); + name[0]='\0'; + Aff_fin("Sauver dans : "); + if ((ret=getline(name, MAX_PATH_LEN, Screen_Rows-1,14)<0)) { + free(name); + etat_loop.etat=3; + return 0; + } + } + if (stat(name,&status)==0) { + if (!Options.use_mailbox) + Aff_fin("Ecraser le fichier ? "); + else Aff_fin("Ajouter au folder ? "); + key=Attend_touche(); + if ((KeyBoard_Quit) || (strchr("yYoO", key)==NULL)) { + if (use_argstr) free(name); + etat_loop.etat=3; + return 0; + } + } + if (!Options.use_mailbox) + fichier=fopen(name,"w"); + else fichier=fopen(name,"a"); + if (fichier==NULL) { + if (use_argstr) free(name); + etat_loop.etat=2; etat_loop.num_message=-6; + return 0; + } + if (res==FLCMD_SAVE) + distribue_action(courant,Sauve_article,NULL ,fichier); + else { + Action_with_args beurk; + + beurk.action=Sauve_article; + beurk.flag=fichier; + distribue_action(courant,grand_distribue, NULL, &beurk); + } + fclose(fichier); + etat_loop.etat=1; etat_loop.num_message=8; + if (use_argstr) free(name); + return 0; +} + +/* do_launch_pager : voisin de do_save */ +int do_launch_pager(int res) { + FILE *fichier; + Numeros_List *courant=&Arg_do_funcs; + int fd; + + fd=Pipe_Msg_Start(1,0,NULL); + fichier=fdopen(fd,"w"); + if (fichier==NULL) { + etat_loop.etat=2; etat_loop.num_message=-6; + if (fd >=0) + Pipe_Msg_Stop(fd); + return 0; + } + distribue_action(courant,Sauve_article,NULL,fichier); + + if (fd>0) fclose(fichier); + Pipe_Msg_Stop(fd); + etat_loop.etat=3; + return 0; +} + +/* lit le fichier temporaire créé par un filtre */ +/* appelée par do_pipe */ +int display_filter_file(char *cmd, int flag) { + FILE *file; + char name[MAX_PATH_LEN]; + char *home; + char prettycmd[MAX_CHAR_STRING]; + if (NULL == (home = getenv ("FLRNHOME"))) + home = getenv ("HOME"); + if (home==NULL) return -1; /* TRES improbable :-) */ + strncpy(name,home,MAX_PATH_LEN-2-strlen(TMP_POST_FILE)); + strcat(name,"/"); strcat(name,TMP_POST_FILE); + file=fopen(name,"r"); + if (file == NULL) { + etat_loop.etat=2; etat_loop.num_message=-11; + return 0; + } + if (flag) strcpy(prettycmd,"! "); + else strcpy(prettycmd,"| "); + strncat(prettycmd,cmd,MAX_CHAR_STRING-10); + strcat(prettycmd," : "); + Aff_file(file,NULL,NULL); /* c'est tres moche */ + fclose(file); + Aff_fin(prettycmd); + Attend_touche(); + etat_loop.etat=1; etat_loop.num_message=14; + return 0; +} + +/* do_pipe : on va essayer de voir... */ +int do_pipe(int res) { + FILE *fichier; + char *name=Arg_str; + int ret, use_argstr=0; + Numeros_List *courant=&Arg_do_funcs; + int fd; + + while ((*name) && (isblank(*name))) name++; + if (name[0]=='\0') { + use_argstr=1; + name=safe_malloc(MAX_PATH_LEN*sizeof(char)); + name[0]='\0'; + Aff_fin("Piper dans : "); + if ((ret=getline(name, MAX_PATH_LEN, Screen_Rows-1,14)<0)) { + free(name); + etat_loop.etat=3; + return 0; + } + } + fd=Pipe_Msg_Start((res!=FLCMD_SHELLIN) && (res!=FLCMD_SHELL), + (res == FLCMD_FILTER ) || (res == FLCMD_GFILTER) + || (res == FLCMD_SHELLIN), name); + if (fd<0) { + if (use_argstr) free(name); + etat_loop.etat=2; etat_loop.num_message=-11; + return 0; + } + fichier=fdopen(fd,"w"); + if (fichier==NULL) { + if (use_argstr) free(name); + etat_loop.etat=2; etat_loop.num_message=-11; + return 0; + } + if ((res==FLCMD_PIPE) || (res==FLCMD_FILTER)) + distribue_action(courant,Sauve_article,NULL ,fichier); + else { + Action_with_args beurk; + beurk.action=Sauve_article; + beurk.flag=fichier; + if ((res != FLCMD_SHELL) && (res != FLCMD_SHELLIN)) + distribue_action(courant,grand_distribue, NULL, &beurk); + } + if (fd>0) fclose(fichier); + Pipe_Msg_Stop(fd); + if ((res == FLCMD_FILTER ) || (res == FLCMD_GFILTER) + || (res == FLCMD_SHELLIN)) + display_filter_file(name, res == FLCMD_SHELLIN); + etat_loop.etat=1; etat_loop.num_message=14; + if (use_argstr) free(name); + return 0; +} + + +/* do_post : lance post */ +int do_post(int res) { + Article_List *origine; + char *str=Arg_str; + int ret; + + if ((res==FLCMD_ANSW) || (res==FLCMD_MAIL) || (res==FLCMD_SUPERSEDES)) { + origine=Article_courant; + if (Arg_do_funcs.flags!=0) + ret=Recherche_article(Arg_do_funcs.num1,&origine,0); + else ret=(origine ? 0 : -2); + if (ret<0) { + etat_loop.etat=1; etat_loop.num_message=3; return 0; + } + } else origine=NULL; + /* Provisoirement, je ne m'occupe pas de la chaine de caractère */ + ret=post_message(origine, str, (res==FLCMD_MAIL ? 1 : + (res==FLCMD_SUPERSEDES ? -1 : 0))); + if (ret==1) { etat_loop.etat=1; etat_loop.num_message=6; } else + if (ret==0) { etat_loop.etat=1; etat_loop.num_message=7; } else + etat_loop.etat=3; + return 0; +} + + +/* do_cancel : cancel (SI !) */ +static int cancel_article(Article_List *article, void *toto) + { return cancel_message(article); } +int do_cancel(int res) { + Numeros_List *courant=&Arg_do_funcs; + int ret; + + ret=distribue_action(courant,cancel_article,NULL,NULL); + if (ret>=0) { + etat_loop.etat=1; etat_loop.num_message=(ret==0 ? 16 : 17); + } else { + etat_loop.etat=2; etat_loop.num_message=-14; + } + return 0; +} + + +/* do_opt */ +int do_opt(int res) { + Get_option_line(Arg_str); + etat_loop.etat=3; + return 0; +} + +int do_opt_menu(int res) { + menu_config_variables(); + etat_loop.etat=1; + etat_loop.num_message=14; + return 0; +} + +int do_neth(int res) { return 0; } + + +/* Affiche la liste des newsgroup */ +/* res=FLCMD_GLIS -> liste tout */ +int do_list(int res) { + char *gpe=Arg_str; + int ret; + + ret=Liste_groupe((res==FLCMD_GLIS)?2:0, gpe); + if (ret<0) { etat_loop.etat=2; etat_loop.num_message=-10; } + else etat_loop.etat=3; + etat_loop.hors_struct |= 1; + return 0; +} + +/* Sauve l'article a_sauver dans fichier */ +static int Sauve_article(Article_List *a_sauver, void *vfichier) { + time_t date; + FILE *fichier = (FILE *) vfichier; + + date=time(NULL); + if (Options.use_mailbox) + fprintf(fichier, "From %s%s %s", flrn_user->pw_name,"@localhost", + ctime(&date)); else + fprintf(fichier, "Newsgroup : %s No : %d\n", Newsgroup_courant->name, + a_sauver->numero); + Copy_article(fichier, a_sauver, 1, NULL); + fprintf(fichier,"\n"); + return 0; +} + + +/* Change de newsgroup pour récuperer le même article */ +/* On va essayer de faire quelque chose d'assez complet */ +/* pour l'instant, on ne gère pas une regexp */ +int do_swap_grp(int res) { + Article_List *article_considere; + char *newsgroup, *buf, *num; + int numero; + Newsgroup_List *mygroup; + + article_considere=Article_courant; + if (Arg_do_funcs.flags!=0) + Recherche_article(Arg_do_funcs.num1, &article_considere, 0); + if (article_considere==NULL) { + etat_loop.etat=1; etat_loop.num_message=3; return 0; + } + + if ((!article_considere->headers) || + (article_considere->headers->k_headers_checked[XREF_HEADER] == 0)) { + cree_header(Article_courant,0,1); + if (!article_considere->headers) { + etat_loop.etat=1; etat_loop.num_message=3; return 0; + } + } + if (!article_considere->headers->k_headers[XREF_HEADER]) { + etat_loop.etat=1; etat_loop.num_message=13; return 0; + } + /* Bon, faut que je parse le Xref... ça a déjà été fait dans article_read */ + buf=safe_strdup(article_considere->headers->k_headers[XREF_HEADER]); + newsgroup=strtok(buf," "); + while ((newsgroup=strtok(NULL," :"))) { + num=strtok(NULL, ": "); + if (!num) {free(buf); etat_loop.etat=1; etat_loop.num_message=3; return 0;} + numero=atoi(num); + if (strcmp(Newsgroup_courant->name,newsgroup)==0) continue; + if (strstr(newsgroup, Arg_str)) { /* et si Arg_str=="" ? */ + mygroup=Newsgroup_deb; + while (mygroup && (strcmp(mygroup->name, newsgroup))) + mygroup=mygroup->next; + if (mygroup==NULL) continue; + etat_loop.Newsgroup_nouveau=mygroup; + etat_loop.num_futur_article=numero; + free(buf); + return 1; + } + } + etat_loop.etat=1; etat_loop.num_message=12; free(buf); return 0; +} + + +/* zap thread */ +/* all >0 si l'on veut "tuer" aussi les cousins */ +/* met le flag flag si zap >0, le retire sinon */ +void zap_thread(Article_List *article,int all, int flag,int zap) +{ + Article_List *racine=article; + int level=0; + if (all) + racine = root_of_thread(racine,0); + if (zap) { + if (flag == FLAG_READ) article_read(racine); else + racine->flag |= flag; + while((racine=next_in_thread(racine,flag,&level,0,0,0))) + if (flag == FLAG_READ) article_read(racine); else + racine->flag |= flag; + } + else { + racine->flag &= ~flag; + while((racine=next_in_thread(racine,flag,&level,0,0,flag))) + racine->flag &= ~flag; + } + return; +} + +/* passe au prochain thread ou il y a un message non lu */ +/* renvoie -1 s'il n'y a rien d'autre */ +int next_thread(int flags) { + Article_List *racine=Article_deb; + while(racine->next) { + racine=racine->next; + racine->flag &= ~FLAG_DISPLAYED; + } + racine = Article_courant; + zap_thread(racine, 1, FLAG_DISPLAYED,1); + while(racine && (racine->flag & (FLAG_DISPLAYED | FLAG_READ))) + { racine = racine->next; } + if (racine) { Article_courant=racine; return 0; } + racine = Article_deb; + while(racine != Article_courant && + (racine->flag & (FLAG_DISPLAYED | FLAG_READ))) + { racine = racine->next; } + if (!(racine->flag & (FLAG_DISPLAYED | FLAG_READ))) + { Article_courant=racine; return 0; } + return -1; +} + +void Get_option_line(char *argument) +{ + int res=0, use_arg=1; + char *buf=argument; + int color=0; + while (isblank(*buf)) buf++; + if (*buf=='\0') { + use_arg=0; + buf = safe_malloc(MAX_BUF_SIZE); + *buf='\0'; + Aff_fin("Option: "); + do { + if (res>0) {Cursor_gotorc(Screen_Rows-1,8); Screen_erase_eol(); + Screen_write_string(buf); + } + if ((res=magic_getline(buf,MAX_BUF_SIZE,Screen_Rows-1,8, + "\011",0))<0) { + free(buf); + return; + } + if (res>0) + options_comp(buf,MAX_BUF_SIZE); + } while (res!=0); + } + /* hack pour reconstruir les couleurs au besoin */ + color=(strstr(buf,"color"))?1:0; + color=(strstr(buf,"mono"))?1:color; + parse_options_line(buf,1); + if (color) { + Init_couleurs(); + Screen_touch_lines (0, Screen_Rows-1); + } + if (!use_arg) free(buf); + return; +} + +/* Action de Menu_simple pour le goto */ +void Ligne_carac_du_groupe (void *letruc, char *lachaine, + int taille) +{ + Newsgroup_List *legroupe=letruc; + lachaine[0]='\0'; + if (legroupe->description) strncat(lachaine,legroupe->description,taille-1); + lachaine[taille-1]='\0'; +} + +/* change de groupe */ +/* les flags correspondent à abonné / tout */ +/* Retourne 0 en cas de succes, -1 si aucun changement */ +/* et -2 si on demande un groupe inexistant */ +/* On retourne exceptionnelement 1 dans le cas ou la chaine demandee */ +/* est vide. */ +int change_group(Newsgroup_List **newgroup, int flags, char *gpe_tab) +{ + char *gpe=gpe_tab; + Newsgroup_List *mygroup=Newsgroup_courant; + regex_t reg; + int avec_un_menu=Options.use_menus, correct; + Liste_Menu *lemenu=NULL, *courant=NULL; + + while (*gpe==' ') gpe++; + if (debug) fprintf(stderr,"\nG : %s\n",gpe); + if (*gpe=='\0') return 1; + + if (Options.use_regexp) { + if(regcomp(®,gpe,REG_EXTENDED|REG_NOSUB)) {return -2;} } + + /* Si on a GGTO et qu'en plus on utilise les menus, on fait tout de suite */ + /* une requete... */ + if (flags && avec_un_menu) { + if (Options.use_regexp) { + char *mustmatch; + mustmatch=reg_string(gpe,1); + if (mustmatch!=NULL) lemenu=menu_newsgroup_re(mustmatch, reg,1); + else { + regfree(®); + free(mustmatch); + return -2; + } + free(mustmatch); + } else lemenu=menu_newsgroup_re(gpe,reg, 0); + /* On copie n'importe quoi pour reg et c'est pas bo */ + } else { + /* premiere passe pour voir si on trouve ca apres */ + if (mygroup) + do { + mygroup=mygroup->next; + /* On sépare les tests du while pour éviter une ligne trop longue */ + if (mygroup==NULL) break; + correct=((Options.use_regexp && !regexec(®,mygroup->name,0,NULL,0)) + || (!Options.use_regexp && strstr(mygroup->name,gpe))) && + (flags || !(mygroup->flags & GROUP_UNSUBSCRIBED)); + if (correct && avec_un_menu) { + courant=ajoute_menu(courant,mygroup->name,mygroup); + if (lemenu==NULL) lemenu=courant; + correct=0; + } + } while (!correct); + + if (mygroup==NULL) mygroup=Newsgroup_deb; + /* deuxieme passe en repartant du debut si on n'a pas trouve */ + while (mygroup!=Newsgroup_courant) { + correct=((Options.use_regexp && !regexec(®,mygroup->name,0,NULL,0)) + || (!Options.use_regexp && strstr(mygroup->name,gpe))) && + (flags || !(mygroup->flags & GROUP_UNSUBSCRIBED)); + if (correct && avec_un_menu) { + courant=ajoute_menu(courant,mygroup->name,mygroup); + if (lemenu==NULL) lemenu=courant; + correct=0; + } + if (correct) break; + mygroup=mygroup->next; + } + + if ((mygroup==Newsgroup_courant) && (!avec_un_menu) && + ((mygroup==NULL) || + ((!Options.use_regexp || regexec(®,mygroup->name,0,NULL,0)) + && (Options.use_regexp || !strstr(mygroup->name,gpe))))) + if (flags) { + if (debug) fprintf(stderr, "On va appeler cherche_newsgroup\n"); + /* on recupere la chaine minimale de la regexp */ + + if (Options.use_regexp) { + char *mustmatch; + mustmatch=reg_string(gpe,1); + if (mustmatch!=NULL) { + if ((mygroup=cherche_newsgroup_re(mustmatch,reg))==NULL) { + if (debug) fprintf (stderr,"Le motif %s ne correspond a aucun groupe\n",gpe); + free(mustmatch); + regfree(®); + return -2; + } + } else { + *newgroup=mygroup; + regfree(®); + free(mustmatch); + return 0; + } + free(mustmatch); + } else { + if ((mygroup=cherche_newsgroup(gpe,0))==NULL) { + if (debug) fprintf (stderr,"Le motif %s ne correspond a aucun groupe\n",gpe); + return -2; + } else { + *newgroup=mygroup; + return 0; + } + } + } else { + if (debug) fprintf (stderr,"Le motif %s ne correspond a aucun groupe\n",gpe); + if (Options.use_regexp) regfree(®); + return -2; + } + } + if (lemenu) { + if (lemenu->suiv==NULL) mygroup=lemenu->lobjet; + else + mygroup=Menu_simple(lemenu,NULL,Ligne_carac_du_groupe,NULL); + if (mygroup==NULL) mygroup=Newsgroup_courant; + Libere_menu(lemenu); + } else if (avec_un_menu) { + if (debug) fprintf (stderr,"Le motif %s ne correspond a aucun groupe\n",gpe); + if (Options.use_regexp) regfree(®); + return -2; + } + + *newgroup=mygroup; + if (debug && *newgroup) + fprintf(stderr, "Nouveau groupe : %s\n", (*newgroup)->name); + if (Options.use_regexp) regfree(®); + return (mygroup==Newsgroup_courant ? -1 : 0); /* on a bien fait le changement */ +} + + +/* Prend le prochain article non lu. Renvoie : */ +/* 0 : Ok, ou force_reste=1... */ +/* -1 : pas de nouveaux articles */ +/* On modifie prochain_non_lu pour qu'il fasse appel a chercher_mewnews */ +/* quand il n'y a rien de nouveau... */ +/* Et eventuellement l'appel a ajoute_message_par_num si posts récents */ +/* Renvoie donc en plus : */ +/* -2 : reconstruire le groupe... */ +int prochain_non_lu(int force_reste, Article_List **debut) { + Article_List *myarticle=*debut; + int res; + + /* on regarde si l'article courant est lu */ + if (myarticle && !(myarticle->flag & FLAG_READ)) + return 0; + + /* On essaie d'abord de chercher dans la thread */ + if( Options.threaded_space) { + myarticle=next_in_thread(myarticle,FLAG_READ,NULL,0,0,0); + if (myarticle) { *debut=myarticle; return 0; } + myarticle=*debut; + } + /* On teste d'abord ce qu'on trouve après Article_courant */ + while (myarticle && (myarticle->flag & FLAG_READ)) + myarticle=myarticle->next; + if (myarticle) { *debut=myarticle; return 0; } + + /* Puis on repart de Article_deb */ + myarticle=Article_deb; + while (myarticle && (myarticle!=*debut) && (myarticle->flag & FLAG_READ)) + myarticle=myarticle->next; + if (myarticle==NULL) myarticle=Article_deb; + if ((myarticle->flag & FLAG_READ)==0) + { *debut=myarticle; return 0; } + + /* Si on a rien trouvé, un appel a cherche_newnews ne fait pas de mal */ + res=cherche_newnews(); + if (res==-2) return -2; + if (res>=1) return prochain_non_lu(force_reste, debut); + + /* On fixe Article_courant au dernier article dans tous les cas */ + while (myarticle->next) myarticle=myarticle->next; + *debut=myarticle; + if (force_reste) + return 0; + + return -1; +} + +/* Prend le prochain newsgroup interessant. Renvoie : */ +/* 0 : Ok... */ +/* -1 : rien de nouveau */ +/* -2 : erreur */ +int prochain_newsgroup(Newsgroup_List **newgroup ) { + Newsgroup_List *mygroup=Newsgroup_courant, *last_mygroup; + int res; + + /* On teste d'abord strictement APRES Newsgroup_courant */ + if (mygroup) + mygroup=mygroup->next; + while (mygroup) { + res=0; + if (!(mygroup->flags & GROUP_UNSUBSCRIBED )) { + res=NoArt_non_lus(mygroup); + if (res>0) break; + } + last_mygroup=mygroup; + /* Plutot indispensable si on fait un zap_newsgroup */ + mygroup=mygroup->next; + if (res==-2) zap_newsgroup(last_mygroup); + if (res==-1) return -2; + } + + if (mygroup) { *newgroup=mygroup; return 0; } + + /* Puis on prend AVANT Newsgroup_courant, en NE testant PAS */ + /* celui-ci */ + mygroup=Newsgroup_deb; + while (mygroup!=Newsgroup_courant) { + res=0; + if (!(mygroup->flags & GROUP_UNSUBSCRIBED)) { + res=NoArt_non_lus(mygroup); + if (res>0) break; + } + last_mygroup=mygroup; + mygroup=mygroup->next; + if (res==-2) zap_newsgroup(last_mygroup); + if (res==-1) return -2; + } + + if (mygroup!=Newsgroup_courant) { *newgroup=mygroup; return 0; } + + return -1; +} + +/* Applique action sur tous les peres de l'article donne*/ +int parents_action(Article_List *article,int all, Action action, void *param) { + Article_List *racine=article; + while(racine->pere && !(racine->pere->flag & FLAG_TAGGED)) { + racine->flag |= FLAG_TAGGED; + racine=racine->pere; + } + do { + action(racine,param); + racine->flag &= ~FLAG_TAGGED; + } while((racine=next_in_thread(racine,FLAG_TAGGED,NULL,0,0, + FLAG_TAGGED))); + return 0; +} + + +/* Applique action(flag) sur tous les articles du thread + * On utilise en interne FLAG_DISPLAYED + * Attention a ne pas interferer + * all indique qu'il faut d'abord remonter a la racine */ +int thread_action(Article_List *article,int all, Action action, void *param) { + Article_List *racine=article; + Article_List *racine2=article; + int res=0; + int res2=0; + int level=0; + /* On remonte a la racine si besoin */ + if (all) + racine = root_of_thread(racine,0); + racine2=racine; + /* On retire le FLAG_DISPLAYED a tout ceux qui l'ont dans le thread + * C'est oblige car l'etat par defaut n'est pas defini + * On le change dans la boucle pour que next_in_thread ne cycle pas */ + racine->flag &= ~FLAG_DISPLAYED; + while((racine=next_in_thread(racine,FLAG_DISPLAYED,&level,0,0, + FLAG_DISPLAYED))) + racine->flag &= ~FLAG_DISPLAYED; + racine=racine2; + /* Et la on peut faire ce qu'il faut */ + do { res2=action(racine,param); + if (res2flag |= FLAG_DISPLAYED; + } while ((racine=next_in_thread(racine,FLAG_DISPLAYED,&level,0,0, + 0))); + return res; +} + + +/* Appelle action sur tous les articles de Numero_List */ +int distribue_action(Numeros_List *num, Action action, Action special, + void *param) { + Article_List *parcours=NULL; + int res,result=0,res2; + if (num->flags==0) { + if (!Article_courant) return -1; + if (special) return special(Article_courant,param); + return action(Article_courant,param); + } + while (num) { + if (num->flags==0) {return 0;} + res=Recherche_article(num->num1,&parcours,1); + res2=0; + switch (num->flags) { + case 1: if (res==0) res2=action(parcours,param); + break; + case 2: if ((res==0) || (res==-1)) { + while(parcours) { + if (parcours->numero > num->num2) break; + res2=action(parcours,param); + if (res2next; + } + } + break; + case 4: if (res==0) res2=parents_action(parcours,0,action,param); + break; + case 8: if (res==0) res2=thread_action(parcours,0,action,param); + break; + default: return -1; + } + if (res2next; + } + return result; +} + +void save_etat_loop() { + memcpy(&etat_save,&etat_loop,sizeof(etat_loop)); +} +void restore_etat_loop() { + memcpy(&etat_loop,&etat_save,sizeof(etat_loop)); +} diff --git a/src/flrn_inter.h b/src/flrn_inter.h new file mode 100644 index 0000000..7ae1d1d --- /dev/null +++ b/src/flrn_inter.h @@ -0,0 +1,27 @@ +#ifndef FLRN_INTER_H +#define FLRN_INTER_H + +#include "art_group.h" +#include "flrn_config.h" + +typedef struct command_desc { + char *nom; + int key; + int key_nm; + int flags; /* 1 : chaine de caractère a prendre + 2 : possibilites d'articles + 4 : demande une chaine dans l'ancien mode + 8 : demande une chaine dans le nouveau mode aussi + 16: demande à avoir un groupe valide */ +#define CMD_NEED_GROUP 16 + int (*appel)(int); +} Flcmd; + +typedef struct { + long article_deb_key; + Article_List *article; + long numero; + char *newsgroup_name; /*[MAX_NEWSGROUP_LEN + 1];*/ +} Flrn_Tag; +#endif + diff --git a/src/flrn_menus.c b/src/flrn_menus.c new file mode 100644 index 0000000..7b96108 --- /dev/null +++ b/src/flrn_menus.c @@ -0,0 +1,187 @@ +/* flrn v 0.1 */ +/* flrn_menus.c 15/05/98 */ +/* */ +/* Routines de manipulation de menus pour une interface méga-torche. */ +/* C'est très proche des librairies I/O, mais on va essayer de faire */ +/* un truc indépendant desdites librairies. */ + +#include +#include "flrn.h" +#include "options.h" +#include "flrn_config.h" +#include "flrn_menus.h" +#include "flrn_inter.h" + +/* Cette fonction ne renvoie qu'un élément sélectionné, ou bien entendu NULL. + * action est appelée quand le curseur est devant un item. Elle recoit une + * ligne dont la longueur est passée avec laquel elle peut jouer. + * action_select est appelée quand on fait return. Elle recoit le l'élément, + * son nom, son numéro (a partir de 0 !), et la même ligne que action. + * si elle est vaut NULL, on quite simplement. On quite si elle renvoie 1. + * */ +void *Menu_simple (Liste_Menu *debut_menu, Liste_Menu *actuel, + void (action)(void *,char *,int), + int (action_select)(void *,char **, int,char *,int)) { + + int act_row=1+Options.skip_line, num_elem=0, num_courant=0; + int correct=0; /* Pour savoir si on s'arrete ou pas... */ + Liste_Menu *courant=actuel, *parcours=debut_menu; + int avant=1; + char *Une_Ligne; + int Une_Ligne_len; + + if (parcours==NULL) return NULL; /* Ouais ouais ouais... */ + Une_Ligne=safe_malloc(Une_Ligne_len=(Screen_Cols +1)); + +/* Il va falloir créer un scrolling pour ce menu, on commence donc par + * remplir Text_scroll... + * A priori, cette partie-ci pourra être recopiée pour toutes les + * fonctions de menus, moyennant quelques modifications. + * Faut-il vraiment faire un scrolling quand ce n'est pas nécessaire... + * Faudra voir si on ne peut pas s'en passer, pour économiser la + * mémoire. */ + while (parcours) { + + strcpy(Une_Ligne," "); + strncat(Une_Ligne,parcours->nom,Une_Ligne_len); + Ajoute_line(Une_Ligne); /* Ajoute_line est peut-être un peu court */ + /* si on veut rajouter des signes... */ + if (parcours==actuel) avant=0; + if (avant) { act_row++; num_courant++; } + num_elem++; + parcours=parcours->suiv; + } +/* Si le courant n'est pas défini, on prend le premier élément... */ + if (avant) { + num_courant=0; + act_row=1+Options.skip_line; + courant=debut_menu; + } +/* Puis on initialise le scroll... */ + Init_Scroll_window(num_elem, 1+Options.skip_line, + Screen_Rows-3*Options.skip_line-2); +/* Et on fait l'update... */ +/* Sans oublier d'effacer l'écran d'abord... */ + Cursor_gotorc(1,0); + Screen_erase_eos(); + if (act_rowScreen_Rows-3-Options.skip_line)) { + p=Do_Scroll_Window(act_row-(Screen_Rows-1)/2,0); + act_row-=p; + } + if (action && courant && !no_change_last_line) { + Cursor_gotorc(Screen_Rows-2,0); + *Une_Ligne='\0'; + action(courant->lobjet,Une_Ligne, Une_Ligne_len); + Screen_write_string(Une_Ligne); + Screen_erase_eol(); + } else if (!no_change_last_line) { + Cursor_gotorc(Screen_Rows-2,0); + Screen_erase_eol(); + } + Cursor_gotorc(act_row,0); + Screen_write_char('>'); + no_change_last_line=0; + key=Attend_touche(); + Cursor_gotorc(act_row,0); + Screen_write_char(' '); + switch (Flcmd_rev[key]) { + case FLCMD_PREC : + case FLCMD_UP : if (courant->prec!=NULL) { + act_row--; num_courant--; courant=courant->prec; } + break; + case FLCMD_DOWN : if (courant->suiv!=NULL) { + act_row++; num_courant++; courant=courant->suiv; } + break; + case FLCMD_QUIT : courant=NULL; + correct=1; break; + case FLCMD_SUIV : if(action_select && courant) { + /* on appelle l'action */ + *Une_Ligne='\0'; + if (!(correct=action_select(courant->lobjet, + &courant->nom,num_courant,Une_Ligne,Une_Ligne_len))) { + /* on met a jour la ligne */ + Cursor_gotorc(Screen_Rows-2,0); + Screen_write_string(Une_Ligne); + Screen_erase_eol(); + no_change_last_line=1; + strcpy(Une_Ligne," "); + strncat(Une_Ligne,courant->nom,Une_Ligne_len); + Change_line(num_courant,Une_Ligne); + /* on la réaffiche */ + Cursor_gotorc(act_row,0); + Screen_write_string(Une_Ligne); + Screen_erase_eol(); + } + } + else correct=1; + break; + } + } + } + free_text_scroll(); + free(Une_Ligne); + return (courant ? courant->lobjet : NULL); +} + + +/* Ici, on libere un menu alloue... c'est assez facile normalement */ +/* ON NE LIBERE QUE LA STRUCTURE EXTERNE DU MENU... SI LES OBJETS */ +/* POINTES SONT TEMPORAIRES AUSSI, IL FAUT LES LIBERER AVANT !!! */ +void Libere_menu (Liste_Menu *debut) { + + while (debut) { + if (debut->suiv) { + debut=debut->suiv; + free(debut->prec); + } else { + free(debut); + debut=NULL; + } + } +} + +void Libere_menu_noms (Liste_Menu *debut) { + Liste_Menu *p=debut; + while (debut) { + free(debut->nom); + debut=debut->suiv; + } + Libere_menu(p); +} + +/* Ajoute un élément au menu dont base est le dernier élément */ +Liste_Menu *ajoute_menu(Liste_Menu *base, char *nom, void *lobjet) { + Liste_Menu *creation; + + creation=safe_malloc(sizeof(Liste_Menu)); + creation->suiv=NULL; + creation->prec=base; + if (base) + base->suiv=creation; + creation->nom=nom; + creation->lobjet=lobjet; + return creation; +} + +/* -1 commande non trouvee */ +/* -2 touche invalide */ +/* -3 touche verrouillee */ +/* -4 plus de macro disponible */ +int Bind_command_menu(char *nom, int key, char *arg) { + return -1; +} diff --git a/src/flrn_menus.h b/src/flrn_menus.h new file mode 100644 index 0000000..eb174a8 --- /dev/null +++ b/src/flrn_menus.h @@ -0,0 +1,15 @@ + +#ifndef FLRN_MENUS_H +#define FLRN_MENUS_H + +/* La structure utilisée est pour l'instant celle-ci. Je ne sais pas si */ +/* c'est la meilleure, mais bon... On verra bien. Peut-être rajouter un */ +/* lien vers une fonction d'expression de l'objet... Ou non, le mieux */ +/* est de le passer en paramètre de la fonction de menu. */ +typedef struct liste_menu_desc { + char *nom; /* En read-only, peut être un pointeur vers n'importe ou */ + void *lobjet; /* Ceci n'est pas libéré non plus !!! */ + struct liste_menu_desc *prec, *suiv; +} Liste_Menu; + +#endif diff --git a/src/flrn_pager.c b/src/flrn_pager.c new file mode 100644 index 0000000..8f9a08b --- /dev/null +++ b/src/flrn_pager.c @@ -0,0 +1,142 @@ +/* flrn v 0.3 */ +/* flrn_pager.c 22/06/98 */ +/* */ +/* Un VRAI pager pour flrn, un jour... */ +/* */ + +#include +#include +#include + +#include "flrn.h" +#include "options.h" +#include "flrn_pager.h" + +/* le tableau touche -> commande */ +int Flcmd_pager_rev[MAX_FL_KEY]; +/* pour les macros */ + +#define MAX_FL_MACRO_PAGER 32 /* Pas exagerer non plus */ +struct { + int cmd; + char *arg; +} Flcmd_macro_pager[MAX_FL_MACRO_PAGER]; + +int Flcmd_num_macros_pager=0; + + +/* Page_message */ +/* La fonction principale du pager */ +/* Init_Scroll_window a déjà été appelé. Si key!=0, c'est la première touche */ +/* frappée... Si short_exit=1, on sort en fin de scrolling, ou si un touche */ +/* n'est pas reconnue... num_elem est le nombre de lignes à scroll... */ +/* act_row est la première ligne du scroll à afficher, row_deb la première */ +/* ligne dans le cas du premier affichage... */ +/* si key==0, on affiche le premier écran... */ +/* Si exit_chars est différent de NULL, tout appui sur une des touches de la */ +/* chaine provoque la sortie... Si il est NULL, on sort toujours en fin de */ +/* message... */ +/* On renvoie le code de la touche de sortie (-1 si ^C ou FLCMD_PAGER_QUIT). */ +/* On renvoie 0 si on est sorti par fin de l'ecran... */ + +int Page_message (int num_elem, int short_exit, int key, int act_row, + int row_deb, char *exit_chars, char *end_mesg) { + int percent, nll, deb=1, le_scroll, at_end; + char buf3[15]; + + at_end=(num_elem=num_elem) { + if (short_exit || (exit_chars==NULL)) return 0; + else { + if (end_mesg) Aff_fin(end_mesg); else Aff_fin("(100%%)-more-"); + } + at_end=1; + } + else { + at_end=0; + percent=(nll*100)/(num_elem+1); + sprintf(buf3,"(%d%%)",percent); + strcat(buf3,"-More-"); + Aff_fin(buf3); + } + } + deb=0; + key=Attend_touche(); + if (KeyBoard_Quit) return -1; + } +} + + +void init_Flcmd_pager_rev() { + int i; + for (i=0;i= MAX_FL_KEY) + return -2; + if (arg ==NULL) { + if (strncmp(nom, "undef", 5)==0) { + Flcmd_pager_rev[key]=FLCMD_PAGER_UNDEF; + return 0; + } + for (i=0;i +#include +#include +#include +#include + + +#include "flrn.h" + +/* renvoie une chaine qui doit être matchée pour que la regexp donnée + * matche + * flag=1 => possibilite de mettre des * dans la chaine + * Attention : il faudra faire un free sur cette chaine */ + +char *reg_string(char *pattern, int flag) { + char *ptr; + char *result; + int len; + + /* c'est trop complique pour moi */ + if (strchr(pattern,'|')) return NULL; + result = safe_malloc(128); + + len=0; + ptr=pattern; + /* est-ce /^.../ ? */ + if (flag && (*ptr!='^')) result[len++]='*'; + + while(*ptr && (len < 126)) { + /* gestion des \truc */ + if (*ptr=='\\') { + ptr++; + /* c'est un caractère a protéger */ + if (index(".^$*[]+{}()\\",*ptr)) {result[len++]=*ptr; + ptr ++; + continue; + } + else break; /* c'est probablement un truc special */ + } + if (*ptr=='[') { /* on traite [...] comme . et on fait gaffe a []] */ + if (len) { if (flag) result[len++]='*'; else break; } + if (*(++ptr)) ptr++; /* on passe toujours un caractère, pour []] */ + ptr=index(ptr,']'); + if (ptr) {ptr++; continue;} + ptr=pattern; break; + } + if (*ptr=='{') { /* c'est pour nous * ou + */ + if (*(++ptr)=='0') { + if (len) len--; + } + if (len) { + if (flag) result[len++]='*'; else break; + } + ptr=index(ptr,'}'); + if (ptr) {ptr++; continue;} + ptr=pattern; break; + } + if (index("[]{}()",*ptr)) break; /* en fait ca devrait etre que (, les + autres sont gérés avant */ + if (index("*?",*ptr)) {if (len) len --;} + if (index("*?+.",*ptr)) { + if (len) { if (flag) result[len++]='*'; else break; } + } + if (index("^$",*ptr)) { if (len) break; } + if (!index(".^$*?[]+\\{}()",*ptr)) + result[len++]=*ptr; + ptr++; + } + if (flag && (*ptr != '$')) result[len++]='*'; + result[len]='\0'; + if (len) return result; + free(result); + return NULL; +} diff --git a/src/flrn_shell.c b/src/flrn_shell.c new file mode 100644 index 0000000..d6a60f1 --- /dev/null +++ b/src/flrn_shell.c @@ -0,0 +1,143 @@ +/* flrn v 0.3 */ +/* flrn_shell.c 22/06/98 */ +/* */ +/* Gestion du process : fork, system, et execve sont dans ce */ +/* fichier... */ +/* */ + +#include +#include +#include +#include + +#include +#include /* pour le fils */ +#include + +#include "flrn.h" +#include "options.h" + +/* Lancement de l'éditeur sur le fichier à éditer.. */ +/* flag est pour l'instant inutilisé. */ +static volatile int sigwinchcatch; + +void sigwinch_while_fork(int sig) { + sigwinchcatch=1; + /*SL*/signal(SIGWINCH, sigwinch_while_fork); +} + +int Launch_Editor (int flag) { + char name[MAX_PATH_LEN]; + char *home; + char *editor; + pid_t pid; + int retval=0; + + sigwinchcatch=0; + if (NULL == (home = getenv ("FLRNHOME"))) + home = getenv ("HOME"); + if (home==NULL) return -1; /* TRES improbable :-) */ + strncpy(name,home,MAX_PATH_LEN-2-strlen(TMP_POST_FILE)); + strcat(name,"/"); strcat(name,TMP_POST_FILE); + + Reset_keyboard(); + Screen_suspend(); + + signal(SIGTSTP, SIG_DFL); /* on s'en fout */ + signal(SIGWINCH, sigwinch_while_fork); + + editor=getenv("EDITOR"); + if (!editor) { + editor = getenv("VISUAL"); + if (!editor) + editor = "vi"; + } + switch ((pid=fork())) { + case -1 : retval = -1; + break; + case 0 : close(tcp_fd); + execlp(editor, editor, name, NULL); + _exit(-1); + default : while ((wait(NULL)<0) && (errno==EINTR)); + } + + Screen_resume(); + Init_keyboard(); /* Ca remet SIGTSTP correct */ + signal(SIGWINCH, sig_winch); + if (sigwinchcatch) sig_winch(0); + sigwinch_received=0; /* pas nécessaire d'arreter l'édition */ + return retval; +} + +/* ouvre le pipe et renvoie un file descr. */ +/* si flag est !=0, on met la sortie std dans un fichier */ +int Pipe_Msg_Start (int flagin ,int flagout, char *cmdline) { + char name[MAX_PATH_LEN]; + char *home; + pid_t pid; + int fd[2]; + + if (!cmdline) { + cmdline=getenv("PAGER"); + if (!cmdline) + cmdline="less"; + } + if (flagin && (pipe(fd)<0)) return -1; + + if (flagout) { + if (NULL == (home = getenv ("FLRNHOME"))) + home = getenv ("HOME"); + if (home==NULL) return -1; /* TRES improbable :-) */ + strncpy(name,home,MAX_PATH_LEN-2-strlen(TMP_POST_FILE)); + strcat(name,"/"); strcat(name,TMP_POST_FILE); + } + + sigwinchcatch=0; + Reset_keyboard(); + Screen_suspend(); + + signal(SIGTSTP, SIG_DFL); /* on s'en fout */ + signal(SIGWINCH, sigwinch_while_fork); + + switch ((pid=fork())) { + case -1 : break; + case 0 : { + int fdfile; + if (flagin) { + close(tcp_fd); close(fd[1]); + close(0); + if (dup2(fd[0],0)<0) _exit(-1); + } + if (flagout) { + if ((fdfile=open(name,O_CREAT|O_WRONLY|O_TRUNC))<0) + _exit(-1); + if (dup2(fdfile,1)<0) _exit(-1); + } + if (system(cmdline)<0) + _exit(-1); + _exit(0); + } + default : if (flagin) { + close(fd[0]); + return fd[1]; + } else return 0; + } + if (flagin) { + close(fd[0]); + Pipe_Msg_Stop(fd[1]); + } + return -1; +} + +/* ferme le pipe et attend */ +int Pipe_Msg_Stop(int fd) { + if (fd>0) close(fd); + while ((wait(NULL)<0) && (errno==EINTR)); + Screen_resume(); + Init_keyboard(); /* Ca remet SIGTSTP correct */ + signal(SIGWINCH, sig_winch); + if (sigwinchcatch) sig_winch(0); + sigwinch_received=0; /* On n'en a rien a faire... */ + return 0; +} + diff --git a/src/flrn_slang.c b/src/flrn_slang.c new file mode 100644 index 0000000..ce28cea --- /dev/null +++ b/src/flrn_slang.c @@ -0,0 +1,307 @@ +/* flrn v 3.0 */ +/* */ +/* flrn_slang.c */ +/* */ +/* Liens avec les fonctions slang */ +/* Ce fichier ne devrait pas être indispensable... si on passe à ncurses */ + +#include +#include +#include +#include +#include "flrn.h" +#include "flrn_slang.h" + +int KeyBoard_Quit; +int Screen_Rows, Screen_Cols; +int Screen_Tab_Width; + +void Screen_suspend() { SLsmg_suspend_smg(); } +void Screen_resume() { SLsmg_resume_smg(); } +void Screen_get_size() { SLtt_get_screen_size(); + Screen_Rows=SLtt_Screen_Rows; + Screen_Cols=SLtt_Screen_Cols; + } +int Screen_init_smg() { int a; a=SLsmg_init_smg(); Screen_Tab_Width=SLsmg_Tab_Width; return a; } +void Reset_screen() { SLsmg_reset_smg(); } +void Get_terminfo() { SLtt_get_terminfo(); + Screen_Rows=SLtt_Screen_Rows; + Screen_Cols=SLtt_Screen_Cols; + } +void Screen_write_char(char c) { SLsmg_write_char(c); } +void Screen_write_string(char *s) { SLsmg_write_string(s); } +void Screen_write_nstring(char *s, int n) { SLsmg_write_nstring(s,n); } +void Screen_write_color_chars(unsigned short *s, unsigned int n) + { SLsmg_write_color_chars(s,n); } +void Cursor_gotorc(int r, int c) { SLsmg_gotorc(r,c); } +void Screen_erase_eol() { SLsmg_erase_eol(); } +void Screen_erase_eos() { SLsmg_erase_eos(); } +void Screen_refresh() { SLsmg_refresh(); } +void Screen_touch_lines (int d, unsigned int n) { SLsmg_touch_lines(d,n); } + +void Set_Use_Ansi_Colors(int n) { SLtt_Use_Ansi_Colors=n; } +int Get_Use_Ansi_Colors() { return SLtt_Use_Ansi_Colors; } +void Color_set(int n, char *s1, char *s2, char *s3) + { SLtt_set_color(n,s1,s2,s3); } +void Color_add_attribute(int n, FL_Char_Type a) + { SLtt_add_color_attribute(n,a); } +void Mono_set (int n, char *s, FL_Char_Type a) + { SLtt_set_mono (n,s,a); } + +void Screen_set_color(int n) { SLsmg_set_color(n); } + + +/* Regestion du clavier */ +int Put_tty_single_input (int a, int b, int c) { return SLang_init_tty(a,b,c); } +void React_suspend_char (int a) { SLtty_set_suspend_state(a); } +int init_clavier() { return SLkp_init(); } +void Set_Ignore_User_Abort (int n) { SLang_Ignore_User_Abort=n; } +int Get_Ignore_User_Abort () { return SLang_Ignore_User_Abort; } +int Set_Abort_Signal(void (*a)(int)) +{ + SLang_set_abort_signal(a); + return 0; /* selon versions de Slang, la fonction peut revoyer un entier */ +} +int Keyboard_getkey() { int n; SLKeyBoard_Quit=KeyBoard_Quit; + n=SLkp_getkey(); + KeyBoard_Quit=SLKeyBoard_Quit; return n; } +void Reset_keyboard() { SLang_reset_tty(); } + + + +/**************************************************************************/ +/**************************************************************************/ +/* Gestion des scrolls ... cf pager.c dans les demos de slang. **/ +/**************************************************************************/ + +/* Fenetre de scroll */ +File_Line_Type *Text_scroll=NULL; +/* on s'inspire de slang, amis on change les types et on simplifie */ +typedef struct +{ + unsigned int flags; + File_Line_Type *top_window_line; /* list element at top of window */ + File_Line_Type *lines; /* first list element */ + unsigned int nrows; /* number of rows in window */ + unsigned int line_num; /* current line number (visible) */ + unsigned int num_lines; /* total number of lines (visible) */ + unsigned int window_row; /* row of current_line in window */ +} +Scroll_Window_Type; + +static Scroll_Window_Type Line_Window; /* On va essayer de limiter */ + /* l'usage de cette variable a ce fichier */ + +void free_text_scroll() { + File_Line_Type *line, *next; + + line=Text_scroll; + while (line != NULL) { + next=line->next; + if (line->data != NULL) free (line->data); + free(line); + line=next; + } + Text_scroll=NULL; +} + +/* fonction de compatibilité... */ +static File_Line_Type *create_line (char *buf) { + File_Line_Type *line; + int i; + + line = (File_Line_Type *) safe_calloc (1,sizeof (File_Line_Type)); +/* line->data = SLmake_string (buf); */ /* use a slang routine */ + line->data = safe_malloc(sizeof(short) * (strlen(buf)+1)); + for(i=0;buf[i];i++) { + line->data[i]=(unsigned char)buf[i]; + } + line->data[i]=buf[i]; + line->data_len=i; + return line; +} + +/* on refait un malloc... */ +static File_Line_Type *create_color_line (unsigned short *buf, int len) { + File_Line_Type *line; + int i; + + line = (File_Line_Type *) safe_calloc (1,sizeof (File_Line_Type)); + line->data = safe_malloc(sizeof(short) * len); + for(i=0;idata[i]=buf[i]; + } + line->data_len=len; + return line; +} + +/* Cette fonction ajoute une ligne au texte a scroller */ +/* Elle renvoie NULL en cas d'échec. */ +File_Line_Type *Ajoute_line(char *buf) { + File_Line_Type *line=Text_scroll; + + if (line==NULL) { + Text_scroll=create_line(buf); + if (Text_scroll) { + Line_Window.num_lines++; + } + return Text_scroll; + } + while (line->next) line=line->next; + line->next=create_line(buf); + if (line->next) { + Line_Window.num_lines++; + line->next->prev=line; + } + return (line->next); +} + +/* Cette fonction ajoute une ligne au texte a scroller */ +/* Elle renvoie NULL en cas d'échec. */ +File_Line_Type *Ajoute_color_Line(unsigned short *buf, int len) { + File_Line_Type *line=Text_scroll; + + if (line==NULL) { + Text_scroll=create_color_line(buf,len); + if (Text_scroll) { + Line_Window.num_lines++; + } + return Text_scroll; + } + while (line->next) line=line->next; + line->next=create_color_line(buf,len); + if (line->next) { + Line_Window.num_lines++; + line->next->prev=line; + } + return (line->next); +} + +File_Line_Type *Change_line(int n,char *buf) { + File_Line_Type *line=Text_scroll; + int i; + for(i=0;inext; + } + if (line==NULL) { + return NULL; + } + free(line->data); + line->data = safe_malloc(sizeof(short) * (strlen(buf)+1)); + for(i=0;buf[i];i++) { + line->data[i]=(unsigned char)buf[i]; + } + return (line); +} + +File_Line_Type *Ajoute_form_Ligne(char *buf, int field) { + File_Line_Type *line=Ajoute_line(buf); + int i; + for(i=0;idata_len;i++) + line->data[i] += field<<8; + return line; +} + +char * Read_Line(char * out, File_Line_Type *line) { + int i; + for(i=0;idata_len;i++) + out[i]=line->data[i]; + return out; +} + +/* Libere la derniere ligne de line */ +void Retire_line(File_Line_Type *line) { + + if (line==NULL) return; + while (line->next) line=line->next; + if (line->data) + free(line->data); + if (line->prev) line->prev->next=NULL; + Line_Window.num_lines--; + free(line); + if (line==Text_scroll) Text_scroll=NULL; + return; +} + +void Init_Scroll_window(int num, int beg, int nrw){ + + memset ((char *)&Line_Window, 0, sizeof (Scroll_Window_Type)); + + Line_Window.top_window_line=Text_scroll; + Line_Window.lines = Text_scroll; + Line_Window.line_num = 1; + Line_Window.num_lines = num; + Line_Window.window_row=beg; + Line_Window.nrows=nrw; +} + +static void Update_scroll_display() { + int row; + File_Line_Type *line; + + /* On va bloquer les signaux temporairement */ + SLsig_block_signals (); + row=Line_Window.window_row; + line= Line_Window.top_window_line; + while (row < Line_Window.nrows+Line_Window.window_row) + { + SLsmg_gotorc(row, 0); + if (line != NULL) + { + SLsmg_write_color_chars(line->data, line->data_len); + line=line->next; + } + SLsmg_erase_eol(); + row++; + } +/* SLsmg_refresh(); Le refresh est fait séparément désormais */ + SLsig_unblock_signals(); +} + +/* ob_update =1 : l'update est obligatoire... */ +int Do_Scroll_Window(int n, int ob_update) { + int i; + if (Line_Window.top_window_line == NULL) + return 0; + if (n<0) { + for (i=0;(i>n) && (Line_Window.top_window_line->prev);i--) { + Line_Window.top_window_line=Line_Window.top_window_line->prev; + } + Line_Window.line_num +=i; + } else { + for (i=0;(inext);i++) { + Line_Window.top_window_line=Line_Window.top_window_line->next; + } + Line_Window.line_num +=i; + } + if (i || ob_update) Update_scroll_display(); + return i; +} + +int Number_current_line_scroll() { + return (Line_Window.line_num); +} + +/* autre */ +/* on va mettre les noms de touches ici... */ +#define FIRST_FL_KEY_OFFSET 0x101 +static const char *fl_key_names[] = { + "up","down","left","right", + "pageup","pagedown","home","end", + "a1","a2","b2","c1", + "c3","redo","undo"}; +#define FIRST_FN_OFFSET 0x200 + +int parse_key_name(char *name) { + int i; + for (i=0;i +#include +#include + +#include "flrn.h" +#include "flrn_string.h" + +/* On fait ici toute la gestion des Lecture_List */ +/* Toutes les fonction renvoient 0 en cas de succes */ +/* <0 en cas d'erreur. */ + +/* sauf alloue_chaine, qui fonctionne comme un malloc */ + +Lecture_List *alloue_chaine() { + return (Lecture_List *)safe_calloc(1,sizeof(Lecture_List)); +} + +int free_chaine(Lecture_List *chaine) { + Lecture_List *courant1, *courant2; + + courant1=chaine; + while (courant1) { + courant2=courant1->suivant; + free(courant1); + courant1=courant2; + } + return 0; +} + + +int ajoute_char(Lecture_List **chaine, int chr) { + Lecture_List *courant; + + courant=*chaine; + if (courant->sizelu[courant->size++]=chr; + } else { + courant->size=MAX_READ_SIZE-1; + courant->lu[MAX_READ_SIZE-1]='\0'; + if (courant->suivant==NULL) { + courant->suivant=alloue_chaine(); + } else courant->suivant->size=0; + courant->suivant->prec=courant; + courant=courant->suivant; + courant->lu[courant->size++]=chr; + *chaine=courant; + } + return 0; +} + +int enleve_char(Lecture_List **chaine) { + Lecture_List *courant; + int res; + + courant=*chaine; + if (courant->size!=0) { + courant->size--; + return 0; + } else { + if (courant->prec==NULL) return -1; + if (courant->suivant) { free_chaine(courant->suivant); courant->suivant=NULL; } + courant=courant->prec; + res=enleve_char(&courant); + *chaine=courant; + return res; + } +} + +int str_cat (Lecture_List **chaine1, char *chaine) { + char *ptr; + for(ptr=chaine;*ptr;ptr++) ajoute_char(chaine1,*ptr); + return 0; +} + +#if 0 +int str_cat (Lecture_List **chaine1, char *chaine) { + int size_res, len_to_cop; + Lecture_List *courant; + + courant=*chaine1; + courant->lu[courant->size]='\0'; + len_to_cop=strlen(chaine); + while (1) { + size_res=(MAX_READ_SIZE-1)-courant->size; + if (size_res>=len_to_cop) { + strcat(courant->lu, chaine); + courant->size+=len_to_cop; + *chaine1=courant; + return 0; + } else { + strncat(courant->lu, chaine, size_res); + chaine+=size_res; + len_to_cop-=size_res; + courant->lu[MAX_READ_SIZE-1]='\0'; + courant->size=MAX_READ_SIZE-1; + if (courant->suivant==NULL) { + courant->suivant=alloue_chaine(); + } else { + courant->suivant->lu[0]='\0'; + courant->suivant->size=0; + } + courant->suivant->prec=courant; + courant=courant->suivant; + } + } + return 0; /* pour les compilos naczes */ +} +#endif + + +/* Cette fonction concatene chaine2 à partir de place dans chaine1 jusqu'au */ +/* charactère char non inclus. */ +int str_ch_cat(Lecture_List **chaine1, Lecture_List *chaine2, int place, char chr) { + char *buf; + + while (chaine2->lu[place]!=chr) { + buf=strchr(chaine2->lu+place,chr); + if (buf!=NULL) { + *buf='\0'; + str_cat(chaine1, chaine2->lu+place); + *buf=chr; + return 0; + } + else { + str_cat(chaine1, chaine2->lu+place); + chaine2=chaine2->suivant; + if (chaine2==NULL) return -1; + place=0; + } + } + return 0; +} + + +/* Cette fonction renvoie le n-ieme caractere en partant de la fin */ +char get_char(Lecture_List *chaine, int n) { + + if (chaine->size>=n) return chaine->lu[chaine->size-n]; + if (chaine->prec) return get_char(chaine->prec, n-chaine->size); + else return 0; +} diff --git a/src/flrn_string.h b/src/flrn_string.h new file mode 100644 index 0000000..81e5d30 --- /dev/null +++ b/src/flrn_string.h @@ -0,0 +1,17 @@ +/* flrn v 0.1 */ +/* flrn_string.h 03/12/97 */ +/* */ +/* Headers pour flrn_string.c */ + +#ifndef FLRN_FLRN_STRING_H +#define FLRN_FLRN_STRING_H + +#include "flrn_config.h" + +typedef struct flrn_lecture_list { + struct flrn_lecture_list *prec,*suivant; + char lu[MAX_READ_SIZE]; + int size; +} Lecture_List; + +#endif diff --git a/src/flrn_tcp.c b/src/flrn_tcp.c new file mode 100644 index 0000000..9676ee6 --- /dev/null +++ b/src/flrn_tcp.c @@ -0,0 +1,494 @@ +/* flrn v 0.3 */ +/* flrn_tcp.c 22/06/98 */ +/* */ +/* Communication avec le serveur... Essentiellement tout ce qui se */ +/* rapporte a la gestion d'une socket... */ + + +#include /* a voir */ +#include /* socket */ +#include /* Internet address manipulation routines */ +#include /* Internet address manipulation routines */ +#include /* Internet address manipulation routines */ +#include /* appel au DNS */ +#include /* read/write */ +#include /* select */ +#include /* atoi */ +#include +#include +#include + +#include "flrn.h" +#include "config.h" +#include "options.h" + +int Date_offset; + +/* Commandes au serveur */ +static const char *tcp_command[] = { + "QUIT", "MODE READER", "NEWGROUPS", "NEWNEWS", "GROUP", + "STAT", "HEAD", "BODY", "XHDR", "XOVER", "POST", "LIST", "DATE", + "ARTICLE" }; + + +/* Ligne lue (cf flrn_glob.h) */ +char tcp_line_read[MAX_READ_SIZE]; +/* file desciptor de la socket */ +int tcp_fd; + + +/* Connection au port d'une machine. La fonction est rudimentaire pour */ +/* l'instant, mais j'ai peur que ça nécessite quelques améliorations */ +/* pour l'avenir. */ +/* Je m'inspire directement de slrn et de traqueur.c (cf Fba et moi) */ +/* D'autre part, la procedure ne recherchera a joindre le serveur que */ +/* par une adresse (cf sltcp.c de slrn pour plus de rigueur) */ +/* Note : requires host != NULL */ +/* Note : dans slrn, on rajoute des tests pour siglongjmp et sigsetjmp, */ +/* mais je prefere ne pas m'en occuper (non mais sans blague). */ + +static int contact_server (char *host, int port) { + struct hostent *hp; /* Le serveur ( si ! ) */ + struct sockaddr_in s_in; /* la socket ( si aussi ! ) */ +#if 0 + struct protoent *tcpproto; + int on=1; /* pour setsockopt */ +#endif + + /* Determination du serveur */ +#ifdef HAVE_INET_ATON + if (!inet_aton(host,&(s_in.sin_addr))) /* inet_addr est OBSOLETE, selon Linux */ +#else + if ((s_in.sin_addr.s_addr=inet_addr(host))==-1) /* mais pas pour tout le monde */ +#endif + { /* host est un nom de machine */ + hp=(struct hostent *) gethostbyname(host); + if (!hp) { + fprintf(stderr, "%s : Unknown host.\n", host); + return -1; + } + s_in.sin_addr.s_addr=*(unsigned long int *)(hp->h_addr_list[0]); /* UNE adresse */ + s_in.sin_family=hp->h_addrtype; /* toujours AF_INET, parait-il*/ + } + else + s_in.sin_family=AF_INET; + s_in.sin_port = htons((unsigned short) port); + + /* Creation de la socket */ + tcp_fd=socket(s_in.sin_family, SOCK_STREAM, 0); + if (tcp_fd < 0) { + perror("socket"); + return -1; + } + + /* cette operation n'est pas faite dans slrn... on verra */ +/* + if ((tcpproto = (struct protoent *) getprotobyname("tcp")) != NULL) + setsockopt(tcp_fd, tcpproto->p_proto, TCP_NODELAY, &on, sizeof(int)); +*/ + + /* La commence normalement un essai par adresse. On n'en a garde qu'une */ + /* donc ce sera plus facile. */ + if (connect(tcp_fd, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) { + perror("connect"); + /* on ferme la socket avant de sortir */ + close(tcp_fd); + return(-1); + } + /* Et voila... Aussi simple que ca */ + return (tcp_fd); +} + + + +/* Lecture "brute" du serveur */ +/* Cette fonction lit des données sur le serveur, et renvoie sur buf ce */ +/* qui est lu. Le résultat de la fonction est le nombre d'octets */ +/* lus. */ +/* min est le nombre d'octects minimum que l'on s'attend a pouvoir lire */ +/* (en cas de reponse a une requete). */ +/* Cette fonction renvoie -1 en cas d'erreur, 0 si connection coupee. */ +/* ATTENTION : on suppose avoir assez de place dans buf. */ + +static int raw_read_server (char *buf, int min) { +#if 0 + fd_set rfds; +#endif +/* struct timeval tv; + int ret_sel,attempt=0; */ + int ret_read; + int lus=0; + + while (lus0) { ligne_ptr++; /* on a effectivement lu '\0' ????? */ + rendus++; + stockes--; ptr++; + if (rendus==max) { /* Ouinnnn */ + ligne[max]='\0'; + return max; + } + } + ret=read_server(ligne_ptr, 0, max-rendus); + if (ret==-1) return -1; + rendus+=ret; + ligne[rendus]='\0'; + return rendus; + } else + { + rendus=max; + strncpy(ligne, ptr, max); + ptr+=rendus; + stockes-=rendus; + ligne[max]='\0'; + return max; + } + } else + { + ptr2++; + if (ptr2-ptr>max) { /* Que des emmerdes ! */ + rendus=max; + strncpy(ligne, ptr, max); + ptr+=rendus; + ligne[max]='\0'; + return max; + } else { /* le cas qui devrait marcher le plus souvent... */ + rendus=ptr2-ptr; + strncpy(ligne, ptr, rendus); + ptr=ptr2; + stockes-=rendus; /* stockes devrait etre toujours >= 0 */ + ligne[rendus]='\0'; + return rendus; + } + } + /* Unreachable */ +} + + +/* Vire tout ce que peut envoyer le serveur jusqu'au prochain "\r\n.\r\n" */ +/* renvoie -1 en cas d'erreur. */ +int discard_server() { + int ret; + + if (debug) fprintf(stderr,"Discard server\n"); + do { + ret=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (ret<1) return -1; + if ((tcp_line_read[0]=='.') && (tcp_line_read[1]!='.')) return 0; + while (tcp_line_read[ret-1]!='\n') { + ret=read_server(tcp_line_read,1,MAX_READ_SIZE); + if (ret<1) return -1; + } + } while (1); +} + + +/* Ouverture PROPRE de la connection */ +/* Cette fonction se charge d'appeler connect_server, et lit la reponse */ +/* du serveur a la connection. */ +/* Elle renvoie -1 en cas d'echec de la connection, le code renvoye par */ +/* le serveur sinon. Si ce code indique un refus du serveur, la */ +/* fonction coupe la connection. */ +int connect_server (char *host, int port) { + int ret, code; + + if (host==NULL) { + fprintf(stderr,"Pas de serveur où se connecter.\n"); + return -1; + } + if (port==0) port=Options.port; + ret=contact_server(host, port); + if (ret<0) { + fprintf(stderr, "Echec de la connection au serveur : %s\n", host); + return -1; + } + + code=return_code(); + + if ((code!=200) && (code!=201)) + { + if (debug) fprintf (stderr,"Accès refusé. On va donc quitter :-( \n"); + close (tcp_fd); + return code; + } + + write_command(CMD_MODE_READER, 0, NULL); + + ret=return_code(); + + if (debug) fprintf (stderr,"Serveur retourne (mode reader) : %d\n", code); + +/* On place juste XMODE READER au cas ou...*/ +/* Non nécéssaire (trn4 ne le fait pas) */ +/* + if (ret>400) { + raw_write_server("XMODE READER\r\n", 14); + ret=return_code(); + } +*/ + + return code; +} + +int adjust_time() +{ + long heure_serveur, heure_locale; + int res,code; + char *buf; + + write_command(CMD_DATE, 0, NULL); + res=read_server(tcp_line_read, 3, MAX_READ_SIZE-1); + if (debug) fprintf(stderr,"Yo :%s\n",tcp_line_read); + if (res<0) return -1; + buf=strchr(tcp_line_read,' '); + code = time(NULL); + if (buf) { + code=strtol(buf+9,&buf,10); + heure_serveur=code %100 + 60*(((code/100)%100) + 60*(((code/10000)%100))); + } else + heure_serveur=code; + + heure_locale=time(NULL); +/* heure_locale = mktime(gmtime(&heure_locale))%86400; */ + Date_offset = ((heure_serveur - heure_locale)%86400); + if (Date_offset > 43200) Date_offset = Date_offset - 86400; + if (Date_offset < -43200) Date_offset = Date_offset + 86400; + if (debug) + fprintf(stderr,"Yu :%ld %ld %d\n",heure_serveur, + heure_locale,Date_offset); + return 0; +} + +/* parse du timeout : on teste "imeout" ou "timed" */ +/* on revoie 1 si le test est juste */ + /* I know of only two timeout responses: (slrn) + * * 503 Timeout + * * 503 connection timed out + */ +static int _nntp_try_parse_timeout(char *str) { + if (strstr(str,"imeout") || strstr(str,"timed")) return 1; + return 0; +} + +/* Lecture juste de la ligne du retour d'une commande. */ +int return_code () { + int lus, code; + + lus=read_server(tcp_line_read, 3, MAX_READ_SIZE-1); + if (lus<3) { + if (debug) fprintf(stderr, "Echec en lecture du serveur\n"); + return -1; + } + + if (debug) fprintf(stderr, "Lecture : %s", tcp_line_read); + + code=atoi(tcp_line_read); + + /* On va essayer de gerer le cas du timeout */ + if ((code==503) && (_nntp_try_parse_timeout(tcp_line_read))) { + if (debug) fprintf(stderr,"Un timeout... %s",tcp_line_read); + if (reconnect_after_timeout(1)<0) return -1; + /* envoie aussi toutes les bonnes commandes au serveur */ + return return_code(); + } + + /* Ici, on gere le cas d'une ligne trop longue */ + while (tcp_line_read[lus-1]!='\n') { + if (debug) fprintf(stderr, "ligne tres longue ???\n"); + lus=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + + if (lus<1) { + if (debug) fprintf(stderr, "Echec en lecture du serveur\n"); + return -1; + } + if (debug) fprintf(stderr, "%s", tcp_line_read); + } + return code; +} + +/* Cette fonction ecrit brutalement au serveur, jusqu'au bout, si c'est */ +/* possible. */ +int raw_write_server (char *buf, unsigned int len) { + int res; + unsigned int total; + + total=0; + + while (total != len) + { + res=write(tcp_fd, buf+total, (len-total)); /* slrn met buf ??? */ + if (res==-1) + { + /* euh... On n'est pas en mode non bloquant... */ +#ifdef EAGAIN + if (errno == EAGAIN) + { + sleep (1); + continue; + } +#endif +#ifdef EWOULDBLOCK + if (errno == EWOULDBLOCK) + { + sleep (1); + continue; + } +#endif + if (errno == EINTR) continue; + if (debug) fprintf(stderr,"Erreur en ecriture\n"); + return total; + } + total += (unsigned int) res; + } + return total; +} + +/* Ecriture d'une commande d'une ligne. */ +/* Parametre : numero de la commande. nombre de parametres, tableau sur */ +/* les parametres. */ +static char line_write[MAX_READ_SIZE]; + /* on sort cette variable de la fonction : elle sert dans */ + /* reconnect_after_timeout. */ + +int write_command (int num_com, int num_param, char **param) { + char *ptr; + int size, pram, tmp_len; + + pram=0; ptr=line_write; + + strcpy (line_write, tcp_command[num_com]); + size=strlen(line_write); ptr+=size; + while (pram0) + { + res=read_server(tcp_line_read, 3, MAX_READ_SIZE-1); + if (res<0) return; + if (debug) fprintf(stderr, "lecture : %s", tcp_line_read); + } + close(tcp_fd); /* Bon, cette procedure est non fiable. */ +} diff --git a/src/flrn_xover.c b/src/flrn_xover.c new file mode 100644 index 0000000..1a2547e --- /dev/null +++ b/src/flrn_xover.c @@ -0,0 +1,453 @@ +/* flrn v 0.1 */ +/* flrn_xover.c 02/02/98 */ +/* */ +/* Gestion de la commade XOVER */ +/* Ceci est destiné à devenir le standard pour flrn. */ +/* */ + +#include +#include +#include +#include +#include "flrn.h" +#include "group.h" +#include "art_group.h" + +/* On définit ici la structure des XOVER qu'on va avoir */ +#define OVERVIEW_SIZE_MAX 15 +#define FULL_HEADER_OVER 256 /* doit être > NB_KNOWN_HEADERS */ +/* -10 = bad, -1 = message-id */ +static int overview_list[OVERVIEW_SIZE_MAX]; +int overview_usable=0; + +/* Cette fonction teste si overview.fmt existe et remplit la structure + * du xover si possible... + * Résultat : 0 Ok + * -1 Bug ou xover inutilisable */ +int get_overview_fmt() { + int res, code, num, i; + char *buf; + int good=0; + + for (num=0; num=400) return -1; + + num=0; + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) return -1; + while (tcp_line_read[0]!='.') { + if (num==OVERVIEW_SIZE_MAX) { /* C'est trop... :-( */ + discard_server(); + return -1; + } + if (strncasecmp(tcp_line_read, "Message-Id:",11)==0) + { + overview_list[num]=-1; + if (strstr(tcp_line_read,"full")) + return -1; + good++; + } else + for (i=0; i=2); + return (good>=2)?0:-1; +} + +/* On obtient maintenant un résumé des articles n1-n2 du groupe par XOVER. */ +/* on crée les articles à chaque fois, en partant de article */ +/* si celui-ci est non NULL, et de Article_deb sinon... */ +/* on va imiter ce que fait cree_liste de art_groupe, mais en mieux si */ +/* si possible */ +/* On suppose qu'on est déjà dans le groupe courant (cette fonction peut */ +/* par exemple se faire appeler par un truc avant cree_liste... */ +/* retour : >=0 si succès... -1 si échec */ +int cree_liste_xover(int n1, int n2, Article_List **input_article) { + int res, code, suite_ligne=0, num=0, crees=0, fait_coupure=0; + char *buf, *buf2; + Article_List *creation, *article=input_article?*input_article:NULL; + Range_List *msg_lus=Newsgroup_courant->read; + int i,lus_index=0; + int new_article=0; + + buf=safe_malloc(50*sizeof(char)); /* De toute façon, on devra */ + /* gérer des lignes TRES longues cette fois :-( */ + /* On envoie la commande Xover */ + sprintf(buf, "%d-%d", n1, n2); + res=write_command(CMD_XOVER, 1, &buf); + free(buf); + if (res<0) return -1; + code=return_code(); + if ((code<0) || (code>300)) return -1; + + if (article==NULL) Article_courant=Article_deb=NULL; + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) return -1; + while ((suite_ligne) || (tcp_line_read[0]!='.')) { + if (!suite_ligne) { + num=0; + creation = safe_calloc (1,sizeof(Article_List)); + buf=tcp_line_read; + buf2=strchr(buf,'\t'); + *buf2='\0'; /* On suppose qu'il ne peut y avoir de pbs ici */ + creation->numero=strtol(buf,NULL,10); + /* TODO faire la même chose partout !!! */ + creation->flag=FLAG_NEW; /* pour le kill_file */ + /* on le recherche */ + if ((article) && (article->numero > creation->numero)) { + /* on insère avant article - sans danger le serveur répond + * dans l'ordre et on suppose qu'on part du début de la liste*/ + if (article->prev != NULL) { free(creation); return -1; } + article->prev=creation; + creation->next=article; + new_article=1; + article=creation; + *input_article=article; + } else { + while ((article) && (article->next) && + (article->next->numero <= creation->numero)) + article=article->next; + if (article && (article->numero == creation->numero)) { + new_article=0; + free_article_headers(article->headers); + article->headers=NULL; + free(creation); + } else { + if (article) { + if (article->next) + article->next->prev=creation; + creation->next=article->next; + article->next=creation; + creation->prev=article; + } else { + Article_deb=Article_courant=creation; + } + new_article=1; + article=creation; + } + } + crees+= new_article; + buf=buf2+1; + article->headers=new_header(); + fait_coupure=0; + /* on regarde si le message est lu */ + /* on recherche dans la table un max >= au numero de message */ + if (new_article) { + while ((msg_lus) && (msg_lus->max[lus_index] < article->numero)) + { + if(++lus_index>= RANGE_TABLE_SIZE) { + lus_index=0; + msg_lus = msg_lus->next; + } + } + /* on verifie que le min est <= au numero */ + if ((msg_lus) && (msg_lus->min[lus_index] <= creation->numero)) { + creation->flag |= FLAG_READ; + } + } + } + buf2=strchr(buf,'\t'); + while (buf2!=NULL) { + *buf2='\0'; + if (buf!=buf2) { + if (new_article && (overview_list[num]==-1)) { + if(suite_ligne) { + article->msgid= safe_realloc(article->msgid, + strlen(article->msgid) + strlen(buf) +2); + strcat(article->msgid, buf); + } else + article->msgid=safe_strdup(buf); + } else + if (overview_list[num]>=0) { + if ((!fait_coupure) && (overview_list[num] & FULL_HEADER_OVER)) { + buf=strchr(buf,':'); + buf++; + if (*buf) buf++; /* on passe aussi le ' ' */ + } + i=overview_list[num] & ~FULL_HEADER_OVER; + if (suite_ligne) { article->headers->k_headers[i]=safe_realloc( + article->headers->k_headers[i], + (strlen(article->headers->k_headers[i])+strlen(buf)+2)); + strcat(article->headers->k_headers[i],buf); + } else + article->headers->k_headers[i]=safe_strdup(buf); + } + } + if (overview_list[num]>=0) { + article->headers->k_headers_checked[ + overview_list[num] & ~FULL_HEADER_OVER ] = 1; + } + buf=buf2+1; + fait_coupure=0; + suite_ligne=0; + buf2=strchr(buf,'\t'); + if (buf2==NULL) buf2=strchr(buf,'\r'); + num++; + } + /* si la ligne est fini, on devrait avoir *buf='\n' */ + if (*buf!='\n') { + if (*buf) + if (overview_list[num]>=0) { + if ((!fait_coupure) && (overview_list[num] & FULL_HEADER_OVER)) { + buf=strchr(buf,':'); + if (buf==NULL) buf=index(buf,'\0'); else { + buf++; + fait_coupure=1; + } + } + } + if (*buf) { + if (overview_list[num] >=0) { + i=overview_list[num] & ~FULL_HEADER_OVER; + if (suite_ligne) { article->headers->k_headers[i]=safe_realloc( + article->headers->k_headers[i], + (strlen(article->headers->k_headers[i])+strlen(buf)+2)); + strcat(article->headers->k_headers[i],buf); + } else + article->headers->k_headers[i]=safe_strdup(buf); + } else + if (new_article && (overview_list[num] == -1)) { + article->msgid=safe_strdup(buf); + } + } + suite_ligne=1; + } else { + /* On décode le header subject */ + /* C'est un peu limité, mais bon, faudra voir ça avec Jo */ + /* On parse la date aussi */ + if (article->headers->k_headers[SUBJECT_HEADER]) + rfc2047_decode(article->headers->k_headers[SUBJECT_HEADER], + article->headers->k_headers[SUBJECT_HEADER], + strlen(article->headers->k_headers[SUBJECT_HEADER])); + if (article->headers->k_headers[DATE_HEADER]) + article->headers->date_gmt=parse_date(article->headers->k_headers[DATE_HEADER]); + suite_ligne=0; + } + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) return -1; + } + return crees; +} + + +int va_dans_groupe() { + int res; + char *buf; + + if (Newsgroup_courant==NULL) return 0; + /* On change le newsgroup courant pour le serveur */ + buf=Newsgroup_courant->name; + res=write_command(CMD_GROUP, 1, &buf); + if (res<0) return -1; + return 0; +} + +/* Cette fonction cree la liste des articles du newsgroup courant + * On suppose provisoirement que la commande XHDR fonctionne. + * Le retour est -2 pour un newsgroup invalide. */ +int cree_liste_noxover(int min, int max, Article_List *start_article) { + int res, code, crees=0; + char *buf, *buf2; + Article_List *creation,*article=start_article; + Range_List *msg_lus=Newsgroup_courant->read; + int lus_index=0; + + /* Envoie de la commande XHDR + * 270 comme taille max d'un message-ID ? */ + buf=safe_malloc(270*sizeof(char)); + sprintf(buf,"Message-ID %d-%d", min, max); + res=write_command(CMD_XHDR, 1, &buf); + if (res<0) {free(buf);return -1;} + code=return_code(); + if ((code<0) || (code>300)) {free(buf);return -1;} + + if (article==NULL) Article_courant=Article_deb=NULL; + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<2) {free(buf);return -1;} + tcp_line_read[res-2]='\0'; /* La, on écrase le \r\n */ + while (tcp_line_read[0]!='.') { + crees++; + creation = safe_calloc (1,sizeof(Article_List)); + creation->prev = article; + if (article) article->next=creation; + else Article_deb=Article_courant=creation; + creation->msgid = safe_malloc ((res-2)*sizeof(char)); + sscanf(tcp_line_read, "%d %s", &(creation->numero), creation->msgid); + + creation->flag =0; + /* on regarde si le message est lu */ + /* on recherche dans la table un max >= au numero de message */ + while ((msg_lus) && (msg_lus->max[lus_index] < creation->numero)) + { + if(++lus_index>= RANGE_TABLE_SIZE) { + lus_index=0; + msg_lus = msg_lus->next; + } + } + /* on verifie que le min est <= au numero */ + if ((msg_lus) && (msg_lus->min[lus_index] <= creation->numero)) { + creation->flag |= FLAG_READ; + } + + article=creation; + + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) {free(buf);return -1;} + tcp_line_read[res-2]='\0'; /* Idem */ + } + if (article) { + article->next=NULL; + Newsgroup_courant->max=article->numero; /* Au cas ou ca aurait */ + /* quand même buggué...*/ + } + + /* Recherche des parents */ + sprintf(buf, "References %d-%d", min, max); + res=write_command(CMD_XHDR, 1, &buf); + if (res<0) {free(buf);return -1;} + code=return_code(); + if ((code<0) || (code>300)) {free(buf);return -1;} + + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) {free(buf); return -1;} + for (article=Article_deb; article; article=article->next) { + if (tcp_line_read[0]=='.') break; /* Impossible */ + /* il faut faire ce genre de check, non ? */ + if ((code=strtol(tcp_line_read,&buf2,10)) != article->numero) + { + if (debug) fprintf(stderr,"xhdr References se passe mal\n"); + while (article && (article->numeronext; } + /* Si article=NULL, on ignore ce qu'il y a encore a lire */ + if (!article) { discard_server(); break; } + } + if (code == article->numero) { + /* on construit le champ References */ + article->headers=new_header(); + article->headers->k_headers_checked[REFERENCES_HEADER] = 1; + if ((buf2 = strchr(buf2,'<'))) + article->headers->k_headers[REFERENCES_HEADER] = + safe_strdup(buf2); + } + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) {free(buf);return -1;} + } + free(buf); + return crees; +} + +int cree_liste(int art_num, int *part) { + int min,max,res,code; + char *buf; + /* On change le newsgroup courant pour le serveur */ + *part=0; + if (va_dans_groupe()<0) return -1; + + /* on n'utilise pas return_code car on veut le max du newsgroup */ + res=read_server(tcp_line_read, 3, MAX_READ_SIZE-1); + if (res<3) { + if (debug) fprintf(stderr, "Cree_liste: Echec en lecture du serveur\n"); + return -1; + } + /* On ne suppose pas que la ligne est trop longue : c'est quasi-exclu */ + if (tcp_line_read[res-1]!='\n') { + if (debug) fprintf(stderr, "Serveur grotesque \n"); + return -1; + } + code=strtol(tcp_line_read, &buf, 10); + + if (code<=0) return -1; + if (code==503) { /* Il y a eu probablement timeout */ + /* on lance donc un reconnect, et on */ + /* recommence */ + if (reconnect_after_timeout(0)<0) return -1; + return cree_liste(art_num, part); + } + if (code==411) { if (debug) fprintf(stderr, "Newsgroup invalide !\n"); + return -2; } + strtol(buf, &buf, 0); + min=strtol(buf, &buf, 0); + max=strtol(buf, &buf, 0); + + if (Newsgroup_courant->Article_deb) { + Article_deb=Newsgroup_courant->Article_deb; + Article_exte_deb=Newsgroup_courant->Article_exte_deb; + if (Newsgroup_courant->max si list active et */ + { + /* on essaie un cree_liste_xover moins couteux qu'un cherche newnews */ + if (cree_liste_xover(Newsgroup_courant->max+1,max,&Article_deb)) { + Date_groupe = time(NULL)+Date_offset; + cree_liens(); + } + else + cherche_newnews(); + } + return 1; + } + /* si on a deja ce qu'on veut, on n'en fait pas trop */ + + if ((maxmax<=max) /* Ca peut etre > si list active et */ + /* group sont contradictoires.... */ + Newsgroup_courant->max=max; + else { /* Des messages ne sont pas disponibles */ + /* On va supposer tout bêtement qu'il y a un cancel */ + if (debug) fprintf(stderr, "Problème de max de Newsgroup_courant\n"); +#if 0 + Aff_error("Patientez svp..."); + sleep(5); /* En esperant que ca suffise */ +#endif + } + + if (art_num==0) art_num=max-100; + /* fixme */ + art_num -= 20; /* on veut essayer d'avoir le père et les frères */ + if ((art_num>min) && (art_num<=max) && (overview_usable)) { + min=art_num; + *part=1; + } + Date_groupe = time(NULL)+Date_offset; + if (overview_usable) + res=cree_liste_xover(min,max,NULL); + else + res=cree_liste_noxover(min,max,NULL); + + if (res<=0) return res; + Article_exte_deb=NULL; + cree_liens(); + /* on memorise tout */ + /* attention, Article_deb et Article_exte_deb pourraient changer !!! */ + /* donc on ne les utilise pas, et on les remplacera a la fin... */ + Newsgroup_courant->article_deb_key=Article_deb_key; + Newsgroup_courant->Article_deb=Article_deb; + Newsgroup_courant->Article_exte_deb=Article_exte_deb; + return res; +} + +int cree_liste_end() { + int res; + /* fixme */ + if (!overview_usable) return 0; + if (Article_deb->numero <2) return 0; + res=cree_liste_xover(1,Article_deb->numero-1,&Article_deb); + if (res) cree_liens(); + return 0; +} diff --git a/src/group.c b/src/group.c new file mode 100644 index 0000000..d93647f --- /dev/null +++ b/src/group.c @@ -0,0 +1,697 @@ +/* flrn v 0.1 */ +/* group.c 24/11/97 */ +/* */ +/* Gestion des groupes... */ +/* */ +/* */ + +#include +#include +#include +#ifdef TM_IN_SYS_TIME +#include +#else +#include +#endif + +#include "flrn.h" +#include "group.h" +#include "options.h" +#include "flrn_menus.h" + +Newsgroup_List *Newsgroup_courant; +Newsgroup_List *Newsgroup_deb; +Newsgroup_List *Newsgroup_deleted=NULL; + +time_t Last_check; + +/* Lit les articles lus marques dans le .flnews */ +/* La syntaxe est celle d'un .newsrc standard */ +/* On lui passe un pointeur apres l'espace qui suit ':' ou '!' */ +Range_List *init_range(char *buf) +{ + Range_List *creation; + char *index=buf; + int i=0; + if ((buf ==NULL) || (*buf == '\0')) + return NULL; + /* allocation du premier bloc */ + creation = safe_calloc(1,sizeof(Range_List)); + do { + /* on lit le premier nombre */ + creation->min[i] = creation->max[i] = strtol(index,&index,10); + if (*index=='\0') + return creation; + /* syntaxe 2-5 ? */ + if (*index=='-') + { + creation->max[i] = -strtol(index,&index,10); + if (*index=='\0') + return creation; + } + if (*index==',') /* le delimiteur */ + index++; + else { + if (debug) fprintf(stderr,"Erreur dans le fichier .flnewsrc\n"); + return NULL; + } + i++; + } while (inext=init_range(index); + return creation; +} + +static Newsgroup_List *add_group(Newsgroup_List **p) { + Newsgroup_List *creation; + creation = safe_calloc(1,sizeof(Newsgroup_List)); + if (*p) (*p)->next=creation; + creation->prev=*p; + if (!Newsgroup_deb) Newsgroup_deb=creation; + *p=creation; + return creation; +} + +/* Creation de la liste des newsgroup a partir du .flnewsrc. */ +void init_groups() { + FILE *flnews_file; + Newsgroup_List *creation; + char buf[MAX_BUF_SIZE], *deb, *ptr; + + Newsgroup_courant=Newsgroup_deb=NULL; + flnews_file = open_flrnfile(".flnewsrc","r",1); + + if (flnews_file==NULL) return; + + while (fgets(buf,MAX_BUF_SIZE,flnews_file)) + { + /* on vire le \n de la fin de la ligne */ + if ((deb=strchr(buf,(int) '\n'))) + *deb='\0'; + /* on cherche le debut de la liste des articles lus */ + ptr=buf; while (*ptr==' ') ptr++; + if (*ptr=='\0') continue; + deb=strchr(ptr,(int) ' '); + if (deb) *(deb++)='\0'; + if (strlen(ptr)>MAX_NEWSGROUP_LEN) + { if (debug) fprintf(stderr,"Nom du newsgroup trop long !!! \n"); + exit(1); } + creation = add_group(&Newsgroup_courant); + strncpy(creation->name, ptr, strlen(ptr)-1); + creation->flags=(ptr[strlen(ptr)-1]==':' ? 0 : GROUP_UNSUBSCRIBED); + creation->name[strlen(ptr)-1]='\0'; + creation->min=1; + creation->read=init_range(deb); + creation->max=0; + } + if (Newsgroup_courant) Newsgroup_courant->next=NULL; + fclose (flnews_file); +} + +/* Ajoute un newsgroup a la liste... Appelé par les fonctions qui suivent */ +/* la_ligne comprend, séparé par des ' ', le nom, la taille... */ +/* On vérifie avant l'existence du newsgroup... */ +static Newsgroup_List *un_nouveau_newsgroup (char *la_ligne) +{ + Newsgroup_List *actuel; + Newsgroup_List *creation; + char *buf; + + buf=strchr(la_ligne,' '); + *(buf++)='\0'; + + actuel=Newsgroup_deb; + if (actuel) { + while (actuel->next) { + if (strcmp(la_ligne,actuel->name)==0) return actuel; + actuel=actuel->next; + } + if (strcmp(la_ligne,actuel->name)==0) return actuel; + } + creation=add_group(&actuel); + strncpy(creation->name, la_ligne, MAX_NEWSGROUP_LEN); + creation->max = strtol(buf, &buf, 0); + creation->min = strtol(buf, &buf, 0); + creation->read= NULL; + creation->description= NULL; + creation->flags=GROUP_UNSUBSCRIBED | GROUP_NEW_GROUP_FLAG; + return creation; +} + +/* Test pour la creation de nouveaux newsgroup (par defaut place pour en */ +/* fin de newsgroup). */ +void new_groups(int opt_c) { + struct tm *gmt_check; + Newsgroup_List *creation; + int res, code; + char *buf; + char param[20]; + int good, bad; + regex_t goodreg, badreg; + + /* On forme la date en GMT */ + if (Last_check!=0) { + gmt_check=gmtime(&Last_check); + res=strftime(param, 20, "%y%m%d %H%M%S GMT", gmt_check); + param[res]='\0'; + if (debug) fprintf(stderr, "Date formée : %s\n",param); + + buf=param; + res=write_command(CMD_NEWGROUPS, 1, &buf); + } else res=write_command(CMD_LIST, 0, NULL); + + if (res<0) return; + code=return_code(); + + if (code>400) { + if (debug) fprintf(stderr,"Code d'erreur\n"); + return; + } + + Newsgroup_courant=Newsgroup_deb; + while (Newsgroup_courant && Newsgroup_courant->next) + Newsgroup_courant=Newsgroup_courant->next; + /* Ici, Newsgroup_courant==NULL signifie que le .flnewsrc etait vide */ + /* on lit la premiere ligne avant la boucle, pour savoir s'il + * faut compiler les regexp */ + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<1) { + if (debug) fprintf(stderr, "Echec en lecture du serveur\n"); + return; + } + if (tcp_line_read[1]=='\r') return; /* a priori, on a lu ".\r\n" */ + + if (Options.auto_subscribe) { + if (regcomp(&goodreg,Options.auto_subscribe,REG_EXTENDED|REG_NOSUB)) + { + char buf[1024]; + regerror(regcomp(&goodreg,Options.auto_subscribe,REG_EXTENDED|REG_NOSUB), + &goodreg,buf,1024); + if (debug) fprintf(stderr,"Goodreg : %s\n",buf); + return; + } + } + + if(Options.auto_ignore) { + if (regcomp(&badreg,Options.auto_ignore,REG_EXTENDED|REG_NOSUB)) { + char buf[1024]; + if (Options.auto_subscribe) regfree(&goodreg); + regerror(regcomp(&badreg,Options.auto_ignore,REG_EXTENDED|REG_NOSUB), + &badreg,buf,1024); + if (debug) fprintf(stderr,"Badreg : %s\n",buf); + return; + } + } + + /* on vire les flags GROUP_NEW_GROUP_FLAG */ + Newsgroup_courant=Newsgroup_deb; + if (Newsgroup_courant!=NULL) + while (Newsgroup_courant->next) { + Newsgroup_courant->flags &= ~GROUP_NEW_GROUP_FLAG; + Newsgroup_courant=Newsgroup_courant->next; + } + + do { + if (res<1) { + if (debug) fprintf(stderr, "Echec en lecture du serveur\n"); + if (Options.auto_subscribe) regfree(&goodreg); + if (Options.auto_ignore) regfree(&badreg); + return; + } + if (tcp_line_read[1]=='\r') break; /* a priori, on a lu ".\r\n" */ + /* On parcourt à chaque fois l'ensemble des newsgroup pour vérifier */ + /* qu'on ne recrée pas un newsgroup. */ + creation=un_nouveau_newsgroup(tcp_line_read); + if (!(creation->flags & GROUP_NEW_GROUP_FLAG)) { + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + continue; + } + Newsgroup_courant=creation; + creation->flags=GROUP_NEW_GROUP_FLAG; + good=bad=0; + if(Options.auto_ignore) + bad=(regexec(&badreg,creation->name,0,NULL,0)==0)?1:0; + if(Options.auto_subscribe) + good=(regexec(&goodreg,creation->name,0,NULL,0)==0)?1:0; + creation->flags |= + (!Options.default_subscribe && !good)?GROUP_UNSUBSCRIBED:0; + if (Options.subscribe_first) + creation->flags |= (bad)?GROUP_UNSUBSCRIBED:0; + else + creation->flags |= (bad&&!good)?GROUP_UNSUBSCRIBED:0; + + if (opt_c) fprintf(stdout, "Nouveau groupe : %s\n", creation->name); + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + } while (1); + if (Options.auto_subscribe) regfree(&goodreg); + if (Options.auto_ignore) regfree(&badreg); +} + +/* Creation du .flrnrc a partir de la liste des newsgroups */ +/* libere la memoire occupee par les groupes */ +void free_groups(int save_flnewsrc) { + FILE *flnews_file=NULL; + Newsgroup_List *tmpgroup=NULL; + Range_List *msg_lus,*tmprange; + int lu_index; + int first=1; + int write_flnewsrc=save_flnewsrc; + + if (debug) fprintf(stderr, "Sauvegarde du .flnewsrc\n"); + if (write_flnewsrc) + { + flnews_file = open_flrnfile(".flnewsrc.new","w+",1); + if (flnews_file==NULL) write_flnewsrc=0;; + } + for (Newsgroup_courant=Newsgroup_deb;Newsgroup_courant; + Newsgroup_courant=tmpgroup) { + tmpgroup=Newsgroup_courant->next; + if (Newsgroup_courant->description) free(Newsgroup_courant->description); + /* on construit la ligne du .flnews */ + if (write_flnewsrc) + fprintf(flnews_file,"%s%c ",Newsgroup_courant->name, + (Newsgroup_courant->flags & GROUP_UNSUBSCRIBED)?'!':':'); + msg_lus=Newsgroup_courant->read; + first=1; + if (write_flnewsrc) { + while (msg_lus) + { + for (lu_index=0;lu_indexmin[lu_index]==0) continue; + if (!first) fprintf(flnews_file,"%c",','); + first=0; + fprintf(flnews_file,"%d",msg_lus->min[lu_index]); + if (msg_lus->min[lu_index]max[lu_index]) + fprintf(flnews_file,"-%d",msg_lus->max[lu_index]); + } + msg_lus=msg_lus->next; + } + fprintf(flnews_file,"\n"); + } + /* on libere la liste des messages lus */ + tmprange=msg_lus=Newsgroup_courant->read; + while(tmprange) { + msg_lus=tmprange; + tmprange=msg_lus->next; + free(msg_lus); + } + Newsgroup_courant->read=NULL; + if (Newsgroup_courant->Article_deb) { + Article_deb=Newsgroup_courant->Article_deb; + Article_exte_deb=Newsgroup_courant->Article_exte_deb; + libere_liste(); + Newsgroup_courant->Article_deb=NULL; + Newsgroup_courant->Article_exte_deb=NULL; + Newsgroup_courant->article_deb_key=0; + } + free(Newsgroup_courant); + } + if (write_flnewsrc) { + fclose(flnews_file); + if (debug) fprintf(stderr,"Ecriture du .flnewsrc.new finie.\n"); + rename_flnewsfile(".flnewsrc.new",NULL); + if (debug) fprintf(stderr,"Renommage du .flnewsrc fini.\n"); + } + if (debug) fprintf(stderr,"%s","C'est fini\n"); +} + +/* Demande au serveur l'existence d'un newsgroup */ +/* L'ajoute à la liste des newsgroup existants (en fin) */ +/* name contient deja s'il y a lieu les *... */ +Newsgroup_List *cherche_newsgroup_re (char *name, regex_t reg) +{ + int res, code; + char *buf; + buf=safe_malloc((MAX_NEWSGROUP_LEN+12)*sizeof(char)); + strcpy(buf, "active "); + strcat(buf, name); + res=write_command(CMD_LIST, 1, &buf); + free(buf); + if (res<1) return NULL; + code=return_code(); + if ((code<0) || (code>400)) return NULL; + while((res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1))>=0) + { + if (tcp_line_read[1]=='\r') return NULL; /* Existe pas... */ + buf=strchr(tcp_line_read,' '); + *buf='\0'; + if (!regexec(®,tcp_line_read,0,NULL,0)) { + *buf=' '; + buf=safe_strdup(tcp_line_read); + discard_server(); + strcpy(tcp_line_read,buf); free(buf); + return un_nouveau_newsgroup(tcp_line_read); + } + } + return NULL; +} + +Newsgroup_List *cherche_newsgroup (char *name, int exact) { + int res, code; + Newsgroup_List *creation; + char *buf; + + buf=safe_malloc((MAX_NEWSGROUP_LEN+12)*sizeof(char)); + strcpy(buf, "active "); + if (!exact) strcat(buf, "*"); + strcat(buf, name); + if (!exact) strcat(buf, "*"); + res=write_command(CMD_LIST, 1, &buf); + free(buf); + if (res<1) return NULL; + + code=return_code(); + if ((code<0) || (code>400)) return NULL; + + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) return NULL; + if (tcp_line_read[1]=='\r') return NULL; /* Existe pas... */ + + creation=un_nouveau_newsgroup(tcp_line_read); + + discard_server(); + return creation; +} + +/* La fonction est semblable à cherche_newsgroup_re, mais elle créée un */ +/* menu au lieu de renvoyer le premier newsgroup obtenu */ +Liste_Menu *menu_newsgroup_re (char *name, regex_t reg, int avec_reg) +{ + Liste_Menu *lemenu=NULL, *courant=NULL; + int res, code; + Newsgroup_List *creation; + char *buf; + buf=safe_malloc((MAX_NEWSGROUP_LEN+12)*sizeof(char)); + strcpy(buf, "active "); + if (!avec_reg) strcat(buf,"*"); + strcat(buf, name); + if (!avec_reg) strcat(buf,"*"); + res=write_command(CMD_LIST, 1, &buf); + free(buf); + if (res<1) return NULL; + code=return_code(); + if ((code<0) || (code>400)) return NULL; + while((res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1))>=0) + { + if (tcp_line_read[1]=='\r') return lemenu; /* On peut repartir */ + buf=strchr(tcp_line_read,' '); + *buf='\0'; + if ((!avec_reg) || (!regexec(®,tcp_line_read,0,NULL,0))) { + *buf=' '; + creation=un_nouveau_newsgroup(tcp_line_read); + courant=ajoute_menu(courant,creation->name,creation); + if (lemenu==NULL) lemenu=courant; + } + } + return lemenu; +} + +/* Renvoie -1 en cas d'erreur */ +/* 0 s'il n'y a rien de neuf */ +/* le nombre d'articles ajoutés s'il y a pas trop de neuf */ +/* -2 s'il y a beaucoup de neuf */ +int cherche_newnews() { + char *buf; + int res,code; + struct tm *gmt_check; + char param[32]; + char *Message_id[25]; + int nombre,i; + time_t actuel; + + /* On envoie un newnews au serveur */ + buf=safe_malloc((MAX_NEWSGROUP_LEN+50)*sizeof(char)); + strcpy(buf, Newsgroup_courant->name); + gmt_check=gmtime(&Date_groupe); + res=strftime(param, 20, " %y%m%d %H%M%S GMT", gmt_check); + param[res]='\0'; + + strcat(buf,param); + if (debug) fprintf(stderr, "Date formée : %s\n",buf); + actuel=time(NULL); + res=write_command(CMD_NEWNEWS, 1, &buf); + free(buf); + code=return_code(); + if ((code<0) || (code>400)) return -1; + nombre=0; + + for(nombre=0; nombre<25;nombre++) { + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) return -1; + if (tcp_line_read[1]!='\r') + Message_id[nombre] = safe_strdup(tcp_line_read); + else break; + } + if (tcp_line_read[1]!='\r') + { + discard_server(); + for (i=0;i<25;i++) + free(Message_id[i]); + return -2; + } + if(nombre >0) { + int articles_ajoutes, mettre_time; + + articles_ajoutes=0; + mettre_time=1; + + /* a remplacer par une insertion du message dans la liste */ + for (i=0;iname); + res=write_command(CMD_LIST, 1, &buf); + free(buf); + if (res<3) return -1; + + code=return_code(); + if ((code<0) || (code>400)) return -1; + + res=read_server(tcp_line_read, 1, MAX_READ_SIZE-1); + if (res<0) return -1; + if (tcp_line_read[1]=='\r') { + return -2; /* Pas glop, tout ca */ + } + /* Normalement, une ligne de lecture suffit amplement */ + buf=strchr(tcp_line_read,' '); + group->max=strtol(buf,&buf,0); + group->min=strtol(buf,&buf,0); + + discard_server(); /* Si il y a plusieurs newsgroups, BEURK */ + + non_lus=group->max-group->min+1; + if (group->max==0) { return 0; /* le groupe est vide */ + } + actuel=group->read; + while (actuel) { + for (i=0; imax[i]min) continue; + if (actuel->min[i]min) non_lus-=actuel->max[i]-group->min+1; + else non_lus-=actuel->max[i]-actuel->min[i]+1; + } + actuel=actuel->next; + } + if (non_lus<0) non_lus=0; + + return non_lus; +} + +void zap_newsgroup(Newsgroup_List *mygroup) { + Newsgroup_List *pere=Newsgroup_deb; + Range_List *range1, *range2; + + if (pere==mygroup) { + Newsgroup_deb=mygroup->next; + if (mygroup->next) mygroup->next->prev=NULL; + } + else { pere=mygroup->prev; + pere->next=mygroup->next; + if (mygroup->next) mygroup->next->prev=pere; + } + if (mygroup->description) free(mygroup->description); + range1=mygroup->read; + while (range1) { + range2=range1->next; + free(range1); + range1=range2; + } + /* on les garde pour pouvoir avoir des ptr dessus */ + /* Ca fait gagner 128 octet par tag soit 64 K... */ + mygroup->description=NULL; + mygroup->read=NULL; + mygroup->prev=NULL; + mygroup->next=Newsgroup_deleted; + Newsgroup_deleted=mygroup; + if (mygroup->next) mygroup->next->prev=mygroup; +/* free(mygroup); */ + return; +} + +/* c'est un code penible... Mais bon, j'ai pas choisi un design simple, + * tant pis pour moi */ +void copy_range_list(Range_List *source, int source_index, Range_List *dest, + int dest_index) +{ + int delta=dest_index-source_index; + int start=source_index+((delta>=0)?0:-delta); + int i,j; + + do { + for (i=start+((delta>=0)?0:-delta); + i=0)?delta:0); i++) + { + dest->min[i+delta] = source->min[i]; + dest->max[i+delta] = source->max[i]; + } + if (delta>0) { + if (source->next) { source=source->next; } + else { + for (j=RANGE_TABLE_SIZE-delta; jmin[j]=dest->max[j]=0; + return; + } + for (i=0, j=RANGE_TABLE_SIZE-delta; jmin[j] = source->min[i]; + dest->max[j] = source->max[i]; + } + } + if (delta<0) { + dest->next=safe_calloc(1,sizeof(Range_List)); + dest=dest->next; + for (i=0,j=RANGE_TABLE_SIZE+delta;jmin[i] = source->min[j]; + dest->max[i] = source->max[j]; + } + if (source->next) { source=source->next; } + else return; + + } + if (delta==0) { + if (source->next) { source=source->next; } + else return; + dest->next=safe_calloc(1,sizeof(Range_List)); + dest=dest->next; + } + start=0; + } while(1); +} + +void add_read_article(Newsgroup_List *mygroup, int article) +{ + Range_List *range1, *range2, *pere; + int lu_index; + int first=0; + int borne=0; + int i; + pere=NULL; + range1=mygroup->read; + lu_index=0; + while(range1) { + for(lu_index=0; lu_indexmax[lu_index]>=article) && (range1->min[lu_index]<=article)) + return; /* l'article est deja lu */ + if ((range1->max[lu_index]==0)|| (range1->min[lu_index]>article)) + break; + } + if ((range1->max[lu_index]==0)|| (range1->min[lu_index]>article)) break; + pere=range1; + range1=range1->next; + } + if (range1) { + if (range1->min[lu_index]==article+1) { range1->min[lu_index]=article; + borne=1;} + if (lu_index==0) { + if (pere) { range1=pere; lu_index=RANGE_TABLE_SIZE-1; } + first=1; + } else lu_index--; + if (range1->max[lu_index] == article-1) { + range1->max[lu_index]=article; + borne++; + } + } else first=1; + if (borne==1) return; + if (borne==2) { + range2=safe_calloc(1,sizeof(Range_List)); + for (i=0;i<=lu_index;i++) { + range2->min[i]=range1->min[i]; + range2->max[i]=range1->max[i]; + } + if (lu_index==RANGE_TABLE_SIZE-1) + { range1=range1->next; + range2->max[lu_index] = range1->max[0]; + range2->next=safe_calloc(1,sizeof(Range_List)); + range2=range2->next; + copy_range_list(range1,1,range2,0); + } else { + range2->max[lu_index]=range1->max[lu_index+1]; + if (lu_index==RANGE_TABLE_SIZE-2) { + range1=range1->next; + copy_range_list(range1,0,range2,lu_index+1); + } else + copy_range_list(range1,lu_index+2,range2,lu_index+1); + } + if (pere) pere->next=range2; + else mygroup->read=range2; + range2=range1; + while(range2) { + range1=range2; + range2=range2->next; + free(range1); + } + return; + } + + range2=safe_calloc(1,sizeof(Range_List)); + if (!first) lu_index++; + range2->min[lu_index]=article; + range2->max[lu_index]=article; + if (!range1) { mygroup->read=range2; return;} + + for (i=0;imin[i]=range1->min[i]; + range2->max[i]=range1->max[i]; + } + + if (lu_index==RANGE_TABLE_SIZE-1) + { + range2->next=safe_calloc(1,sizeof(Range_List)); + range2=range2->next; + copy_range_list(range1,lu_index,range2,0); + } else + copy_range_list(range1,lu_index,range2,lu_index+1); + if (pere) pere->next=range2; + else mygroup->read=range2; + range2=range1; + while(range2) { + range1=range2; + range2=range2->next; + free(range1); + } + return; +} diff --git a/src/group.h b/src/group.h new file mode 100644 index 0000000..f1ed6b6 --- /dev/null +++ b/src/group.h @@ -0,0 +1,50 @@ +/* flrn v 0.1 */ +/* group.h 24/11/97 */ +/* */ +/* Headers pour la gestion des newsgroups. */ + +#ifndef FLRN_GROUP_H +#define FLRN_GROUP_H + +#include "flrn_config.h" + +#ifdef TM_IN_SYS_TIME +#include +#else +#include +#endif + +/* (on pourra mettre ca en option par la suite) */ + +#define RANGE_TABLE_SIZE 10 +typedef struct Range_Lu +{ + struct Range_Lu *next; + int min[RANGE_TABLE_SIZE], max[RANGE_TABLE_SIZE]; +} Range_List; + +typedef struct Group_List +{ + struct Group_List *next; + struct Group_List *prev; + unsigned int flags; +#define GROUP_UNSUBSCRIBED 0x001 +#define GROUP_NEW_GROUP_FLAG 0x002 + + char name[MAX_NEWSGROUP_LEN + 1]; + + char *description; /* Description du newsgroup (quand c'est defini :( ) */ + int min; /* Le numero de l'article minimum disponible */ + int max; /* Le max du newsgroup */ + Range_List *read; /* Les articles lus */ + long article_deb_key; + Article_List *Article_deb, *Article_exte_deb; +} Newsgroup_List; + + +extern Newsgroup_List *Newsgroup_courant; +extern Newsgroup_List *Newsgroup_deb; + +extern time_t Last_check; + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2041426 --- /dev/null +++ b/src/main.c @@ -0,0 +1,109 @@ +/* flrn v 0.1 */ +/* main.c 22/11/97 */ +/* */ +/* Programme principal de flrn. Pour l'intant, ne fait quasiment */ +/* rien (normal, ce n'est pas prevu pour faire quelque chose). */ +/* */ + +#include +#include +#include +#include +#include +#include +#include + +#include "flrn.h" +#include "options.h" +#include "group.h" +#include "version.h" + +int debug; +struct passwd *flrn_user; + +void Usage(char *argv[]) +{ + printf("Utilisation : %s [-d] [-c] [-v] [-s] []\n",argv[0]); +} + +void Help(char *argv[]) +{ + Usage(argv); + printf( +" -d|--debug : affiche plein d'informations sur la sortie d'erreur\n" +" -c|--co : donne les nouveaux newsgroups, et le nombre d'articles non lus\n" +" -s|--show-config : donne un .flrn avec les valeurs par defaut.\n" +" -v|--version: donne la version et quitte\n" +" -h|--help : affiche ce message\n\n%s\n",version_string); +} + +int main(int argc, char *argv[]) +{ + int code, res=0; + int opt_c=0; + int i; + char *newsgroup=NULL; + + debug=0; + /* J'aime pas les sigpipe */ + { + struct sigaction ign; + /* les champs n'ont pas le même nom sous tous les os */ + /* par contre, on veut toujours les mettre a 0 */ + memset(&ign,0,sizeof(struct sigaction)); + ign.sa_handler = SIG_IGN; + sigaction(SIGPIPE,&ign,NULL); + } + + for (i=1; i500) fprintf(stderr, "Le serveur refuse la connection.\n"); + return 1; + } + adjust_time(); + get_overview_fmt(); + + if (!opt_c) { + res=Init_screen(); + if (res==0) return 1; + res=Init_keyboard(); + if (res<0) return 1; + } + + init_groups(); + new_groups(opt_c); + if (!opt_c) res=loop(newsgroup); else aff_opt_c(); + quit_server(); + free_groups(res); + if (!opt_c) { + Reset_screen(); + Reset_keyboard(); + } + free_options(); + fprintf(stdout,"That's all folks !\n"); + return 0; +} diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..e2a431c --- /dev/null +++ b/src/options.c @@ -0,0 +1,463 @@ +/* flrn v 0.1 */ +/* options.c 22/11/97 */ +/* */ +/* Gestion des options... */ +/* */ +/* */ + +#include +#include +#include +#include + +#include "flrn.h" +#include "art_group.h" +#define IN_OPTION_C +#include "options.h" +#include "site_config.h" + +static char *delim = "=: \t\n"; + +Known_Headers unknown_Headers[MAX_HEADER_LIST]; + + +void var_comp(char *var, int len) +{ + char *buf=var; + int used; + char *guess=NULL; + int prefix_len=0; + int match=0; + int i,j; + if (strncmp(buf,"no",2)==0) buf +=2; + used=strlen(buf); + for (i=0; i< NUM_OPTIONS; i++) + if(strncmp(buf, All_options[i].name,used)==0) + if (!All_options[i].flags.lock) { + if (match) { + if (!prefix_len) prefix_len=strlen(guess); + for (j=0;j1) { + if (prefix_len < len) {strncpy(buf, guess, prefix_len); + buf[prefix_len]='\0'; + } + } + return; +} + +/* il faut que len soit plus grand que touts les noms de commandes + * on pourait ajouter pour la completude la completion apres color + * mais ca veut dire reecrire le code qui gere ca... */ +void options_comp(char *option, int len) +{ + int used; + int res; + char *my_option=safe_malloc(len); + char *buf; + + strcpy(my_option,option); + buf=strtok(my_option,delim); + if (!buf) {free(my_option); return;} + used=strlen(buf); + + if (strncmp(buf,OPT_SET,used)==0) { + if (used0) return i-1; + if (strcasecmp(buf,"others")==0) return NB_KNOWN_HEADERS; + buf2=strpbrk(buf,delim); + if (buf2==NULL) l=strlen(buf); else l=buf2-buf; + for (i=0;i