ؕ~ TIN-1_22.BCKl TIN-1_22.BCKHBACKUP/LOG [SRC.TIN-1_22...]*.*; $1$DUA4:[ANONYMOUS.TIN]TIN-1_22.BCK/SAV MCQUILLIN /@[]V5.5 _HOBBES::  _$1$DUA4: V5.5-2 ~ y*[SRC.TIN-1_22]$M$AKEFILE.;1+,e.-// 4-- -d0@123KPWO.56`D.t7U^189]VG/HJZ# Makefile for tin - for tin compiler flag options read INSTALL and README. # # Options that may need changing. For advanced options read the INSTALL file. # CC = cc COPTS = -c -O #CC = gcc #COPTS = -c -g # LD has to be changed to ln for AmigaDOS LD = $(CC) YACC = yacc #YACC = bison -y #DEBUG = DEBUG = -g -DDEBUG # Where are your news libdir & spooldir? LIBDIR = /usr/lib/news SPOOLDIR= /usr/spool/news NOVROOTDIR= $(SPOOLDIR) # Where do you want the binary & manual page installed? INS_BINARY_DIR = /usr/local/bin INS_DAEMON_DIR = $(LIBDIR) INS_MANUAL_DIR = /usr/local/man/man INS_MANUAL_EXT = 1 # From: address in posted articles (don't use both - read the INSTALL file) NNTP_INEWS_GATEWAY= NNTP_INEWS_DOMAIN= #.erlm.siemens.de # Default nntp server (can be overridden by NNTPSERVER environment variable) NNTP_DEFAULT_SERVER=# news.erlm.siemens.de # Use INN clientlibs config functions (add -DUSE_INN_NNTPLIB to COPTS line) INN_NNTPLIB= AMIGA_COPTS=-so -wc -m0s -DM_AMIGA -DSYSV -DSLOW_SCREEN_UPDATE \ -DNO_PIPING -DNO_SHELL_ESCAPE -DAMIGA_BBS -DLIBDIR=""uulib:"" \ -DSPOOLDIR=""uunews:"" PROJECT = tin EXE = tin EXED = tind MAKE = make SHELL = /bin/sh STRIP = strip STRIP2 = mcs -d #ROFF = nroff -man ROFF = groff -Tascii -man #BASE_VER= 1.21/tin-1.21 BASE_VER= 170993 VER = 1.22 MAIL_ADDR = "iain.lea@erlm.siemens.de" HFILES = config.h tin.h extern.h nntplib.h proto.h stpwatch.h amiga.h os_2.h \ win32.h CFILES = active.c amiga.c art.c curses.c debug.c envarg.c feed.c getline.c \ group.c hashstr.c help.c inews.c init.c kill.c lang.c mail.c \ main.c memory.c misc.c newsrc.c nntplib.c open.c os_2.c page.c \ parsdate.y post.c prompt.c rcfile.c save.c screen.c search.c \ select.c sigfile.c signal.c spooldir.c strftime.c thread.c \ wildmat.c win32.c xref.c OFILES = active.o amiga.o art.o curses.o debug.o envarg.o feed.o getline.o \ group.o hashstr.o help.o inews.o init.o kill.o lang.o mail.o \ main.o memory.o misc.o newsrc.o nntplib.o open.o os_2.o page.o \ parsdate.o post.o prompt.o rcfile.o save.o screen.o search.o \ select.o sigfile.o signal.o spooldir.o strftime.o thread.o \ wildmat.o win32.o xref.o SUPPORT = Makefile Makefile.ami Makefile.bcc Makefile.icc MANIFEST README \ README.AMI README.OS2 CHANGES INSTALL HACKERS TODO FTP \ *.[13] $(EXE).nrf $(EXE).lsm actived.c NNTP_PATCH = README.NNTP INSTALL.NNTP common.patch server.patch xindex.c \ xmotd.c xoverview.c xuser.c ALL_FILES = $(SUPPORT) $(NNTP_PATCH) $(HFILES) patchlev.h $(CFILES) LINTFLAGS=-a -c -h -n -x all: @echo "Makefile for the TIN v$(VER) Usenet newsreader. Configuration:" @echo " " @echo " Compiler=[$(CC)] Copts=[$(COPTS)] INN Lib=[$(INN_NNTPLIB)]" @echo " Install Bindir=[$(INS_BINARY_DIR)] Mandir=[$(INS_MANUAL_DIR)$(INS_MANUAL_EXT)] Manext=[$(INS_MANUAL_EXT)]" @echo " News Lib=[$(LIBDIR)] Spool=[$(SPOOLDIR)] Nov=[$(NOVROOTDIR)]" @echo " News Gateway=[$(NNTP_INEWS_GATEWAY)] Domain=[$(NNTP_INEWS_DOMAIN)] Server=[$(NNTP_DEFAULT_SERVER)]" @echo " " @echo "If the above options are OK make one of the following targets:" @echo " " @echo " make bsd [ BSD[I] / DGUX / NeXT / OSF1 / Pyramid / SunOS / Ultrix (nntp)]" @echo " make sysv [ SysV / HPUX 7&8 ] make sysvr4 [ SysVR4 / HPUX 9 ]" @echo " make aix [ IBM AIX (nntp) ] make amiga [ AmigaDOS ]" @echo " make apollo [ Apollo DomainOS ] make coherent [ Coherent 4.0 ]" @echo " make dynix [ Sequent DYNIX (nntp) ] make irix [ SGI Irix ]" @echo " make linux [ Linux (nntp) ] make minix [ Minix 386 ]" @echo " make mips [ Mips / CDC EPIX ] make ptx [ Sequent PTX ]" @echo " make qnx [ QNX 4.1 ] make sco [ SCO Unix ]" @echo " make sinix [ SNI Sinix ] make tower [ NCR Tower]" @echo " make xenix [ SCO Xenix 386 ]" @echo " " @echo "Note that targets marked with '(nntp)' have -DNNTP_ABLE defined automatically." @echo " " .c.o: $(CC) $(CFLAGS) $*.c # Uncomment for COHERENT os #.c.y: # $(YACC) $*.y # For IBM AIX aix: @echo "Compiling $(EXE) v$(VER) for IBM AIX..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSYSV -DRS6000 \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit # For Commodore AmigaOS amiga: @echo "Compiling $(EXE) v$(VER) for AmigaDOS (Manx-C 5.2)..." $(MAKE) CC=$(CC) "CFLAGS=$(AMIGA_COPTS)" LIBS=-lc EXE=tin linkit # For Apollo apollo: @echo "Compiling $(EXE) v$(VER) for Apollo..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit # For BSD-derived systems bsd: @echo "Compiling $(EXE) v$(VER) for BSD/BSDI/DGUX/NeXT/OSF1/Pyramid/SunOS/Ultrix..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DBSD \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit # For Coherent 4.0 # NETLIBS="-lsocket" \ # coherent: @echo "Compiling $(EXE) v$(VER) for Coherent 4.0..." @$(MAKE) CC=$(CC) CFLAGS='-c -DSYSV \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lterm $(INN_NNTPLIB)" \ LFLAGS= EXE=tin linkit # For Sequent DYNIX dynix: @echo "Compiling $(EXE) v$(VER) for Sequent DYNIX..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DBSD \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap -lseq $(INN_NNTPLIB)" \ EXE=tin linkit # For SGI Irix irix: @echo "Compiling $(EXE) v$(VER) for SGI Irix..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -cckr -DSYSV \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lsun -lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit # For Linux linux: @echo "Compiling $(EXE) v$(VER) for Linux (NNTP Ready)..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -static -DSYSV \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit # For Minix 386 minix: @echo "Compiling $(EXE) v$(VER) for Minix 386..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DMINIX -D_POSIX_SOURCE \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses $(INN_NNTPLIB)" \ EXE=tin linkit chmem +131072 $(EXE) # For Mips/CDC EPIX mips: @echo "Compiling $(EXE) v$(VER) for Mips/CDC EPIX..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DBSD -DEPIX -systype bsd43 \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-L/bsd43/usr/lib -lcurses -ltermcap -lc $(INN_NNTPLIB)" \ EXE=tin linkit # For Sequent PTX ptx: @echo "Compiling $(EXE) v$(VER) for Sequent PTX..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DPTX \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap" \ NETLIBS="-lsocket -linet -lnsl_s $(INN_NNTPLIB)" \ EXE=tin linkit # For QNX qnx: @echo "Compiling $(EXE) v$(VER) for QNX 4.1 (Watcom C 8.5E)..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -2 -ml -M -fi=unix.h -Wc,-zt200 -D_POSIX_SOURCE_ -DQNX4 \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses $(INN_NNTPLIB)" \ LFLAGS="-Wc,-zt200 -2 -ml -M -N24K '-Wl,op H=1k'" \ EXE=tin linkit # For SCO Unix # NETLIBS="-lnsl_s -lsocket" \ # sco: @echo "Compiling $(EXE) v$(VER) for SCO Unix..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSYSV -DSCO_UNIX -UM_XENIX \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -lgen -lc_s $(INN_NNTPLIB)" \ EXE=tin linkit # For SNI Sinix sinix: @echo "Compiling $(EXE) v$(VER) for SNI Sinix..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) \ -DNNTP_INEWS_GATEWAY="\\"$(NNTP_INEWS_GATEWAY)\\" \ -DNNTP_INEWS_DOMAIN="\\"$(NNTP_INEWS_DOMAIN)\\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\\"$(LIBDIR)\\" \ -DSPOOLDIR=\\"$(SPOOLDIR)\\" \ -DNOVROOTDIR=\\"$(NOVROOTDIR)\\"' \ LIBS="-lcurses $(INN_NNTPLIB)" \ EXE=tin linkit # For System V # NETLIBS="-lnet -lnsl_s" \ # sysv: @echo "Compiling $(EXE) v$(VER) for System V..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSYSV \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit # For System V Release 4 # NETLIBS="-lnsl -lsocket" \ # sysvr4: @echo "Compiling $(EXE) v$(VER) for System V Release 4..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSVR4 \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit # For NCR Tower # NETLIBS="-lnet -lnsl_s" \ # tower: @echo "Compiling $(EXE) v$(VER) for NCR Tower..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSYSV -DNCR -DISTRING \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit # For Xenix 386 # NETLIBS="-lsocket" \ # xenix: @echo "Compiling $(EXE) v$(VER) for Xenix 386..." @$(MAKE) CC=$(CC) CFLAGS='-c -Zi -DSYSV \ -DNNTP_INEWS_GATEWAY="\\"$(NNTP_INEWS_GATEWAY)\\" \ -DNNTP_INEWS_DOMAIN="\\"$(NNTP_INEWS_DOMAIN)\\" \ -DNNTP_DEFAULT_SERVER="\\"$(NNTP_DEFAULT_SERVER)\\" \ -DLIBDIR=\\"$(LIBDIR)\\" \ -DSPOOLDIR=\\"$(SPOOLDIR)\\" \ -DNOVROOTDIR=\\"$(NOVROOTDIR)\\"' \ LIBS="-lcurses -ltinfo -lx $(INN_NNTPLIB)" \ LFLAGS=-Zi EXE=tin linkit # !!! THE FOLLOWING ARE SITE SPECIFIC - IGNORE !!! ccenter_obj: @echo "Compiling a CL target for SUN01 with NNTP ONLY..." #setopt program_name $(EXE) #setopt sys_load_flags -Dsun -Dsparc -I/usr/include -I. -L/usr/lib -lcurses -ltermcap #load -c -g -DBSD -DNNTP_ABLE -DNNTP_ONLY -lcurses -ltermcap $(CFILES) #link # @$(MAKE) CC=gcc CFLAGS='-c -O2 -Wall -DSYSV -DHAVE_ISPELL -DNNTP_ABLE -DDONT_HAVE_NNTP_EXTS -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" -DDEBUG' \ # anl433: @echo "Compiling $(EXE) v$(VER) for ANL433 with NNTP..." @$(MAKE) CC=gcc CFLAGS='-c -O -Wall -DSYSV -DHAVE_MAIL_HANDLING -DHAVE_ISPELL -DNNTP_ABLE -DDEBUG \ -DDONT_HAVE_NNTP_EXTS \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ NETLIBS="-linet -lnsl_s" \ EXE=tin linkit daemon: @echo "Compiling $(EXE) v$(VER) for ANL433 with INDEX DAEMON..." @$(MAKE) CC=gcc CFLAGS='-c -O2 -DSYSV -DINDEX_DAEMON -DDEBUG' \ EXE=$(EXED) linkit dg01: @echo "Compiling $(EXE) v$(VER) for DG01 with NNTP ONLY..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DBSD -DNNTP_ONLY -DDEBUG \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit idefix: @echo "Compiling $(EXE) v$(VER) for IDEFIX with NNTP ONLY..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSYSV -DSCO_UNIX -UM_XENIX -DNNTP_ONLY \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ NETLIBS="-lnsl_s -lsocket" \ LIBS="-lcurses -lgen -lc_s $(INN_NNTPLIB)" \ EXE=tin linkit os2unix: @echo "Compiling $(EXE) v$(VER) for OS2UNIX with NNTP ONLY..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSYSV -DNNTP_ONLY -DDEBUG \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap -lx $(INN_NNTPLIB)" \ NETLIBS="-linet -lnsl_s" \ EXE=tin linkit mx331: @echo "Compiling $(EXE) v$(VER) for MX331 with NNTP ONLY..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSYSV -DNNTP_ONLY \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses $(INN_NNTPLIB)" \ EXE=tin linkit mx351: @echo "Compiling $(EXE) v$(VER) for MX351 with NNTP ONLY..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DSVR4 -DNNTP_ONLY \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ NETLIBS="-lnsl -lsocket" \ LIBS="-lcurses -ltermlib $(INN_NNTPLIB)" \ EXE=tin linkit sparc10_1: @echo "Compiling $(EXE) v$(VER) for SPARC10 with NNTP ONLY..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DBSD -DNNTP_ONLY \ -DNNTP_INEWS_GATEWAY=\"$(NNTP_INEWS_GATEWAY)\" \ -DNNTP_INEWS_DOMAIN=\"$(NNTP_INEWS_DOMAIN)\" \ -DNNTP_DEFAULT_SERVER=\"$(NNTP_DEFAULT_SERVER)\" \ -DLIBDIR=\"$(LIBDIR)\" \ -DSPOOLDIR=\"$(SPOOLDIR)\" \ -DNOVROOTDIR=\"$(NOVROOTDIR)\"' \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=tin linkit sparc10cd: @echo "Compiling $(EXE) v$(VER) for SPARC10 with CDROM ABLE..." @$(MAKE) CC=$(CC) CFLAGS='$(COPTS) -DBSD -DCDROM_ABLE -DDEBUG' \ NNTPLIB="$(CDLIB)" \ LIBS="-lcurses -ltermcap $(INN_NNTPLIB)" \ EXE=cdtin linkit linkit: $(OFILES) @echo "Linking $(EXE) v$(VER)..." $(LD) $(LFLAGS) -o $(EXE) $(OFILES) $(NNTPLIB) $(NETLIBS) $(LIBS) @ls -l $(EXE) install: @echo "Installing $(EXE) v$(VER)..." @$(STRIP) $(EXE) @-$(STRIP2) $(EXE) @-mv $(EXE) $(INS_BINARY_DIR) @-rm $(INS_BINARY_DIR)/r$(EXE) @ln $(INS_BINARY_DIR)/$(EXE) $(INS_BINARY_DIR)/r$(EXE) @chmod 755 $(INS_BINARY_DIR)/$(EXE) @ls -l $(INS_BINARY_DIR)/$(EXE) $(INS_BINARY_DIR)/r$(EXE) @cp $(EXE).1 $(INS_MANUAL_DIR)$(INS_MANUAL_EXT)/$(EXE).$(INS_MANUAL_EXT) @chmod 644 $(INS_MANUAL_DIR)$(INS_MANUAL_EXT)/$(EXE).$(INS_MANUAL_EXT) install_setuid: @echo "Installing SETUID $(EXE) v$(VER)..." @$(STRIP) $(EXE) @-$(STRIP2) $(EXE) @-mv $(EXE) $(INS_BINARY_DIR) @chown news $(INS_BINARY_DIR)/$(EXE) @chgrp news $(INS_BINARY_DIR)/$(EXE) @-rm $(INS_BINARY_DIR)/r$(EXE) @ln $(INS_BINARY_DIR)/$(EXE) $(INS_BINARY_DIR)/r$(EXE) @su news -c "chmod 6755 $(INS_BINARY_DIR)/$(EXE)" @ls -l $(INS_BINARY_DIR)/$(EXE) $(INS_BINARY_DIR)/r$(EXE) @cp $(EXE).1 $(INS_MANUAL_DIR)$(INS_MANUAL_EXT)/$(EXE).$(INS_MANUAL_EXT) @chmod 644 $(INS_MANUAL_DIR)$(INS_MANUAL_EXT)/$(EXE).$(INS_MANUAL_EXT) install_daemon: @echo "Installing index daemon $(EXED) v$(VER)..." @$(STRIP) $(EXED) @-$(STRIP2) $(EXED) @-mv $(EXED) $(INS_DAEMON_DIR) @chown news $(INS_DAEMON_DIR)/$(EXED) @chmod 0755 $(INS_DAEMON_DIR)/$(EXED) @ls -l $(INS_DAEMON_DIR)/$(EXED) proto: @echo "Generating function prototypes for proto.h..." @echo "#if __STDC__" > PROTO.H @echo " " >> PROTO.H @cproto -e $(CFILES) >> PROTO.H @echo " " >> PROTO.H @echo "#else" >> PROTO.H @echo " " >> PROTO.H @cproto -e -f1 $(CFILES) >> PROTO.H @echo " " >> PROTO.H @echo "#endif" >> PROTO.H @-mv PROTO.H proto.h nroff: @echo "Creating nroff man page..." @-$(ROFF) $(EXE).1 > $(EXE).nrf manifest: @echo "Creating MANIFEST..." @echo "MANIFEST for $(PROJECT)-$(VER) (`date`)" > MANIFEST @echo "----------------------------------------------------" >> MANIFEST @wc -c $(ALL_FILES) >> MANIFEST shar: @echo "Generating shell archive..." @$(MAKE) nroff @$(MAKE) manifest @$(MAKE) chmod @shar -a -F -L75 -n $(PROJECT)-$(VER) -s $(MAIL_ADDR) \ -o ../$(PROJECT)-$(VER) $(ALL_FILES) uuencode: @$(MAKE) tar @echo "Uuencoding $(PROJECT).tar.Z..." @uuencode $(PROJECT).tar.Z $(PROJECT).tar.Z > $(PROJECT).tar.Z.uue @ls -l $(PROJECT).tar.Z.uue diff: @echo "Generating diffs against $(PROJECT)-$(BASE_VER)..." @$(MAKE) nroff @$(MAKE) manifest @$(MAKE) chmod @-mv -f $(PROJECT)-$(VER).diff $(PROJECT)-$(VER).diff- @-diff -rus ../$(BASE_VER) . > $(PROJECT)-$(VER).diff # @-diff -rcs ../$(BASE_VER) . > $(PROJECT)-$(VER).diff @ls -l $(PROJECT)-$(VER).diff patch: @$(MAKE) diff @echo "Generating patch against $(PROJECT)-$(BASE_VER)..." @-mv $(PROJECT)-$(VER).diff $(PROJECT)-$(VER).patch @shar -a -n $(PROJECT)-$(VER) -s $(MAIL_ADDR) -L75 \ -o ../patch-$(VER) $(PROJECT)-$(VER).patch @ls -l ../patch-$(VER).* tar: @echo "Generating compressed tar file..." @-rm $(PROJECT).tar $(PROJECT).tar.Z > /dev/null 2>&1 $(MAKE) chmod @tar cvf $(PROJECT).tar $(ALL_FILES) @echo "Compressing $(PROJECT).tar..." @compress $(PROJECT).tar @ls -l $(PROJECT).tar.Z gtar: @echo "Generating gzipped tar file..." @-rm $(PROJECT).t $(PROJECT).t.[gz] > /dev/null 2>&1 $(MAKE) chmod @tar cvf $(PROJECT).t $(ALL_FILES) @echo "Gzipping $(PROJECT).t..." @gzip $(PROJECT).t @ls -l $(PROJECT).t.[gz] ftptar: @echo "Creating FTP tar file with a directory of $(PROJECT)-$(VER)..." @-mkdir $(PROJECT)-$(VER) @chmod 755 $(PROJECT)-$(VER) @$(MAKE) chmod @cp $(ALL_FILES) $(PROJECT)-$(VER) @tar cvf $(PROJECT)-$(VER).tar $(PROJECT)-$(VER) @echo "Compressing $(PROJECT)-$(VER).tar..." @compress $(PROJECT)-$(VER).tar @ls -l $(PROJECT)-$(VER).tar.Z chmod: @chmod 644 $(ALL_FILES) zip: @echo "Generating zip archive file..." @-rm $(PROJECT).zip > /dev/null 2>&1 @zip $(PROJECT).zip $(ALL_FILES) @ls -l $(PROJECT).zip zoo: @echo "Generating zoo archive file..." @-rm $(PROJECT).zoo > /dev/null 2>&1 @zoo ah $(PROJECT).zoo $(ALL_FILES) @ls -l $(PROJECT).zoo tags: @echo "Generating tags (results in ./tags)..." @-rm tags @ctags $(HFILES) patchlev.h $(CFILES) lint: @echo "Linting source (results in ./LINT)..." @lint $(LINTFLAGS) -DNNTP_ABLE $(CFILES) > LINT clean: @echo "Cleaning..." -/bin/rm -f $(OFILES) clobber: @echo "Clobbering..." /bin/rm -f $(OFILES) $(EXE) tags cflow: @echo "Creating cflow for $(PROJECT)..." @cflow $(CFILES) > cflow.$(PROJECT) & cscope: @echo "Creating cscope database $(PROJECT)..." @cscope $(ALL_FILES) man: @echo "Printing $(EXE) manual..." @$(ROFF) $(EXE).1 | lpr print: for FILE in $(HFILES) patchlev.h $(CFILES) $(SUPPORT); do \ echo "Printing $$FILE..."; \ a2ps $$FILE | lpr; \ done actived: @echo "Compiling actived for AmigaDOS (Manx-C 5.2)..." $(CC) CFLAGS=$(AMIGA_COPTS) actived $(LD) actived -lc active.o: active.c $(HFILES) amiga.o: amiga.c $(HFILES) art.o: art.c $(HFILES) curses.o: curses.c $(HFILES) debug.o: debug.c $(HFILES) envarg.o: envarg.c $(HFILES) feed.o: feed.c $(HFILES) getline.o: getline.c $(HFILES) group.o: group.c $(HFILES) hashstr.o: hashstr.c $(HFILES) help.o: help.c $(HFILES) inews.o: inews.c $(HFILES) init.o: init.c $(HFILES) kill.o: kill.c $(HFILES) lang.o: lang.c $(HFILES) mail.o: mail.c $(HFILES) patchlev.h main.o: main.c $(HFILES) patchlev.h memory.o: memory.c $(HFILES) misc.o: misc.c $(HFILES) newsrc.o: newsrc.c $(HFILES) nntplib.o: nntplib.c $(HFILES) open.o: open.c $(HFILES) patchlev.h os_2.o: os_2.c $(HFILES) page.o: page.c $(HFILES) parsdate.o: parsdate.y $(HFILES) post.o: post.c $(HFILES) patchlev.h prompt.o: prompt.c $(HFILES) rcfile.o: rcfile.c $(HFILES) save.o: save.c $(HFILES) screen.o: screen.c $(HFILES) search.o: search.c $(HFILES) select.o: select.c $(HFILES) sigfile.o: sigfile.c $(HFILES) signal.o: signal.c $(HFILES) spooldir.o: spooldir.c $(HFILES) strftime.o: strftime.c $(HFILES) thread.o: thread.c $(HFILES) wildmat.o: wildmat.c win32.o: win32.c win32.h xref.o: xref.c $(HFILES) *[SRC.TIN-1_22]$M$AKEFILE.AMI;1+,f.// 4o-d0@123KPWO56 yt7U^189]VG/HJ CC = sc LD = $(CC) LFLAGS = link startup=cres EXE = tin MAKE = smake VER = 1.21 LIBS = CFLAGS = optimize stringmerge nostkchk parm=register optinlocal gst=tin.gst \ DEFINE M_AMIGA DEFINE SYSV DEFINE NO_SHELL_ESCAPE DEFINE NO_PIPING \ DEFINE SLOW_SCREEN_UPDATE DEFINE AMIGA_BBS DEFINE LIBDIR="uulib:" \ DEFINE DONT_LOG_USER DEFINE SPOOLDIR="uunews:" HFILES1 = config.h tin.h extern.h nntplib.h proto.h amiga.h os_2.h win32.h HFILES = tin.gst $(HFILES1) CFILES = active.c amiga.c art.c curses.c debug.c envarg.c feed.c getline.c \ group.c hashstr.c help.c inews.c init.c kill.c lang.c mail.c \ main.c memory.c misc.c newsrc.c nntplib.c open.c os_2.c page.c \ parsdate.y post.c prompt.c rcfile.c save.c screen.c search.c \ select.c sigfile.c signal.c spooldir.c strftime.c thread.c \ wildmat.c win32.c xref.c OFILES = active.o amiga.o art.o curses.o debug.o envarg.o feed.o getline.o \ group.o hashstr.o help.o inews.o init.o kill.o lang.o mail.o \ main.o memory.o misc.o newsrc.o nntplib.o open.o os_2.o page.o \ parsdate.o post.o prompt.o rcfile.o save.o screen.o search.o \ select.o sigfile.o signal.o spooldir.o strftime.o thread.o \ wildmat.o win32.o xref.o .c.o: $(CC) $(CFLAGS) $*.c tin: $(OFILES) @echo "Linking $(EXE) v$(VER)..." $(LD) $(LFLAGS) pname=$(EXE) $(OFILES) $(LIBS) @ls -l $(EXE) actived: @echo "Compiling actived for AmigaDOS ..." $(CC) CFLAGS=$(AMIGA_COPTS) actived $(LD) actived $(LIBS) tin.gst: $(HFILES1) $(CC) $(CFLAGS) makegst=tin.gst gst.h active.o: active.c $(HFILES) amiga.o: amiga.c $(HFILES) art.o: art.c $(HFILES) curses.o: curses.c $(HFILES) debug.o: debug.c $(HFILES) envarg.o: envarg.c $(HFILES) feed.o: feed.c $(HFILES) getline.o: getline.c $(HFILES) group.o: group.c $(HFILES) hashstr.o: hashstr.c $(HFILES) help.o: help.c $(HFILES) inews.o: inews.c $(HFILES) init.o: init.c $(HFILES) kill.o: kill.c $(HFILES) lang.o: lang.c $(HFILES) mail.o: mail.c $(HFILES) patchlev.h main.o: main.c $(HFILES) patchlev.h memory.o: memory.c $(HFILES) misc.o: misc.c $(HFILES) newsrc.o: newsrc.c $(HFILES) nntplib.o: nntplib.c $(HFILES) open.o: open.c $(HFILES) patchlev.h os_2.o: os_2.c $(HFILES) page.o: page.c $(HFILES) parsdate.o: parsdate.y $(HFILES) post.o: post.c $(HFILES) patchlev.h prompt.o: prompt.c $(HFILES) rcfile.o: rcfile.c $(HFILES) save.o: save.c $(HFILES) screen.o: screen.c $(HFILES) search.o: search.c $(HFILES) select.o: select.c $(HFILES) sigfile.o: sigfile.c $(HFILES) signal.o: signal.c $(HFILES) spooldir.o: spooldir.c $(HFILES) strftime.o: strftime.c $(HFILES) thread.o: thread.c $(HFILES) wildmat.o: wildmat.c win32.o: win32.c win32.h xref.o: xref.c $(HFILES) *[SRC.TIN-1_22]$M$AKEFILE.BCC;1+,g. // 4 -d0@123KPWO 56`Vt7U^189]VG/HJ#============================================================= # # TIN.MAK - Makefile for project C:\tin\tin.prj # Created on 05/30/93 at 20:25 # #============================================================= .AUTODEPEND #============================================================= # Translator Definitions #============================================================= CC = bcc +TIN.CFG TASM = tasm.exe TLIB = tlib.exe TLINK = tlink LINK = link386 RC = brcc.exe RB = rc.exe LIBPATH = C:\BCOS2\LIB INCLUDEPATH = C:\BCOS2\INCLUDE;c:\curses;c:\tcpip\include;c:\tin #============================================================= # Implicit Rules #============================================================= .c.obj: $(CC) -c {$< } .cpp.obj: $(CC) -c {$< } .asm.obj: $(TASM) -Mx $*.asm,$*.obj .rc.res: $(RC) -r $*.rc #============================================================= # List Macros #============================================================= EXE_DEPENDENCIES = \ \curses\curseso.lib \ parsdate.obj \ curses.obj \ xref.obj \ win32.obj \ wildmat.obj \ thread.obj \ strftime.obj \ spooldir.obj \ signal.obj \ sigfile.obj \ select.obj \ search.obj \ screen.obj \ save.obj \ rcfile.obj \ prompt.obj \ post.obj \ page.obj \ os_2.obj \ open.obj \ nntplib.obj \ newsrc.obj \ misc.obj \ memory.obj \ main.obj \ mail.obj \ lang.obj \ kill.obj \ init.obj \ inews.obj \ help.obj \ hashstr.obj \ group.obj \ getline.obj \ feed.obj \ envarg.obj \ debug.obj \ art.obj \ active.obj #============================================================= # Explicit Rules #============================================================= tin.exe: tin.cfg $(EXE_DEPENDENCIES) $(TLINK) /s /c /Toe /ap /L$(LIBPATH) @&&| C:\BCOS2\LIB\C02.OBJ+ parsdate.obj+ curses.obj+ xref.obj+ win32.obj+ wildmat.obj+ thread.obj+ strftime.obj+ spooldir.obj+ signal.obj+ sigfile.obj+ select.obj+ search.obj+ screen.obj+ save.obj+ rcfile.obj+ prompt.obj+ post.obj+ page.obj+ os_2.obj+ open.obj+ nntplib.obj+ newsrc.obj+ misc.obj+ memory.obj+ main.obj+ mail.obj+ lang.obj+ kill.obj+ init.obj+ inews.obj+ help.obj+ hashstr.obj+ group.obj+ getline.obj+ feed.obj+ envarg.obj+ debug.obj+ art.obj+ active.obj tin,tin C:\BCOS2\LIB\C2.LIB+ C:\BCOS2\LIB\OS2.LIB+ c:\curses\curseso.lib+ C:\TCPIP\LIB\TCPIPDLL.LIB | #============================================================= # Individual File Dependencies #============================================================= parsdate.obj: tin.cfg parsdate.c curses.obj: tin.cfg curses.c xref.obj: tin.cfg xref.c win32.obj: tin.cfg win32.c wildmat.obj: tin.cfg wildmat.c thread.obj: tin.cfg thread.c strftime.obj: tin.cfg strftime.c spooldir.obj: tin.cfg spooldir.c signal.obj: tin.cfg signal.c sigfile.obj: tin.cfg sigfile.c select.obj: tin.cfg select.c search.obj: tin.cfg search.c screen.obj: tin.cfg screen.c save.obj: tin.cfg save.c rcfile.obj: tin.cfg rcfile.c prompt.obj: tin.cfg prompt.c post.obj: tin.cfg post.c page.obj: tin.cfg page.c os_2.obj: tin.cfg os_2.c open.obj: tin.cfg open.c nntplib.obj: tin.cfg nntplib.c newsrc.obj: tin.cfg newsrc.c misc.obj: tin.cfg misc.c memory.obj: tin.cfg memory.c main.obj: tin.cfg main.c mail.obj: tin.cfg mail.c lang.obj: tin.cfg lang.c kill.obj: tin.cfg kill.c init.obj: tin.cfg init.c inews.obj: tin.cfg inews.c help.obj: tin.cfg help.c hashstr.obj: tin.cfg hashstr.c group.obj: tin.cfg group.c getline.obj: tin.cfg getline.c feed.obj: tin.cfg feed.c envarg.obj: tin.cfg envarg.c debug.obj: tin.cfg debug.c art.obj: tin.cfg art.c active.obj: tin.cfg active.c pwd.obj: tin.cfg pwd.c #============================================================= # Compiler Configuration File #============================================================= tin.cfg: copy &&| -L$(LIBPATH) -I$(INCLUDEPATH) -vi- -DM_OS2 -DNNTP_ABLE -DDONT_REREAD_ACTIVE_FILE -DDEBUG -w-par -w-pro -w-pia -w-aus -w-rvl -w-rch | tin.cfg ,'5~ TIN-1_22.BCKhd[SRC.TIN-1_22]$M$AKEFILE.ICC;1 *[SRC.TIN-1_22]$M$AKEFILE.ICC;1+,h.// 4-d0@123KPWO56t7f^189]VG/HJ*# IBM Developer's Workframe/2 Make File Creation run at 22:01:04 on 06/04/93 # Make File Creation run in directory: # C:\TIN; .SUFFIXES: .SUFFIXES: .c tin.exe: \ active.OBJ \ art.OBJ \ curses.OBJ \ debug.OBJ \ envarg.OBJ \ feed.OBJ \ getline.OBJ \ group.OBJ \ hashstr.OBJ \ help.OBJ \ inews.OBJ \ init.OBJ \ kill.OBJ \ lang.OBJ \ mail.OBJ \ main.OBJ \ memory.OBJ \ misc.OBJ \ newsrc.OBJ \ nntplib.OBJ \ open.OBJ \ os_2.OBJ \ page.OBJ \ parsdate.OBJ \ post.OBJ \ prompt.OBJ \ rcfile.OBJ \ save.OBJ \ screen.OBJ \ search.OBJ \ select.OBJ \ sigfile.OBJ \ signal.OBJ \ spooldir.OBJ \ strftime.OBJ \ thread.OBJ \ wildmat.OBJ \ win32.OBJ \ xref.OBJ \ MAKEFILE @REM @< active[] */ int reread_active_file = FALSE; int active_index = -1; /* * Compare two pointers to "group_t" structures - used in qsort. */ int cmp_group_p (group1, group2) group_p *group1; group_p *group2; { return (strcmp ((*group1)->name, (*group2)->name)); } /* * Compare two pointers to "notify_t" structures - used in qsort. */ int cmp_notify_p (notify1, notify2) notify_p *notify1; notify_p *notify2; { return (strcmp ((*notify1)->name, (*notify2)->name)); } /* * Get default array size for active[] from environment (AmigaDOS) * or just return the standard default. */ int get_active_num () { #ifdef ENV_VAR_GROUPS char *ptr; int num; ptr = getenv (ENV_VAR_GROUPS); if (ptr != (char *) 0) { num = atoi (ptr); return (num ? num : DEFAULT_ACTIVE_NUM); } #endif return DEFAULT_ACTIVE_NUM; } /* * Resync active file when SIGALRM signal received that * is triggered by alarm (reread_active_file_secs) call. */ void resync_active_file () { char old_group[PATH_LEN]; if (reread_active_file) { if (cur_groupnum >= 0 && group_top) { strcpy (old_group, active[my_group[cur_groupnum]].name); } else { old_group[0] = '\0'; } free_active_arrays (); max_active = get_active_num (); expand_active (); read_mail_active_file (); read_news_active_file (); read_attributes_file (); read_mailgroups_file (); read_newsgroups_file (); if (! read_cmd_line_groups ()) { read_newsrc (TRUE); toggle_my_groups (show_only_unread_groups, old_group); } set_groupname_len (FALSE); set_alarm_signal (); mail_setup (); group_selection_page (); } } /* * Find group name in active[] array and return index otherwise -1 */ int find_group_index (group) char *group; { int i = -1; long h; h = hash_groupname (group); i = group_hash[h]; /* * hash linked list chaining */ while (i >= 0) { if (strncmp (group, active[i].name, strlen (active[i].name)) == 0) { return (i); } i = active[i].next; } return (-1); } /* * parse line from news or mail active files */ int parse_active_line (line, max, min, moderated) char *line; long *max; long *min; char *moderated; { char *p, *q, *r; if (line[0] == '#' || line[0] == '\n') { return FALSE; } for (p = line; *p && *p != ' ' && *p != '\n'; p++) { continue; } if (*p != ' ') { error_message (txt_bad_active_file, line); return FALSE; } *p++ = '\0'; for (q = p; *q && *q != ' '; q++) { continue; } if (*q != ' ') { error_message (txt_bad_active_file, line); return FALSE;; } *q++ = '\0'; for (r = q; *r && *r != ' '; r++) { continue; } if (*r != ' ') { error_message (txt_bad_active_file, line); return FALSE; } *r++ = '\0'; if (*r) { strcpy (moderated, r); r = (char *) strchr (moderated, '\n'); if (*r) { *r = '\0'; } } *max = (long) atol (p); *min = (long) atol (q); return TRUE; } int parse_newsrc_active_line (fp, max, min, moderated, group) FILE *fp; long *max; long *min; char *moderated; char *group; { char *ptr, *s; char line[NNTP_STRLEN]; char buf[LEN]; while (fgets (buf, sizeof (buf), fp) != (char *) 0) { ptr = my_strpbrk (buf, ":!"); if (! ptr || *ptr != ':') { continue; } *ptr = '\0'; sprintf (line, "group %s", buf); put_server (line); debug_nntp ("parse_newsrc_active_line", buf); get_server (line, sizeof (line)); debug_nntp ("parse_newsrc_active_line", line); ptr = strchr (line, ' '); if (! ptr) { continue; } *ptr++ = '\0'; if (atoi (line) != OK_GROUP) { continue; } ptr = strchr (s=ptr, ' '); if (! ptr) { continue; } *ptr++ = '\0'; /* s now points to num of articles */ ptr = strchr (s=ptr, ' '); if (! ptr) { continue; } *ptr++ = '\0'; /* s now points to minimum article */ *min = atol (s); ptr = strchr (s=ptr, ' '); if (! ptr) { continue; } *ptr++ = '\0'; /* s now points to maximum article */ *max = atol (s); strcpy (group, ptr); strcpy (moderated, "n"); return TRUE; } return FALSE; } /* * Load the news active file into active[] and create copy * of active ~/.tin/active */ void read_news_active_file () { FILE *fp; char buf[LEN]; char moderated[PATH_LEN]; int i; long h, min, max; if ((update && update_fork) || ! update) { wait_message (txt_reading_news_active_file); } if (newsrc_active) { if ((fp = fopen (newsrc, "r")) == NULL) { if (cmd_line) { fputc ('\n', stderr); } error_message (txt_cannot_open, news_active_file); tin_done(1); } } else { if ((fp = open_news_active_fp ()) == NULL) { if (compiled_with_nntp) { sprintf (msg, txt_cannot_open_active_file, news_active_file, progname); error_message (msg, ""); } else { if (cmd_line) { fputc ('\n', stderr); } error_message (txt_cannot_open, news_active_file); } tin_done (1); } } if (num_active == -1) { num_active = 0; for (i = 0; i < TABLE_SIZE; i++) { group_hash[i] = -1; } } strcpy (moderated, "y"); while (1) { if (newsrc_active) { if (! parse_newsrc_active_line (fp, &max, &min, moderated, buf)) { break; } } else { if (fgets (buf, sizeof (buf), fp) == NULL) { break; } if (! parse_active_line (buf, &max, &min, moderated)) { continue; } } /* * Don't load group into active[] from active file if * 'x' junked group * '=' aliased group */ if (moderated[0] != 'x' && moderated[0] != '=') { /* * Load group into group hash table */ if (num_active >= max_active) { expand_active (); } h = hash_groupname (buf); if (group_hash[h] == -1) { group_hash[h] = num_active; } else { /* hash linked list chaining */ for (i=group_hash[h]; active[i].next >= 0; i=active[i].next) { if (strcmp(active[i].name, buf) == 0) { goto read_news_active_continue; /* kill dups */ } } if (strcmp(active[i].name, buf) == 0) goto read_news_active_continue; active[i].next = num_active; } /* * Load group info. */ active[num_active].type = GROUP_TYPE_NEWS; active[num_active].name = str_dup (buf); active[num_active].spooldir = spooldir; active[num_active].description = (char *) 0; active[num_active].max = max; active[num_active].min = min; active[num_active].moderated = moderated[0]; active[num_active].next = -1; /* hash chaining */ active[num_active].my_group = UNSUBSCRIBED; /* not in my_group[] yet */ active[num_active].unread = 0; active[num_active].newsrcmax = max; active[num_active].newsrcmin = min; active[num_active].newsrcsize = 0; active[num_active].newsrcupdate = FALSE; active[num_active].newsrc = NULL; #ifdef INDEX_DAEMON active[num_active].last_updated_time = 0L; #endif num_active++; } read_news_active_continue:; } fclose (fp); /* * exit if active file is empty */ if (! num_active) { error_message (txt_active_file_is_empty, news_active_file); tin_done (1); } if ((cmd_line && ! update && ! verbose) || (update && update_fork)) { wait_message ("\n"); } check_for_any_new_groups (); } /* * create ~/.tin/active if it does not exist (local news only) */ void backup_active (create) int create; { char buf[PATH_LEN]; FILE *fp; register int i; struct stat sb; joinpath (buf, rcdir, "active"); if (create) { if (stat (buf, &sb) != -1) { return; } } #ifdef VMS if ((fp = fopen (buf, "w", "fop=cif")) != (FILE *) 0) { #else if ((fp = fopen (buf, "w")) != (FILE *) 0) { #endif for (i = 0; i < num_active ; i++) { /* for each group */ fprintf (fp, "%s\n", active[i].name); } fclose (fp); chmod (buf, 0644); } } /* * Check for any newly created newsgroups. * * If reading news locally stat() the active file to get its * size otherwise do a LIST NEWGROUPS to the NNTP server */ void check_for_any_new_groups () { char old_active_file_server[LEN]; char old_active_file_attribute[LEN]; char buf[LEN], *ptr; FILE *fp = (FILE *) 0; group_p *sorted_active = (group_p *) 0; int group_not_found; int ch, num = 0; /* num == num_old_active groups */ int ch_default = 'n'; int update_old_active = FALSE; int max_old_active; int new_active_size; int old_active_size; long epoch; int compared; notify_p old_active = (notify_p) 0; notify_p *sorted_old_active = (notify_p *) 0; char *autosubscribe, *autounsubscribe; register int i, j; struct stat sb; struct tm *tm; if ((! check_for_new_newsgroups || (update && ! update_fork))) { return; } /* * reading news locally (local) or via NNTP (server name) */ if (read_news_via_nntp) { time (&epoch); tm = localtime (&epoch); sprintf (new_active_file_attribute, "%02d%02d%02d %02d%02d%02d", tm->tm_year, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); strcpy (new_active_file_server, nntp_server); max_old_active = 16; } else { backup_active (TRUE); strcpy (new_active_file_server, "local"); max_old_active = num_active; if (stat (news_active_file, &sb) >= 0) { sprintf (new_active_file_attribute, "%ld", sb.st_size); } } /* * find out if we have read news from here before otherwise -1 */ active_index = find_active_size_index (new_active_file_server); if (debug == 2) { if (active_index >= 0) { strcpy (old_active_file_server, active_size[active_index].server); strcpy (old_active_file_attribute, active_size[active_index].attribute); } else { strcpy (old_active_file_server, "UNKNOWN"); strcpy (old_active_file_attribute, "UNKNOWN"); } sprintf (msg, "Active size index=[%d] old=[%s %s] new=[%s %s]", active_index, old_active_file_server, old_active_file_attribute, new_active_file_server, new_active_file_attribute); error_message (msg, ""); sleep (2); } if (! read_news_via_nntp && active_index >= 0) { new_active_size = atoi (new_active_file_attribute); old_active_size = atoi (active_size[active_index].attribute); if (new_active_size <= old_active_size) { goto notify_groups_done; } } /* * Only check if spooldir is active news feed */ if (strcmp (spooldir_alias, "news") != 0) { goto notify_groups_done; } if ((fp = open_newgroups_fp (active_index)) == (FILE *) 0) { goto notify_groups_done; } Raw (TRUE); old_active = (notify_p) my_malloc ((unsigned) sizeof (struct t_notify) * max_old_active); if (old_active == (notify_p) 0) { error_message (txt_out_of_memory, progname); goto notify_groups_done; } while (fgets (buf, sizeof (buf), fp) != NULL) { if (buf[0] == '.') { break; } strncpy (old_active[num].name, buf, sizeof (old_active[num].name)); ptr = old_active[num].name; while (*ptr && *ptr != ' ' && *ptr != '\n') { ptr++; } *ptr = '\0'; old_active[num].visited = FALSE; num++; if (num >= max_old_active) { max_old_active= max_old_active + (max_old_active / 2); old_active= (notify_p) my_realloc( (char *) old_active, (unsigned) sizeof(struct t_notify) * max_old_active); if (old_active == (notify_p) 0) { error_message (txt_out_of_memory, progname); goto notify_groups_done; } } } /* * Check if there are user-set groups to be * automatically subscribed or unsubscribed. */ autosubscribe = getenv ("AUTOSUBSCRIBE"); autounsubscribe = getenv ("AUTOUNSUBSCRIBE"); if (read_news_via_nntp) { for (i = 0 ; i < num ; i++) { if (find_group_index (old_active[i].name) == -1) { continue; } if (! prompt_subscribe_group (old_active[i].name, autosubscribe, autounsubscribe)) { if (cmd_line) { printf ("\r\n"); fflush (stdout); } goto notify_groups_done; } } } else { wait_message (txt_checking_active_file); /* * The following code replaces the original check for new newsgroups, * a source of much aggravation. * * The original algorithm compared each group name in active to (on * average) 1/2 the groups in old_active, for ~N*N/2 string compares. * This works out to ~~ 8,000,000 comparisons for 4000 newsgroups. * * The new algorithm allocates an array of pointers to the active * and old_active array elements, sorts each pointer array with * quicksort, then scans through the two arrays in 1 pass; this takes * 2*N*Log2(N) string compares for the sorts, N for the final pass. * This should work out to ~~ 100,000 comparisons for the same 4000 * newsgroups, a BIG, BIG win. */ /* * Allocate arrays to be sorted */ sorted_active = (group_p *) my_malloc ( (unsigned) sizeof (group_p) * num_active); if (sorted_active == (group_p *) 0) { error_message (txt_out_of_memory, progname); goto notify_groups_done; } sorted_old_active = (notify_p *) my_malloc ( (unsigned) sizeof (notify_p) * num); if (sorted_old_active == (notify_p *) 0) { error_message (txt_out_of_memory, progname); goto notify_groups_done; } /* * Fill arrays to be sorted */ for (i = 0 ; i < num_active ; i++) { sorted_active[i] = &active[i]; } for (j = 0 ; j < num ; j++) { sorted_old_active[j] = &old_active[j]; } /* * Sort the arrays */ qsort (sorted_active, num_active, sizeof (group_p), cmp_group_p); qsort (sorted_old_active, num, sizeof (notify_p), cmp_notify_p); /* * Now do the pass through both arrays at once */ for (i = 0, j = 0 ; i < num_active ; i++) { group_not_found = TRUE; compared = 1; while ((j < num) && (0 > (compared = strcmp ( sorted_old_active[j]->name, sorted_active[i]->name)))) { j++; } if (compared == 0) { /* * found it so read in next group */ group_not_found = FALSE; sorted_old_active[j]->visited = TRUE; j++; } if (group_not_found == FALSE) { continue; } update_old_active = TRUE; /* * auto-subscribe/unsubscribe logic */ if (! prompt_subscribe_group (sorted_active[i]->name, autosubscribe, autounsubscribe)) { if (cmd_line) { printf ("\r\n"); fflush (stdout); } goto notify_groups_done; } } } if (cmd_line) { fputc ('\r', stdout); fflush (stdout); } CleartoEOLN(); if (! read_news_via_nntp) { /* * Look for bogus groups */ ch_default = 'y'; for (j = 0 ; j < num ; j++) { if (old_active[j].visited) { continue; } do { update_old_active= 1; fputc ('\r', stdout); CleartoEOLN (); printf (txt_delete_bogus_group, old_active[j].name, ch_default); fflush (stdout); ch = ReadCh (); if (ch == CR) { ch = ch_default; } } while (! strchr ("nqy\033", ch)); switch (ch) { case 'y': delete_group (old_active[j].name); break; case 'q': case ESC: goto notify_groups_done; case 'n': default: break; } printf ("\r\n"); fflush (stdout); } } /* * write active[] to ~/.tin/active (local spooldir) */ if (! read_news_via_nntp && update_old_active) { backup_active (FALSE); } notify_groups_done: if (fp != (FILE *) 0) { fclose (fp); } /* * update attribute field/create new entry with new size/date */ if (active_index >= 0) { if (active_size[active_index].attribute != (char *) 0) { free (active_size[active_index].attribute); active_size[active_index].attribute = (char *) 0; } active_size[active_index].attribute = str_dup (new_active_file_attribute); } else { sprintf (buf, "%s[%s]", new_active_file_server, new_active_file_attribute); load_active_size_info (buf); } /* * Free malloc'd arrays */ if (old_active != (notify_p) 0) { free ((char *) old_active); old_active = (notify_p) 0; } if (sorted_old_active != (notify_p *) 0) { free ((char *) sorted_old_active); sorted_old_active = (notify_p *) 0; } if (sorted_active != (group_p *) 0) { free ((char *) sorted_active); sorted_active = (group_p *) 0; } if (cmd_line) { Raw (FALSE); } } /* * prompt user if new group should be subscribed to */ int prompt_subscribe_group (group, autosubscribe, autounsubscribe) char *group; char *autosubscribe; char *autounsubscribe; { int ch, ch_default = 'n'; int idx; if ((autosubscribe != (char *) 0) && match_group_list (group, autosubscribe)) { idx = add_group (group, TRUE); subscribe (active[my_group[idx]].name, ':', my_group[idx], FALSE); return TRUE; } else if ((autounsubscribe != (char *) 0) && match_group_list (group, autounsubscribe)) { /* ignore this group */ return TRUE; } do { if (cmd_line) { fputc ('\r', stdout); CleartoEOLN (); } else { clear_message (); } printf (txt_subscribe_to_new_group, group, ch_default); fflush (stdout); ch = ReadCh (); if (ch == CR) { ch = ch_default; } } while (! strchr ("nqy\033", ch)); fputc (ch, stdout); fflush (stdout); switch (ch) { case 'y': idx = add_group (group, TRUE); subscribe (active[my_group[idx]].name, ':', my_group[idx], FALSE); break; case 'q': case ESC: return FALSE; case 'n': default: break; } if (cmd_line) { printf ("\r\n%s", txt_checking); fflush (stdout); } else { wait_message (txt_checking); } return TRUE; } int match_group_list (group, group_list) char *group; char *group_list; { char *separator; char pattern[LEN]; int accept, negate, list_len, group_len; accept = FALSE; list_len = strlen (group_list); /* * walk through comma-separated entries in list */ while (list_len > 0) { /* * find end/length of this entry */ separator = strchr (group_list, ','); if (separator != (char *) 0) { group_len = separator-group_list; } else { group_len = list_len; } if ((negate = ('!' == *group_list))) { /* * a '!' before the pattern inverts sense of match */ group_list++; group_len--; list_len--; } /* * copy out the entry and terminate it properly */ strncpy (pattern, group_list, group_len); pattern[group_len] = (char) 0; /* * do a regexp match using routines used in kill.c */ if (STR_MATCH(group, pattern)) { accept = ! negate; /* matched! */ } /* * now examine next entry if any */ if ((char) 0 != group_list[group_len]) { group_len++; /* skip the separator */ } group_list += group_len; list_len -= group_len; } return (accept); } /* * Per group attributes */ void set_default_attributes () { #ifndef INDEX_DAEMON register int i; for (i = 0; i < num_active ; i++) { active[i].attribute.maildir = default_maildir; active[i].attribute.savedir = default_savedir; active[i].attribute.sigfile = default_sigfile; active[i].attribute.organization = (default_organization[0] ? default_organization : (char *) 0); active[i].attribute.followup_to = (char *) 0; active[i].attribute.printer = default_printer; active[i].attribute.read_during_session = FALSE; active[i].attribute.show_only_unread = default_show_only_unread; active[i].attribute.thread_arts = default_thread_arts; active[i].attribute.sort_art_type = default_sort_art_type; active[i].attribute.show_author = default_show_author; active[i].attribute.auto_save= default_auto_save; active[i].attribute.batch_save = default_batch_save; active[i].attribute.delete_tmp_files = FALSE; active[i].attribute.post_proc_type = default_post_proc_type; } #endif /* INDEX_DAEMON */ } /* * Load the group attributes into active[].attribute from ~/.tin/attributes * * attribute.maildir = STRING * attribute.savedir = STRING * attribute.organization = STRING * attribute.sigfile = STRING * attribute.followup_to = STRING * attribute.printer = STRING * attribute.auto_save = ON/OFF * attribute.batch_save = ON/OFF * attribute.delete_tmp_files = ON/OFF^vu~ TIN-1_22.BCK$d[SRC.TIN-1_22]ACTIVE.C;2Bx|0) * attribute.show_only_unread = ON/OFF * attribute.thread_arts = ON/OFF * attribute.show_author = NUM * 0=none, 1=name, 2=addr, 3=both * attribute.sort_art_type = NUM * 0=none, 1=subj descend, 2=subj ascend * 3=from descend, 4=from ascend * 5=date descend, 6=date ascend * attribute.post_proc_type = NUM * 0=none, 1=unshar, 2=uudecode * 3=uudecode & list zoo (unix) / lha (AmigaDOS) archive * 4=uudecode & extract zoo (unix) / lha (AmigaDOS) archive * 5=uudecode & list zip archive * 6=uudecode & extract zip archive */ void read_attributes_file () { #ifndef INDEX_DAEMON char buf[PATH_LEN]; char line[PATH_LEN]; FILE *fp; int num; int index = -1; set_default_attributes (); if ((fp = fopen (attributes_file, "r")) == (FILE *) 0) { return; } if ((update && update_fork) || ! update) { wait_message (txt_reading_attributes_file); } while (fgets (line, sizeof (line), fp) != NULL) { if (line[0] == '#' || line[0] == '\n') { continue; } if (match_string (line, "newsgroup=", buf, sizeof (buf))) { if (debug == 2) { error_message("group=[%s]",buf); } index = find_group_index (buf); continue; } if (match_string (line, "maildir=", buf, sizeof (buf))) { if (index >= 0) { active[index].attribute.maildir = str_dup (buf); if (debug == 2) { sprintf (msg, "maildir=[%s]", active[index].attribute.maildir); error_message (msg, ""); } } continue; } if (match_string (line, "savedir=", buf, sizeof (buf))) { if (index >= 0) { active[index].attribute.savedir = str_dup (buf); if (debug == 2) { sprintf (msg, "savedir=[%s]", active[index].attribute.savedir); error_message (msg, ""); } } continue; } if (match_string (line, "sigfile=", buf, sizeof (buf))) { if (index >= 0) { active[index].attribute.sigfile = str_dup (buf); if (debug == 2) { sprintf (msg, "sigfile=[%s]", active[index].attribute.sigfile); error_message (msg, ""); } } continue; } if (match_string (line, "organization=", buf, sizeof (buf))) { if (index >= 0) { active[index].attribute.organization = str_dup (buf); if (debug == 2) { error_message("organization=[%s]",active[index].attribute.organization); } } continue; } if (match_string (line, "followup_to=", buf, sizeof (buf))) { if (index >= 0) { active[index].attribute.followup_to = str_dup (buf); if (debug == 2) { error_message("followup_to=[%s]",active[index].attribute.followup_to); } } continue; } if (match_string (line, "printer=", buf, sizeof (buf))) { if (index >= 0) { active[index].attribute.printer = str_dup (buf); if (debug == 2) { error_message("printer=[%s]",active[index].attribute.printer); } } continue; } if (match_boolean (line, "show_only_unread=", &num)) { if (index >= 0) { active[index].attribute.show_only_unread = num; } continue; } if (match_boolean (line, "thread_arts=", &num)) { if (index >= 0) { active[index].attribute.thread_arts = num; } continue; } if (match_boolean (line, "auto_save=", &num)) { if (index >= 0) { active[index].attribute.auto_save = num; } continue; } if (match_boolean (line, "batch_save=", &num)) { if (index >= 0) { active[index].attribute.batch_save = num; } continue; } if (match_boolean (line, "delete_tmp_files=", &num)) { if (index >= 0) { active[index].attribute.delete_tmp_files = num; } continue; } if (match_number (line, "sort_art_type=", &num)) { if (index >= 0) { active[index].attribute.sort_art_type = num; } continue; } if (match_number (line, "show_author=", &num)) { if (index >= 0) { active[index].attribute.show_author = num; } continue; } if (match_number (line, "post_proc_type=", &num)) { if (index >= 0) { active[index].attribute.post_proc_type = num; } continue; } } fclose (fp); if ((cmd_line && ! update && ! verbose) || (update && update_fork)) { wait_message ("\n"); } #endif /* INDEX_DAEMON */ } /* * Save the group attributes from active[].attribute to ~/.tin/attributes */ void write_attributes_file () { #ifndef INDEX_DAEMON FILE *fp; register int i; #ifdef VMS if ((fp = fopen (attributes_file, "w", "fop=cif")) == (FILE *) 0) { #else if ((fp = fopen (attributes_file, "w")) == (FILE *) 0) { #endif return; } if (! cmd_line) { if ((update && update_fork) || ! update) { wait_message (txt_writing_attributes_file); } } fprintf (fp, "# Group attributes file\n#\n"); fprintf (fp, "# newsgroup=STRING (ie. alt.sources) [mandatory]\n#\n"); fprintf (fp, "# maildir=STRING (ie. ~/Mail)\n"); fprintf (fp, "# savedir=STRING (ie. ~user/News)\n"); fprintf (fp, "# organization=STRING\n"); fprintf (fp, "# sigfile=STRING (ie. $var/sig)\n"); fprintf (fp, "# followup_to=STRING\n"); fprintf (fp, "# printer=STRING\n"); fprintf (fp, "# auto_save=ON/OFF\n"); fprintf (fp, "# batch_save=ON/OFF\n"); fprintf (fp, "# delete_tmp_files=ON/OFF\n"); fprintf (fp, "# show_only_unread=ON/OFF\n"); fprintf (fp, "# thread_arts=ON/OFF\n#\n"); fprintf (fp, "# show_author=NUM\n"); fprintf (fp, "# 0=none, 1=name, 2=addr, 3=both\n#\n"); fprintf (fp, "# sort_art_type=NUM\n"); fprintf (fp, "# 0=none, 1=subj descend, 2=subj ascend,\n"); fprintf (fp, "# 3=from descend, 4=from ascend,\n"); fprintf (fp, "# 5=date descend, 6=date ascend\n#\n"); fprintf (fp, "# post_proc_type=NUM\n"); fprintf (fp, "# 0=none, 1=unshar, 2=uudecode,\n"); #ifdef M_AMIGA fprintf (fp, "# 3=uudecode & list lha archive,\n"); fprintf (fp, "# 4=uudecode & extract lha archive\n"); #else fprintf (fp, "# 3=uudecode & list zoo archive,\n"); fprintf (fp, "# 4=uudecode & extract zoo archive\n"); #endif fprintf (fp, "# 5=uudecode & list zip archive,\n"); fprintf (fp, "# 6=uudecode & extract zip archive\n\n"); for (i = 0 ; i < num_active ; i++) { fprintf (fp, "newsgroup=%s\n", active[i].name); fprintf (fp, "maildir=%s\n", active[i].attribute.maildir); fprintf (fp, "savedir=%s\n", active[i].attribute.savedir); fprintf (fp, "organization=%s\n", active[i].attribute.organization); fprintf (fp, "sigfile=%s\n", active[i].attribute.sigfile); fprintf (fp, "followup_to=%s\n", active[i].attribute.followup_to); fprintf (fp, "printer=%s\n", active[i].attribute.printer); fprintf (fp, "show_only_unread=%s\n", (active[i].attribute.show_only_unread ? "ON" : "OFF")); fprintf (fp, "thread_arts=%s\n", (active[i].attribute.thread_arts ? "ON" : "OFF")); fprintf (fp, "auto_save=%s\n", (active[i].attribute.auto_save ? "ON" : "OFF")); fprintf (fp, "batch_save=%s\n", (active[i].attribute.batch_save ? "ON" : "OFF")); fprintf (fp, "delete_tmp_files=%s\n", (active[i].attribute.delete_tmp_files ? "ON" : "OFF")); fprintf (fp, "sort_art_type=%d\n", active[i].attribute.sort_art_type); fprintf (fp, "show_author=%d\n", active[i].attribute.show_author); fprintf (fp, "post_proc_type=%d\n", active[i].attribute.post_proc_type); } fclose (fp); #endif /* INDEX_DAEMON */ } /* * Load the last updated time for each group in the active file so that * tind is more efficient and only has to stat the group dir and compare * the last changed time with the time read from the ~/.tin/active.times * file to determine if the group needs updating. * * alt.sources 71234589 * comp.archives 71234890 */ void read_active_times_file () { #ifdef INDEX_DAEMON char *p, *q; char buf[LEN]; char group[PATH_LEN]; FILE *fp; int i; long updated_time; if ((fp = fopen (active_times_file, "r")) == (FILE *) 0) { return; } while (fgets (buf, sizeof (buf), fp) != NULL) { /* * read the group name */ for (p = buf, q = group ; *p && *p != ' ' && *p != '\t' ; p++, q++) { *q = *p; } *q = '\0'; /* * read the last updated time */ updated_time = atol (p); /* * find the group in active[] and set updated time */ i = find_group_index (group); if (i >= 0) { active[i].last_updated_time = updated_time; } if (debug == 2) { printf ("group=[%-40.40s] [%ld]\n", active[i].name, active[i].last_updated_time); } } fclose (fp); #endif /* INDEX_DAEMON */ } /* * Save the last updated time for each group to ~/.tin/active.times */ void write_active_times_file () { #ifdef INDEX_DAEMON char buf[LEN]; char group[PATH_LEN]; FILE *fp; register int i; #ifdef VMS if ((fp = fopen (active_times_file, "w", "fop=cif")) == (FILE *) 0) { #else if ((fp = fopen (active_times_file, "w")) == (FILE *) 0) { #endif return; } for (i = 0 ; i < num_active ; i++) { fprintf (fp, "%s %ld\n", active[i].name, active[i].last_updated_time); } fclose (fp); #endif /* INDEX_DAEMON */ } void load_active_size_info (info) char *info; { char *ptr_name; char *ptr_size; char buf[PATH_LEN]; int i; /* * initialize active_size[] if no entries */ if (! num_active_size) { for (i = 0 ; i < max_active_size ; i++) { active_size[i].server = (char *) 0; active_size[i].attribute = (char *) 0; } } my_strncpy (buf, info, sizeof (buf)); ptr_name = buf; ptr_name = (char *) strchr (buf, ']'); if (ptr_name != (char *) 0) { *ptr_name = '\0'; } ptr_name = buf; ptr_name = (char *) strchr (buf, '['); if (ptr_name != (char *) 0) { ptr_size = ptr_name; *ptr_name = '\0'; if (num_active_size > max_active_size) { expand_active_size (); } active_size[num_active_size].server = str_dup (buf); active_size[num_active_size].attribute = str_dup (++ptr_size); if (debug == 2) { sprintf (buf, "ACTIVE server=[%s] attribute=[%s]", active_size[num_active_size].server, active_size[num_active_size].attribute); error_message ("%s", buf); } num_active_size++; } } int find_active_size_index (cur_active_server) char *cur_active_server; { int i, found = FALSE; for (i = 0 ; i < num_active_size ; i++) { if (strcmp (cur_active_server, active_size[i].server) == 0) { found = TRUE; break; } } return (found ? i : -1); } /* * check for message of the day (motd) file * * If reading news locally stat() the active file to get its * mtime otherwise do a XMOTD command to the NNTP server */ void read_motd_file () { #ifndef INDEX_DAEMON char buf[LEN]; char motd_file_date[32]; FILE *fp = (FILE *) 0; int lineno = 0; long new_motd_date = 0L; long old_motd_date = 0L; struct stat sb; struct tm *tm; if (update && ! update_fork) { return; } old_motd_date = atol (motd_file_info); /* * reading news locally (local) or via NNTP (server name) */ if (read_news_via_nntp) { if (! old_motd_date) { strcpy (motd_file_date, "920101 000000"); } else { tm = localtime (&old_motd_date); sprintf (motd_file_date, "%02d%02d%02d %02d%02d%02d", tm->tm_year, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } time (&new_motd_date); } else { if (stat (motd_file, &sb) >=0) { new_motd_date = sb.st_mtime; } } if (old_motd_date && new_motd_date <= old_motd_date) { goto read_motd_done; } /* * Only check if spooldir is active news feed */ if (strcmp (spooldir_alias, "news") != 0) { goto read_motd_done; } if ((fp = open_motd_fp (motd_file_date)) != (FILE *) 0) { while (fgets (buf, sizeof (buf), fp) != NULL) { if (buf[0] == '.') { break; } printf ("%s", buf); lineno++; } fclose (fp); if (lineno) { wait_message (txt_cmdline_hit_any_key); Raw (TRUE); ReadCh (); Raw (FALSE); wait_message ("\n"); } } read_motd_done: /* * update motd tinrc entry with new date */ sprintf (motd_file_info, "%ld", new_motd_date); #endif /* INDEX_DAEMON */ } /* * find first occurrence of any char from str2 in str1 */ char * my_strpbrk (str1, str2) char *str1; char *str2; { register char *ptr1; register char *ptr2; for (ptr1 = str1; *ptr1 != '\0'; ptr1++) { for (ptr2 = str2; *ptr2 != '\0';) { if (*ptr1 == *ptr2++) { return (ptr1); } } } return (char *) 0; } *[SRC.TIN-1_22]ACTIVED.C;1+,w.// 4-d0@123KPWO56 8t7c189]VG/HJ/* * Project : tin - a Usenet reader * Module : actived.c * Author : M.Tomlinson & I.Lea * Created : 23-08-92 * Updated : 02-08-93 * Notes : Creates an active file by looking through all the * .next files in the news directories, and writing * this to UULIB:newactive. The UULIB:newsgroups file * must exist. * Copyright : (c) Copyright 1991-93 by Mark Tomlinson & Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include #define NEWSGROUPS_FILE "UULIB:newsgroups" #define NEWSACTIVE_FILE "UULIB:newsactive" main () { char buf[20]; char groupname[81]; char next_path[90]; char *p, last[21]; FILE *fp, *ng, *active; long numgroups = 0, x; if ((ng = fopen (NEWSGROUPS_FILE, "r")) == (FILE *) 0) { perror (NEWSGROUPS_FILE); exit (1); } if ((active = fopen (NEWSACTIVE_FILE, "w")) == (FILE *) 0) { perror (NEWSACTIVE_FILE); exit (1); } while (fgets (groupname, 80, ng)) { for (p = groupname; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++) { ; } *p = 0; sprintf (next_path,"UUNEWS:%s",groupname); for (p = &next_path[7]; *p ; p++) { if (*p == '.') { *p = '/'; /* convert to tree structure */ } } strcat (next_path,"/.next"); if (fp = fopen (next_path,"r")) { fgets (last,20,fp); x = atol (last) - 1; fclose (fp); } else { x = 0; } fprintf (active, "%s %05ld 00001 y\n", groupname, x); numgroups++; } fclose (ng); sprintf (buf, "%d", numgroups+1); setenv (ENV_VAR_GROUPS, buf); } *[SRC.TIN-1_22]AMIGA.C;1+,. // 4 -d0@123KPWO 562t78g189]VG/HJ/* * Project : tin - a Usenet reader * Module : amiga.c * Author : M.Tomlinson & I.Lea * Created : 01-04-91 * Updated : 02-08-93 * Notes : Extra functions for Amiga port * Copyright : (c) Copyright 1991-93 by Mark Tomlinson & Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #if defined(M_AMIGA) #include #include #ifdef __SASC # include # include #else # include # include # include # include #endif #include #include extern struct DosLibrary *DOSBase; int optind = 1; char *optarg; /* * dummy */ int chmod (file, mode) const char *file; int mode; { return 0; } /* * use the task addres for pid which is unique. */ int getpid (void) { return ((long) FindTask(0L) >> 2); } /* * stub for tputs */ #ifndef INDEX_DAEMON int tputs (str, count, func) char *str; int count; int (*func)(int); { if (! str) { return 0; } while (*str) { if (*str == '\n') func('\r'); func(*str++); } return 0; } #endif /* * stub for tzset */ void tzset (void) { #ifdef __SASC __daylight = 0; __timezone = 0; __tzname[0] = __tzname[1] = "GMT"; #endif } /* * joinpath tacks a file (or sub dir) on to the end of a directory name. * Not just as simple as putting a '/' between the two, as the directory * name may be an assign! */ void joinpath (str, dir, file) char *str; char *dir; char *file; { char c; if (strlen (dir) == 0) { strcpy (str, file); return; } c = dir[strlen(dir)-1]; if (c=='/' || c==':') { sprintf (str, "%s%s", dir, file); } else { sprintf (str, "%s/%s", dir, file); } } void sleep (seconds) int seconds; { Delay (50*seconds); } /* * I'm not really sure how well popen and pclose work, but they seem OK */ FILE * popen (command, mode) char *command; char *mode; { char cmd[256]; if (mode[0] == 'w') { sprintf (cmd, "run >NIL: %s", command); Execute (cmd, 0L, 0L); return fopen ("PIPE:", mode); } else { FILE *rp; rp = fopen ("PIPE:", mode); sprintf (cmd,"run %s >PIPE:",command); Execute (cmd, 0L, 0L); return rp; } } void pclose (pipe) FILE *pipe; { fclose (pipe); } /* * Directory stuff */ DIR * opendir (name) char *name; { DIR *di; di = calloc (1, sizeof (DIR)); di->Lock = Lock (name,ACCESS_READ); if (di->Lock == 0) { free (di); return 0; } if (Examine(di->Lock,&di->fib)==0) { UnLock(di->Lock); free (di); return 0; } return di; } struct dirent * readdir (di) DIR *di; { static struct dirent de; if (ExNext (di->Lock, &di->fib) == 0) { return 0; } de.d_name = di->fib.fib_FileName; de.d_reclen = strlen (de.d_name); return &de; } void closedir (di) DIR *di; { UnLock (di->Lock); free (di); } char getopt (argc, argv, options) int argc; char *argv[]; char *options; { char c,*z; static int subind = 0; for (;optind < argc ; optind++) { if (subind == 0) { c = argv[optind][0]; if (c != '-') { return EOF; } subind = 1; } c = argv[optind][subind]; if (c != 0) { break; } } if (optind == argc) { return EOF; } /* get rid of funnies */ if (c == ':' || c == '?') { return '?'; } if ((z = strchr (options,c)) == 0) { return '?'; } if (z[1] == ':') { if (argv[optind][subind+1]) { optarg = &argv[optind][subind+1]; } else { optarg = argv[++optind]; } optind++; subind = 0; return c; } subind++; return c; } int system (str) const char *str; { if (DOSBase->dl_lib.lib_Version >= 36) { return (System ((char *)str, 0L)); } else { return (!Execute((char *)str, 0L, 0L)); } } /* * The stat call in Aztec C doesn't tell us if the entry is a directory * or not. This one does. You will have to change to define * ST_DIRECT though */ int stat (name, buf) char *name; struct stat *buf; { BPTR dirlock; register struct FileInfoBlock *inf; if (! (dirlock = Lock (name, ACCESS_READ))) { return -1; } if (! (inf = malloc(sizeof(*inf)))) { UnLock (dirlock); return -1; } Examine (dirlock,inf); UnLock (dirlock); buf->st_attr = ((inf->fib_EntryType>0) ? ST_DIRECT : 0) | (inf->fib_Protection & 0xf); buf->st_size = inf->fib_Size; buf->st_mtime = ((inf->fib_Date.ds_Days + 2922) * (24 * 60) + inf->fib_Date.ds_Minute) * 60 + inf->fib_Date.ds_Tick / TICKS_PER_SECOND; free (inf); return 0; } /* * This getenv and setenv will use the WB2.0 calls if you have the new * rom. If not, it resorts to looking in the ENV: directory. */ char * getenv (name) register const char *name; { register FILE *fp; register char *ptr; static char buf[256]; static char value[256]; /* 2.0 style? */ if (DOSBase->dl_lib.lib_Version >= 36) { if (GetVar ((char *)name,value,256,0L) == -1) { return 0; } } else { if (strlen (name) > 252) { return 0; } strcpy (buf,"ENV:"); strcpy (&buf[4],name); if (! (fp = fopen(buf,"r"))) { return 0; } for (ptr = value; (*ptr=getc(fp))!=EOF && ++ptr < &value[256];); fclose(fp); *ptr = 0; } return value; } int setenv (name, value) char *name; char *value; { if (DOSBase->dl_lib.lib_Version >= 36) { SetVar ((char *)name,(char *)value,strlen(value)+1,GVF_LOCAL_ONLY); } return 0; } void make_post_cmd (cmd, name) char *cmd; char *name; { char *p; if ((p = getenv (ENV_VAR_POSTER)) != (char *) 0) { sprintf (cmd, p, name); } else { sprintf (cmd, DEFAULT_POSTER, name); } } #else /* * The ';' is to satisfy a really picky Ansi compiler */ ; #endif /* M_AMIGA */ *[SRC.TIN-1_22]AMIGA.H;1+,.// 4I-d0@123KPWO56`t7Fg189]VG/HJ/* * Project : tin - a Usenet reader * Module : amiga.h * Author : M.Tomlinson & I.Lea * Created : 17-09-92 * Updated : 20-06-93 * Notes : Directory support for AmigaDOS * Copyright : (c) Copyright 1991-93 by Mark Tomlinson & Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #if defined(M_AMIGA) #ifndef AMIGA_H #define AMIGA_H #include /* SAS-C and Aztec don't take the mode for mkdir() */ extern int mkdir(char *path); #define mkdir(p,m) mkdir(p) /* Replace Aztec's stat function with one that gives ST_DIRECT info */ struct stat { char st_attr; long st_mtime; long st_size; }; #define ST_DELETE 0x01 #define ST_EXECUTE 0x02 #define ST_WRITE 0x04 #define ST_READ 0x08 #define ST_DIRECT 0x10 /* Aztec's stat() doesn't give this information */ /* For opendir(), and readdir() */ struct dirent { char *d_name; long d_reclen; }; typedef struct { BPTR Lock; struct FileInfoBlock fib; int first; } DIR; extern DIR *opendir (char *name); extern struct dirent *readdir (DIR *di); extern void closedir (DIR *di); extern int stat(char *name, struct stat *buf); extern FILE *popen (char *command, char *mode); extern void pclose (FILE *pipe); extern void sleep(int secs); extern int tputs (char *cp, int count, int (*outc)(int)); extern int getpid(void); extern int setenv(char *, char *); extern int optind; extern char *optarg; extern char getopt (int argc, char *argv[], char *options); extern void make_post_cmd (char *cmd, char *name); extern void make_post_process_cmd(char *cmd, char *dir, char *file); #endif /* AMIGA_H */ #endif /* M_AMIGA */ *[SRC.TIN-1_22]ART.C;5+,.H// 4HF-d0@123KPWOI56+Ƌ7Ƌ89]VG/HJ /* * Project : tin - a Usenet reader * Module : art.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 22-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #include "stpwatch.h" char index_file[PATH_LEN]; char *glob_art_group; static long last_read_article; /* * Construct the pointers to the basenotes of each thread * arts[] contains every article in the group. inthread is * set on each article that is after the first article in the * thread. Articles which have been expired have their thread * set to -2 (ART_EXPIRED). */ void find_base (index) int index; /* active[index] */ { register int i; register int j; top_base = 0; debug_print_arts (); if (active[index].attribute.show_only_unread) { for (i = 0; i < top; i++) { if (IGNORE_ART(i) || arts[i].inthread != FALSE) { continue; } if (top_base >= max_art) { expand_art (); } if (arts[i].unread == ART_UNREAD) { base[top_base++] = i; } else { for (j = i ; j >= 0 ; j = arts[j].thread) { if (arts[j].unread) { base[top_base++] = i; break; } } } } } else { for (i = 0; i < top; i++) { if (IGNORE_ART(i) || arts[i].inthread != FALSE) { continue; } if (top_base >= max_art) { expand_art (); } base[top_base++] = i; } } } /* * Count the number of non-expired articles in arts[] */ int num_of_arts () { int sum = 0; register int i; for (i = 0; i < top; i++) { if (arts[i].thread != ART_EXPIRED) { sum++; } } return sum; } /* * Return TRUE if arts[] contains any expired articles * (articles we have an entry for which don't have a * corresponding article file in the spool directory) */ int purge_needed (group_path) char *group_path; { register int i; for (i = 0; i < top; i++) { if (arts[i].thread == ART_EXPIRED) { return TRUE; } } return FALSE; } /* * Main group indexing routine. Group should be the name of the * newsgroup, i.e. "comp.unix.amiga". group_path should be the * same but with the .'s turned into /'s: "comp/unix/amiga" * * Will read any existing index, create or incrementally update * the index by looking at the articles in the spool directory, * and attempt to write a new index if necessary. */ int index_group (group, group_path) char *group; char *group_path; { int index; int killed = FALSE; int expired = FALSE; int modified = FALSE; long min; long max; register int i; glob_art_group = group; if (! update) { sprintf (msg, txt_group, group); wait_message (msg); } BegStopWatch("index_group"); index = find_group_index (group); if (index != -1) { set_alarm_clock_off (); set_signals_art (); hash_reclaim (); free_art_array (); /* * load article numbers into base[] */ setup_base (group, group_path); /* * load articles from xover/xindex file if it exists */ min = base[0]; max = (top_base ? base[top_base-1] : base[0]); if (! read_xover_file (group, min, max)) { read_xindex_file (group); } /* * add any articles to arts[] that are new or were killed */ modified = read_group (group, group_path); /* * check that user did not abort indexing */ if (modified == -1) { set_alarm_clock_on (); return FALSE; } /* * Stat all articles to see if any have expired */ if (purge_index_files) { if (! cmd_line) { sprintf (msg, txt_purge, group); wait_message (msg); } for (i = 0 ; i < top ; i++) { if (! stat_article (arts[i].artnum, group_path)) { expired = TRUE; if (cmd_line && verbose) { fputc ('P', stdout); fflush (stdout); } } } if (expired && cmd_line && verbose) { fputc ('\n', stdout); fflush (stdout); } } /* * If reading index file via XINDEX this is useless */ if (expired || modified || purge_needed (group_path)) { if (! cmd_line && index_file[0]) { wait_message (txt_writing_index_file); } write_xindex_file (group); } read_newsrc_line (group); killed = kill_any_articles (index); make_threads (FALSE); find_base (index); if ((modified || killed) && ! update) { clear_message (); } set_alarm_clock_on (); } EndStopWatch(); PrintStopWatch(); return TRUE; } /* * Index a group. Assumes any existing index has already been * loaded. Return values are: * TRUE loaded index and modified it * FALSE loaded index but not modified * -1 user aborted indexing operation */ int read_group (group, group_path) char *group; char *group_path; { char *ptr, buf[PATH_LEN]; char progress[PATH_LEN]; extern long head_next; int count = 0; int modified = FALSE; int respnum, total = 0; long art; register int i; static char dir[PATH_LEN] = ""; /* * change to groups spooldir to optimize fopen()'s on local articles */ #ifdef INDEX_DAEMON if (dir[0] == 0) #endif get_cwd (dir); joinpath (buf, active[my_group[cur_groupnum]].spooldir, group_path); my_chdir (buf); buf[0] = '\0'; sprintf (progress, txt_group, active[my_group[cur_groupnum]].name); /* * Count num of arts to index so the user has an idea of index time */ BegStopWatch("valid_artnum1"); for (i = 0; i < top_base; i++) { if (base[i] <= last_read_article || valid_artnum (base[i]) >= 0) { continue; } total++; } EndStopWatch(); PrintStopWatch(); /* * Reset the next article number index (for when HEAD fails) */ head_next = -1; for (i = 0; i < top_base; i++) { /* for each article # */ art = base[i]; /* * Do we already have this article in our index? Change * thread from (ART_EXPIRED) to (ART_NORMAL) if so and * skip the header eating. */ BegStopWatch("valid_artnum2"); if ((respnum = valid_artnum (art)) >= 0 || art <= last_read_article) { if (respnum >= 0) { arts[respnum].thread = ART_NORMAL; arts[respnum].unread = ART_UNREAD; } continue; } EndStopWatch(); PrintStopWatch(); /* * we've modified the index so it will need to be re-written */ if (! modified) { modified = TRUE; } if ((ptr = open_art_header (art)) == (char *) 0) { continue; } /* * Add article to arts[] */ if (top >= max_art) { expand_art(); } arts[top].artnum = art; arts[top].thread = ART_NORMAL; set_article (&arts[top]); if (! parse_headers (ptr, &arts[top])) { sprintf (buf, "FAILED parse_header(%ld)", art); debug_nntp ("read_group", buf); continue; } last_read_article = arts[top].artnum; /* used if arts are killed */ top++; if (++count % MODULO_COUNT_NUM == 0 && ! update) { if (input_pending ()) { if (read (STDIN_FILENO, buf, sizeof (buf)-1)) { if (buf[0] == ESC || buf[0] == 'q' || buf[0] == 'Q') { if (prompt_yn (cLINES, txt_abort_indexing, 'y')) { chdir (dir); return -1; } else { printf (progress); } } } } if (count == 0) { printf (progress); } else { printf ("\b\b\b\b\b\b\b\b\b%4d/%-4d", count, total); } fflush (stdout); } if (update && verbose) { fputc ('.', stdout); fflush (stdout); } } if (count && update && verbose) { fputc ('\n', stdout); fflush (stdout); } /* * change to previous dir before indexing started */ my_chdir (dir); return modified; } /* * Go through the articles in arts[] and use .thread to snake threads * through them. Use the subject line to construct threads. The * first article in a thread should have .inthread set to FALSE, the * rest TRUE. Only do unexprired articles we haven't visited yet * (arts[].thread == -1 ART_NORMAL). */ void make_threads (rethread) int rethread; { register int i; register int j; if (! cmd_line) { if (active[my_group[cur_groupnum]].attribute.thread_arts) { wait_message (txa~ TIN-1_22.BCKd[SRC.TIN-1_22]ART.C;5Hlt_threading_arts); } else { wait_message (txt_unthreading_arts); } } /* if (debug == 2) { sprintf (msg, "rethread=[%d] thread_arts=[%d] attr_thread_arts=[%d]", rethread, default_thread_arts, active[my_group[cur_groupnum]].attribute.thread_arts); error_message (msg, ""); } */ /* * arts[].thread & arts[].inthread need to be reset if re-threading */ if (rethread || active[my_group[cur_groupnum]].attribute.thread_arts) { /* if (debug == 2) { error_message("Resetting .thread & .inthread", ""); } */ for (i=0 ; i < top ; i++) { arts[i].thread = ART_NORMAL; arts[i].inthread = FALSE; } } switch (active[my_group[cur_groupnum]].attribute.sort_art_type) { case SORT_BY_NOTHING: /* don't sort at all */ qsort ((char *) arts, top, sizeof (struct t_article), artnum_comp); break; case SORT_BY_SUBJ_DESCEND: case SORT_BY_SUBJ_ASCEND: qsort ((char *) arts, top, sizeof (struct t_article), subj_comp); break; case SORT_BY_FROM_DESCEND: case SORT_BY_FROM_ASCEND: qsort ((char *) arts, top, sizeof (struct t_article), from_comp); break; case SORT_BY_DATE_DESCEND: case SORT_BY_DATE_ASCEND: qsort ((char *) arts, top, sizeof (struct t_article), date_comp); break; default: break; } /* * FIXME - Once full group attributes are implemented what should the case be here? */ if (active[my_group[cur_groupnum]].attribute.thread_arts == 0 || default_thread_arts == 0) { /* if (debug == 2) { error_message("Returning before threading", ""); } */ return; } /* if (debug == 2) { error_message("Threading", ""); } */ for (i = 0; i < top; i++) { if (arts[i].thread != ART_NORMAL || IGNORE_ART(i)) { continue; } for (j = i+1; j < top; j++) { if (! IGNORE_ART(j) && arts[j].inthread == FALSE && ((arts[i].subject == arts[j].subject) || ((arts[i].part || arts[i].patch) && arts[i].archive == arts[j].archive))) { arts[i].thread = j; arts[j].inthread = TRUE; break; } } } } int parse_headers (buf, h) char *buf; struct t_article *h; { char buf2[HEADER_LEN]; char art_from_addr[LEN]; char art_full_name[LEN]; char *ptr, *ptrline, *s; int flag, n = 0, len = 0; int lineno = 0; int max_lineno = 25; int got_archive = FALSE; int got_date = FALSE; int got_from = FALSE; int got_lines = FALSE; int got_received = FALSE; int got_subject = FALSE; int got_xref = FALSE; n = strlen (buf); if (n == 0) { return FALSE; } buf[n-1] = '\0'; ptr = buf; while (1) { for (ptrline = ptr; *ptr && *ptr != '\n'; ptr++) { if (((*ptr) & 0xFF) < ' ') { *ptr = ' '; } } flag = *ptr; *ptr++ = '\0'; lineno++; switch (*ptrline) { case 'F': /* From: mandatory */ case 'T': /* To: mandatory (mailbox) */ if (! got_from) { if (match_header (ptrline, "From", buf2, HEADER_LEN) || match_header (ptrline, "To", buf2, HEADER_LEN)) { parse_from (buf2, art_from_addr, art_full_name); h->from = hash_str (art_from_addr); if (art_full_name[0]) { h->name = hash_str (art_full_name); } got_from = TRUE; } } break; case 'R': /* Received: If found its probably a mail article */ if (! got_received) { if (match_header (ptrline, "Received", buf2, HEADER_LEN)) { max_lineno = 50; got_received = TRUE; } } break; case 'S': /* Subject: mandatory */ if (! got_subject) { if (match_header (ptrline, "Subject", buf2, HEADER_LEN)) { s = eat_re (buf2); h->subject = hash_str (eat_re (s)); got_subject = TRUE; } } break; case 'D': /* Date: mandatory */ if (! got_date) { if (match_header (ptrline, "Date", buf2, HEADER_LEN)) { h->date = parsedate (buf2, (struct _TIMEINFO *) 0); got_date = TRUE; } } break; case 'X': /* Xref: optional */ if (! got_xref) { if (match_header (ptrline, "Xref", buf2, HEADER_LEN) || match_header (ptrline, "xref", buf2, HEADER_LEN)) { s = buf2; while (*s && *s == ' ') { s++; } h->xref = str_dup (s); got_xref = TRUE; } } break; case 'L': /* Lines: optional */ if (! got_lines) { if (match_header (ptrline, "Lines", buf2, HEADER_LEN)) { h->lines = atoi (buf2); got_lines = TRUE; } } break; case 'A': /* Archive-name: optional */ if (match_header (ptrline, "Archive-name", buf2, HEADER_LEN) || match_header (ptrline, "Archive-Name", buf2, HEADER_LEN)) { if ((s = (char *) strchr (buf2, '/')) != (char *) 0) { if (strncmp (s+1, "part", 4) == 0 || strncmp (s+1, "Part", 4) == 0) { h->part = str_dup (s+5); len = (int) strlen (h->part); if (h->part[len-1] == '\n') { h->part[len-1] = '\0'; } } else if (strncmp (s+1,"patch",5) == 0 || strncmp (s+1,"Patch",5) == 0) { h->patch = str_dup (s+6); len = (int) strlen (h->patch); if (h->patch[len-1] == '\n') { h->patch[len-1] = '\0'; } } if (h->part || h->patch) { s = buf2; while (*s && *s != '/') s++; *s = '\0'; s = buf2; h->archive = hash_str (s); got_archive = TRUE; } } } break; } if (! flag || lineno > max_lineno || got_archive) { if (got_from && got_date) { if (! got_subject) { h->subject = hash_str (""); } debug_print_header (h); return TRUE; } else { return FALSE; } } } /* NOTREACHED */ } /* * Write out an index file. Write the group name first so if * local indexing is done so we can disambiguate between group * name hash collisions by looking at the index file. */ void write_xindex_file (group) char *group; { char nam[LEN]; FILE *fp; int *iptr; int index; int realnum; long min_artnum = 0L; long max_artnum = 0L; register int i; if (! index_file[0]) { if (debug) { error_message (txt_no_index_file, ""); } return; } set_tin_uid_gid(); sprintf (nam, "%s.%d", index_file, process_id); #ifdef VMS if ((fp = fopen (nam, "w", "fop=cif")) == NULL) { #else if ((fp = fopen (nam, "w")) == NULL) { #endif perror_message (txt_cannot_open, nam); set_real_uid_gid (); return; } /* * dump group header info. */ index = my_group[cur_groupnum]; if (active[index].attribute.sort_art_type != SORT_BY_NOTHING) { qsort ((char *) arts, top, sizeof (struct t_article), artnum_comp); } fprintf (fp, "%s\n", group); fprintf (fp, "%d\n", num_of_arts ()); if (top <= 0) { fprintf (fp, "0\n"); } else { min_artnum = arts[0].artnum; max_artnum = arts[top-1].artnum; if (last_read_article > max_artnum) { max_artnum = last_read_article; fprintf (fp, "%ld\n", last_read_article); } else { fprintf (fp, "%ld\n", max_artnum); } if (active[index].type == GROUP_TYPE_MAIL) { i = FALSE; if (min_artnum > active[index].min) { active[index].min = min_artnum; i = TRUE; } if (max_artnum > active[index].max) { active[index].max = max_artnum; i = TRUE; } if (i) { write_mail_active_file (); } } } /* * dump articles */ realnum = 0; for (i = 0; i < top; i++) { if (arts[i].thread == ART_EXPIRED) { continue; } #ifdef DEBUG debug_print_header (&arts[i]); #endif fprintf(fp, "%ld\n", arts[i].artnum); iptr = (int *) arts[i].subject; iptr--; if (! arts[i].subject) { fprintf(fp, " \n"); } else if (*iptr < 0 || *iptr > top) { fprintf(fp, " %s\n", arts[i].subject); *iptr = realnum; } else if (*iptr == i) { fprintf(fp, " %s\n", arts[i].subject); } else { fprintf(fp, "%%%d\n", *iptr); } iptr = (int *) arts[i].from; iptr--; if (! arts[i].from) { fprintf (fp, " \n"); } else if (*iptr < 0 || *iptr > top) { fprintf (fp, " %s\n", arts[i].from); *iptr = realnum; } else if (*iptr == i) { fprintf(fp, " %s\n", arts[i].from); } else { fprintf(fp, "%%%d\n", *iptr); } iptr = (int *) arts[i].name; iptr--; if (! arts[i].name) { fprintf (fp, " \n"); } else if (*iptr < 0 || *iptr > top) { fprintf (fp, " %s\n", arts[i].name); *iptr = realnum; } else if (*iptr == i) { fprintf(fp, " %s\n", arts[i].name); } else { fprintf(fp, "%%%d\n", *iptr); } fprintf (fp, "%ld\n", arts[i].date); if (! arts[i].xref) { fprintf (fp, "\n"); } else { fprintf (fp, " %s\n", arts[i].xref); } if (arts[i].lines < 0) { fprintf (fp, "\n"); } else { fprintf (fp, " %d\n", arts[i].lines); } iptr = (int *) arts[i].archive; iptr--; if (! arts[i].archive) { fprintf (fp, "\n"); } else if (*iptr < 0 || *iptr > top) { fprintf (fp, " %s\n", arts[i].archive); *iptr = realnum; } else if (arts[i].part || arts[i].patch) { if (*iptr == i) { fprintf(fp, " %s\n", arts[i].archive); } else { fprintf (fp, "%%%d\n", *iptr); } } else { fprintf (fp, "\n"); } if (! arts[i].part) { fprintf (fp, " \n"); } else { fprintf (fp, "%s\n", arts[i].part); } if (! arts[i].patch) { fprintf (fp, " \n"); } else { fprintf (fp, "%s\n", arts[i].patch); } realnum++; } fclose (fp); rename_file (nam, index_file); chmod (index_file, 0644); set_real_uid_gid(); if (debug == 2) { #ifdef VMS sprintf (msg, "copy %s INDEX", index_file); #else sprintf (msg, "cp %s INDEX", index_file); #endif system (msg); } } /* * Read in an XINDEX index file. * * index file header * 1. newsgroup name (ie. alt.sources) * 2. number of articles (ie. 26) * 3. number of last read article (ie. 210) * 4. Is this a complete/killed index file (ie. COMPLETE/KILLED) * * index file record * 1. Article number (ie. 183) [mandatory] * 2. Subject: line (ie. Which newsreader?) [mandatory] * 3. From: line (addr) (ie. iain@norisc) [mandatory] * 4. From: line (name) (ie. Iain Lea) [mandatory] * 5. Date: of posting (ie. 71459801) [mandatory] * 6. Xref: grp:num ... (ie. alt.test:389) [optional] * 7. Lines: number (ie. 23) [optional] * 8. Archive: name (ie. compiler) [optional] * 9. Part number of Archive: name (ie. 01) [optional] * 10. Patch number of Archive: name (ie. 01) [optional] */ void read_xindex_file (group_name) char *group_name; { char buf[LEN], *p; int error = 0; int i, n; FILE *fp = NULL; top = 0; last_read_article = 0L; if ((fp = open_xindex_fp (group_name)) == (FILE *) 0) { return; } /* * load header - discard group name, num. of arts in index file after any arts were killed */ if (fgets (buf, sizeof buf, fp) == NULL || fgets (buf, sizeof buf, fp) == NULL) { error = 0; goto corrupt_index; } i = atoi (buf); /* * num. of last_read_article including any that were killed */ if (fgets(buf, sizeof buf, fp) == NULL) { error = 1; goto corrupt_index; } last_read_article = (long) atol (buf); /* * load articles */ while (top < i) { if (top >= max_art) { expand_art (); } arts[top].thread = ART_EXPIRED; set_article (&arts[top]); /* * 1. Article no. */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 2; goto corrupt_index; } arts[top].artnum = (long) atol (buf); /* * 2. Subject: */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 3; goto corrupt_index; } if (buf[0] == '%') { n = atoi (&buf[1]); if (n >= top || n < 0) { error = 4; goto corrupt_index; } arts[top].subject = arts[n].subject; } else if (buf[0] == ' ') { for (p = &buf[1]; *p && *p != '\n'; p++) continue; *p = '\0'; arts[top].subject = hash_str (&buf[1]); } else { error = 5; goto corrupt_index; } /* * 3. From: (addr part) */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 6; goto corrupt_index; } if (buf[0] == '%') { n = atoi (&buf[1]); if (n >= top || n < 0) { error = 7; goto corrupt_index; } arts[top].from = arts[n].from; } else if (buf[0] == ' ') { for (p = &buf[1]; *p && *p != '\n'; p++) continue; *p = '\0'; arts[top].from = hash_str (&buf[1]); } else { error = 8; goto corrupt_index; } /* * 4. From: (name part) */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 9; goto corrupt_index; } if (buf[0] == '%') { n = atoi (&buf[1]); if (n > top || n < 0) { error = 10; goto corrupt_index; } if (n == top) { /* no full name so .name = .from */ arts[top].name = arts[top].from; } else { arts[top].name = arts[n].name; } } else if (buf[0] == ' ') { for (p = &buf[1]; *p && *p != '\n'; p++) continue; *p = '\0'; if (buf[1]) { arts[top].name = hash_str (&buf[1]); } } else { error = 11; goto corrupt_index; } /* * 5. Date: */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 12; goto corrupt_index; } arts[top].date = (long) atol (buf); /* * 6. Xref: */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 13; goto corrupt_index; } if (buf[0] == '\n') { arts[top].xref = (char *) 0; } else { p = strrchr (buf, '\n'); if (p != (char *) 0) { *p = '\0'; } arts[top].xref = str_dup (&buf[1]); } /* * 7. Lines: */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 14; goto corrupt_index; } if (buf[0] == '\n') { arts[top].lines = -1; } else { arts[top].lines = atoi (buf); } /* * 8. Archive-name: */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 15; goto corrupt_index; } if (buf[0] == '\n') { arts[top].archive = (char *) 0; } else if (buf[0] == '%') { n = atoi (&buf[1]); if (n > top || n < 0) { error = 16; goto corrupt_index; } arts[top].archive = arts[n].archive; } else if (buf[0] == ' ') { for (p = &buf[1]; *p && *p != '\n' ; p++) continue; *p = '\0'; arts[top].archive = hash_str (&buf[1]); } else { error = 17; goto corrupt_index; } /* * 9. Part no. */ if (fgets(buf, sizeof buf, fp) == NULL) { error = 18; goto corrupt_index; } if (buf[0] != ' ') { p = strrchr (buf, '\n'); if (p != (char *) 0) { *p = '\0'; } arts[top].part = str_dup (buf); } /* * 10. Patch no. */ if (fgets (buf, sizeof buf, fp) == NULL) { error = 19; goto corrupt_index; } if (buf[0] != ' ') { p = strrchr (buf, '\n'); if (p != (char *) 0) { *p = '\0'; } arts[top].patch = str_dup (buf); } debug_print_header (&arts[top]); top++; } fclose(fp); /* * If reading in a mail group index check if min & max numbers are * consistant with what has been read from index file. */ i = my_group[cur_groupnum]; if (active[i].type == GROUP_TYPE_MAIL) { n = FALSE; if (top && top > active[i].max) { active[i].max = top; n = TRUE; } if (top && arts[0].artnum > active[i].min) { active[i].min = arts[0].artnum; n = TRUE; } if (n) { write_mail_active_file (); } } return; corrupt_index: if (! update) { sprintf (msg, txt_corrupt_index, index_file, error, top); error_message (msg, ""); } if (debug == 2) { #ifdef VMS sprintf (msg, "copy %s INDEX.BAD", index_file); #else sprintf (msg, "cp %s INDEX.BAD", index_file); #endif system (msg); } last_read_article = 0L; if (fp) { fclose(fp); } set_tin_uid_gid (); unlink (index_file); set_real_uid_gid (); top = 0; } /* * Read in an XOVER index file. Fields are separated by TAB. * * index file record * 1. article number (ie. 183) [mandatory] * 2. Subject: line (ie. Which newsreader?) [mandatory] * 3. From: line (ie. iain@norisc) [mandatory] * 4. Date: line (ie. rfc822 format) [mandatory] * 5. Lines: line (ie. 23) [optional] * 6. Xref: line (ie. alt.test:389) [optional] */ int read_xover_file (group_name, min, max) char *group_name; long min; long max; { char *p, *q; char buf[1024]; char art_full_name[LEN]; char art_from_addr[LEN]; FILE *fp; top = 0; last_read_article = 0L; /* * setup the overview file (whether it be local or via nntp) */ if ((fp = open_xover_fp (group_name, min, max)) == (FILE *) 0) { return FALSE; } while (fgets (buf, sizeof (buf), fp) != (char *) 0) { if (strcmp (buf, ".") == 0) { break; } if (top >= max_art) { expand_art (); } arts[top].thread = ART_EXPIRED; set_article (&arts[top]); p = buf; /* * read the article number */ last_read_article = arts[top].artnum = atol (p); q = strchr (p, '\t'); if (q == (char *) 0) { error_message ("Bad overview record '%s'", buf); continue; } else { p = q + 1; } /* * READ subject */ q = strchr (p, '\t'); if (q == (char *) 0) { error_message ("Bad overview record (Subject) '%s'", p); debug_nntp ("read_xover_file", "Bad overview record (Subject)"); continue; } else { *q = '\0'; } arts[top].subject = hash_str (eat_re (eat_re (p))); p = q + 1; /* * READ author */ q = strchr (p, '\t'); if (q == (char *) 0) { error_message ("Bad overview record (From) '%s'", p); debug_nntp ("read_xover_file", "Bad overview record (From)"); continue; } else { *q = '\0'; } parse_from (p, art_from_addr, art_full_name); arts[top].from = hash_str (art_from_addr); if (art_full_name[0]) { arts[top].name = hash_str (art_full_name); } p = q + 1; /* * READ article date */ q = strchr (p, '\t'); if (q == (char *) 0) { error_message ("Bad overview record (Date) '%s'", p); debug_nntp ("read_xover_file", "Bad overview record (Date)"); continue; } else { *q = '\0'; } arts[top].date = parsedate (p, NULL); p = q + 1; /* * SKIP article messageid */ q = strchr (p, '\t'); if (q == (char *) 0) { error_message ("Bad overview record (Msg-id) '%s'", p); debug_nntp ("read_xover_file", "Bad overview record (Msg-id)"); continue; } p = q + 1; /* * SKIP article references */ q = strchr (p, '\t'); if (q == (char *) 0) { error_message ("Bad overview record (References) '%s'", p); debug_nntp ("read_xover_file", "Bad overview record (References)"); continue; } p = q + 1; /* * SKIP article bytes */ q = strchr (p, '\t'); if (q == (char *) 0) { error_message ("Bad overview record (Bytes) '%s'", p); debug_nntp ("read_xover_file", "Bad overview record (Bytes)"); continue; } else { *q = '\0'; } p = q + 1; /* * READ article lines (special case - last standard nov header) */ q = strchr (p, '\t'); if (q == (char *) 0) { if (!*p || (*p < '0' && *p > '9')) { error_message ("Bad overview record (Lines) '%s'", p); debug_nntp ("read_xover_file", "Bad overview record (Lines)"); continue; } } else { *q = '\0'; } arts[top].lines = atoi (p); p = (q == (char *) 0 ? (char *) 0 : q + 1); /* * READ article xrefs */ if (p != (char *) 0 && xref_supported) { q = str_str (p, "Xref: ", 6); if (q != (char *) 0) { p = q + 6; q = p; while (*q && *q != '\t') { q++; } *q = '\0'; q = strrchr (p, '\n'); if (q != (char *) 0) { *q = '\0'; } q = p; while (*q && *q == ' ') { q++; } arts[top].xref = str_dup (q); } } /* * end of overview line processing */ debug_print_header (&arts[top]); top++; } fclose (fp); return TRUE; } /* * Look in the local $HOME/RCDIR/[ INDEX_NEWSDIR | INDEX_MAILDIR ] etc. * directory for the index file for the given group. Hashing the group * name gets a number. See if that #.1 file exists; if so, read first * line. Group we want? If no, try #.2. Repeat until no such file or * we find an existing file that matches our group. * Return group type of the index file (mail / news / -1 on error). */ int find_index_file (group) char *group; { char *p, dir[PATH_LEN]; FILE *fp; int i = 1, j, type; static char buf[PATH_LEN]; unsigned long h; j = find_group_index (group); if (j == -1) { return j; } type = active[j].type; h = hash_groupname (group); if (read_news_via_nntp && xindex_supported && type == GROUP_TYPE_NEWS) { sprintf (index_file, "%sxindex.%d", TMPDIR, process_id); return (type); } if (local_index) { if (type == GROUP_TYPE_NEWS) { my_strncpy (dir, index_newsdir, sizeof (dir)); } else { my_strncpy (dir, index_maildir, sizeof (dir)); } } else { #ifdef VMS joindir (dir, get_val ("TIN_INDEXDIR", active[j].spooldir), INDEX_NEWSDIR); #else joinpath (dir, get_val ("TIN_INDEXDIR", active[j].spooldir), INDEX_NEWSDIR); #endif } i = 1; while (TRUE) { sprintf (buf, "%lu.%d", h, i); joinpath (index_file, dir, buf); if ((fp = fopen (index_file, "r")) == (FILE *) 0) { return (type); } if (fgets (buf, sizeof (buf), fp) == (char *) 0) { fclose (fp); return (type); } fclose (fp); for (p = buf; *p && *p != '\n'; p++) { continue; } *p = '\0'; if (strcmp (buf, group) == 0) { return (type); } i++; } } /* * Run the index file updater only for the groups we've loaded. */ void do_update () { char group_path[PATH_LEN]; register int i, j; long beg_epoch, end_epoch; #ifdef INDEX_DAEMON char buf[PATH_LEN]; long group_time, index_time; struct stat stinfo; #endif if (verbose) { time (&beg_epoch); } /* * load last updated times for each group (tind daemon only) */ read_active_times_file (); /* * loop through groups and update any required index files */ for (i = 0; i < group_top; i++) { make_group_path (active[my_group[i]].name, group_path); #ifdef INDEX_DAEMON joinpath (buf, active[my_group[i]].spooldir, group_path); if (stat (buf, &stinfo) == -1) { if (verbose) { error_message ("Can't stat group %s\n", buf); } continue; } group_time = stinfo.st_mtime; index_time = 0L; if (find_index_file (active[my_group[i]].name) == -1) { continue; } if (stat (index_file, &stinfo) == -1) { if (verbose) { printf ("Can't stat %s index %s\n", active[my_group[i]].name, index_file); } } else { index_time = stinfo.st_mtime; } if (debug == 2) { printf ("[%s] [%s] idxtime=[%ld] old=[%ld] new=[%ld]\n", active[my_group[i]].name, index_file, index_time, active[my_group[i]].last_updated_time, group_time); } if (index_time == 0L || active[my_group[i]].last_updated_time == 0L || group_time > active[my_group[i]].last_updated_time || purge_index_files) { active[my_group[i]].last_updated_time = group_time; } else { continue; } #endif if (verbose) { printf ("%s %s\n", (catchup ? "Catchup" : "Updating"), active[my_group[i]].name); fflush (stdout); } if (! index_group (active[my_group[i]].name, group_path)) { continue; } if (catchup) { for (j = 0; j < top; j++) { arts[j].unread = ART_READ; } update_newsrc (active[my_group[i]].name, my_group[i], FALSE); } } /* * save last updated times for each group (tind daemon only) */ write_active_times_file (); if (verbose) { time (&end_epoch); sprintf (msg, txt_catchup_update_info, (catchup ? "Caughtup" : "Updated"), group_top, end_epoch - beg_epoch); wait_message (msg); } } int artnum_comp (p1, p2) t_comptype *p1; t_comptype *p2; { struct t_article *s1 = (struct t_article *) p1; struct t_article *s2 = (struct t_article *) p2; /* * s1->artnum less than s2->artnum */ if (s1->artnum < s2->artnum) { return -1; } /* * s1->artnum greater than s2->artnum */ if (s1->artnum > s2->artnum) { return 1; } return 0; } int subj_comp (p1, p2) t_comptype *p1; t_comptype *p2; { int retval; struct t_article *s1 = (struct t_article *) p1; struct t_article *s2 = (struct t_article *) p2; /* * return result of strcmp (reversed for descending) */ return (active[my_group[cur_groupnum]].attribute.sort_art_type == SORT_BY_SUBJ_ASCEND ? (retval = my_stricmp (s1->subject, s2->subject)) ? retval : ((s1->date - s2->date) > 0) ? 1 : -1 : (retval = my_stricmp (s2->subject, s1->subject)) ? retval : ((s1->date - s2->date) > 0) ? 1 : -1); } int from_comp (p1, p2) t_comptype *p1; t_comptype *p2; { int retval; struct t_article *s1 = (struct t_article *) p1; struct t_article *s2 = (struct t_article *) p2; /* * return result of strcmp (reversed for descending) */ return (active[my_group[cur_groupnum]].attribute.sort_art_type == SORT_BY_FROM_ASCEND ? (retval = my_stricmp (s1->from, s2->from)) ? retval : ((s1->date - s2->date) > 0) ? 1 : -1 : (retval = my_stricmp (s2->from, s1->from)) ? retval : ((s1->date - s2->date) > 0) ? 1 : -1); } int date_comp (p1, p2) t_comptype *p1; t_comptype *p2; { struct t_article *s1 = (struct t_article *) p1; struct t_article *s2 = (struct t_article *) p2; if (active[my_group[cur_groupnum]].attribute.sort_art_type == SORT_BY_DATE_ASCEND) { /* * s1->date less than s2->date */ if (s1->date < s2->date) { return -1; } /* * s1->date greater than s2->date */ if (s1->date > s2->date) { return 1; } } else { /* * s2->date less than s1->date */ if (s2->date < s1->date) { return -1; } /* * s2->date greater than s1->date */ if (s2->date > s1->date) { return 1; } } return 0; } void set_article (art) struct t_article *art; { art->subject = (char *) 0; art->from = (char *) 0; art->name = (char *) 0; art->date = 0L; art->xref = (char *) 0; art->lines = -1; art->archive = (char *) 0; art->part = (char *) 0; art->patch = (char *) 0; art->unread = ART_UNREAD; art->inthread = FALSE; art->killed = FALSE; art->tagged = FALSE; art->hot = FALSE; art->zombie = FALSE; } int input_pending () { #ifdef HAVE_SELECT int fd = STDIN_FILENO; fd_set fdread; struct timeval tvptr; FD_ZERO(&fdread); tvptr.tv_sec = 0; tvptr.tv_usec = 0; FD_SET(fd, &fdread); if (select (1, &fdread, NULL, NULL, &tvptr)) { if (FD_ISSET(fd, &fdread)) { return TRUE; } } #endif /* HAVE_SELECT */ #if defined(HAVE_POLL) && !defined(HAVE_SELECT) static int timeout = 0; static long nfds = 1; static struct pollfd fds[]= { STDIN_FILENO, POLLIN, 0 }; if (poll (fds, nfds, timeout) < 0) { /* * Error on poll */ return FALSE; } switch (fds[0].revents) { case POLLIN: return TRUE; break; /* * Other conditions on the stream */ case POLLHUP: case POLLERR: default: return FALSE; break; } #endif /* HAVE_POLL */ return FALSE; } int valid_artnum (art) long art; { register int prev, range; register int dtop = top; register int cur = 1; while (dtop /= 2) { cur = cur << 1; } range = cur / 2; cur--; while (1) { if (arts[cur].artnum == art) { return cur; } prev = cur; if (arts[cur].artnum < art) { cur = cur + range; } else { cur = cur - range; } if (prev == cur) { return -1; } if (cur >= top) { cur = top - 1; } range = range / 2; } } r*[SRC.TIN-1_22]BISON.SIMPLE;2+,.!// 4! I-d0123KPWO!56ଡ଼ۼ7 u89]VG/HJ/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ #line 3 "bison.simple" /* Skeleton output parser for bison, Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef alloca #ifdef __GNUC__ #define alloca __builtin_alloca #else /* not GNU C. */ #if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) #include #else /* not sparc */ #if defined (MSDOS) && !defined (__TURBOC__) #include #else /* not MSDOS, or __TURBOC__ */ #if defined(_AIX) #include #pragma alloca #else /* not MSDOS, __TURBOC__, or _AIX */ #ifdef __hpux #ifdef __cplusplus extern "C" { void *alloca (unsigned int); }; #else /* not __cplusplus */ void *alloca (); #endif /* not __cplusplus */ #endif /* __hpux */ #endif /* not _AIX */ #endif /* not MSDOS, or __TURBOC__ */ #endif /* not sparc. */ #endif /* not GNU C. */ #endif /* alloca not defined. */ /* This is the parser code that is written into each bison parser when the %semantic_parser declaration is not specified in the grammar. It was written by Richard Stallman by simplifying the hairy parser used when %semantic_parser is specified. */ /* Note: there must be only one dollar sign in this file. It is replaced by the list of actions, each action as one case of the switch. */ #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY -2 #define YYEOF 0 #define YYACCEPT return(0) #define YYABORT return(1) #define YYERROR goto yyerrlab1 /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(token, value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { yychar = (token), yylval = (value); \ yychar1 = YYTRANSLATE (yychar); \ YYPOPSTACK; \ goto yybackup; \ } \ else \ { yyerror ("syntax error: cannot back up"); YYERROR; } \ while (0) #define YYTERROR 1 #define YYERRCODE 256 #ifndef YYPURE #define YYLEX yylex() #endif #ifdef YYPURE #ifdef YYLSP_NEEDED #define YYLEX yylex(&yylval, &yylloc) #else #define YYLEX yylex(&yylval) #endif #endif /* If nonreentrant, generate the variables here */ #ifndef YYPURE int yychar; /* the lookahead symbol */ YYSTYPE yylval; /* the semantic value of the */ /* lookahead symbol */ #ifdef YYLSP_NEEDED YYLTYPE yylloc; /* location data for the lookahead */ /* symbol */ #endif int yynerrs; /* number of parse errors so far */ #endif /* not YYPURE */ #if YYDEBUG != 0 int yydebug; /* nonzero means print parse trace */ /* Since this is uninitialized, it does not stop multiple parsers from |V~ TIN-1_22.BCKd[SRC.TIN-1_22]BISON.SIMPLE;2! coexisting. */ #endif /* YYINITDEPTH indicates the initial size of the parser's stacks */ #ifndef YYINITDEPTH #define YYINITDEPTH 200 #endif /* YYMAXDEPTH is the maximum size the stacks can grow to (effective only if the built-in stack extension method is used). */ #if YYMAXDEPTH == 0 #undef YYMAXDEPTH #endif #ifndef YYMAXDEPTH #define YYMAXDEPTH 10000 #endif /* Prevent warning if -Wstrict-prototypes. */ #ifdef __GNUC__ int yyparse (void); #endif #if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ #define __yy_bcopy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT) #else /* not GNU C or C++ */ #ifndef __cplusplus /* This is the most reliable way to avoid incompatibilities in available built-in functions on various systems. */ static void __yy_bcopy (from, to, count) char *from; char *to; int count; { register char *f = from; register char *t = to; register int i = count; while (i-- > 0) *t++ = *f++; } #else /* __cplusplus */ /* This is the most reliable. way to avoid incompatibilities in available built-in functions on various systems. */ static void __yy_bcopy (char *from, char *to, int count) { register char *f = from; register char *t = to; register int i = count; while (i-- > 0) *t++ = *f++; } #endif #endif #line 184 "bison.simple" int yyparse() { register int yystate; register int yyn; register short *yyssp; register YYSTYPE *yyvsp; int yyerrstatus; /* number of tokens to shift before error messages enabled */ int yychar1 = 0; /* lookahead token as an internal (translated) token number */ short yyssa[YYINITDEPTH]; /* the state stack */ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */ short *yyss = yyssa; /* refer to the stacks thru separate pointers */ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */ #ifdef YYLSP_NEEDED YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */ YYLTYPE *yyls = yylsa; YYLTYPE *yylsp; #define YYPOPSTACK (yyvsp--, yyssp--, yylsp--) #else #define YYPOPSTACK (yyvsp--, yyssp--) #endif int yystacksize = YYINITDEPTH; #ifdef YYPURE int yychar; YYSTYPE yylval; int yynerrs; #ifdef YYLSP_NEEDED YYLTYPE yylloc; #endif #endif YYSTYPE yyval; /* the variable used to return */ /* semantic values from the action */ /* routines */ int yylen; #if YYDEBUG != 0 if (yydebug) fprintf(stderr, "Starting parse\n"); #endif yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss - 1; yyvsp = yyvs; #ifdef YYLSP_NEEDED yylsp = yyls; #endif /* Push a new state, which is found in yystate . */ /* In all cases, when you get here, the value and location stacks have just been pushed. so pushing a state here evens the stacks. */ yynewstate: *++yyssp = yystate; if (yyssp >= yyss + yystacksize - 1) { /* Give user a chance to reallocate the stack */ /* Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; short *yyss1 = yyss; #ifdef YYLSP_NEEDED YYLTYPE *yyls1 = yyls; #endif /* Get the current used size of the three stacks, in elements. */ int size = yyssp - yyss + 1; #ifdef yyoverflow /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. */ #ifdef YYLSP_NEEDED /* This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow("parser stack overflow", &yyss1, size * sizeof (*yyssp), &yyvs1, size * sizeof (*yyvsp), &yyls1, size * sizeof (*yylsp), &yystacksize); #else yyoverflow("parser stack overflow", &yyss1, size * sizeof (*yyssp), &yyvs1, size * sizeof (*yyvsp), &yystacksize); #endif yyss = yyss1; yyvs = yyvs1; #ifdef YYLSP_NEEDED yyls = yyls1; #endif #else /* no yyoverflow */ /* Extend the stack our own way. */ if (yystacksize >= YYMAXDEPTH) { yyerror("parser stack overflow"); return 2; } yystacksize *= 2; if (yystacksize > YYMAXDEPTH) yystacksize = YYMAXDEPTH; yyss = (short *) alloca (yystacksize * sizeof (*yyssp)); __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp)); yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp)); __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp)); #ifdef YYLSP_NEEDED yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp)); __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp)); #endif #endif /* no yyoverflow */ yyssp = yyss + size - 1; yyvsp = yyvs + size - 1; #ifdef YYLSP_NEEDED yylsp = yyls + size - 1; #endif #if YYDEBUG != 0 if (yydebug) fprintf(stderr, "Stack size increased to %d\n", yystacksize); #endif if (yyssp >= yyss + yystacksize - 1) YYABORT; } #if YYDEBUG != 0 if (yydebug) fprintf(stderr, "Entering state %d\n", yystate); #endif goto yybackup; yybackup: /* Do appropriate processing given the current state. */ /* Read a lookahead token if we need one and don't already have one. */ /* yyresume: */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yyn == YYFLAG) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* yychar is either YYEMPTY or YYEOF or a valid token in external form. */ if (yychar == YYEMPTY) { #if YYDEBUG != 0 if (yydebug) fprintf(stderr, "Reading a token: "); #endif yychar = YYLEX; } /* Convert token to internal form (in yychar1) for indexing tables with */ if (yychar <= 0) /* This means end of input. */ { yychar1 = 0; yychar = YYEOF; /* Don't call YYLEX any more */ #if YYDEBUG != 0 if (yydebug) fprintf(stderr, "Now at end of input.\n"); #endif } else { yychar1 = YYTRANSLATE(yychar); #if YYDEBUG != 0 if (yydebug) { fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]); /* Give the individual parser a way to print the precise meaning of a token, for further debugging info. */ #ifdef YYPRINT YYPRINT (stderr, yychar, yylval); #endif fprintf (stderr, ")\n"); } #endif } yyn += yychar1; if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1) goto yydefault; yyn = yytable[yyn]; /* yyn is what to do for this token type in this state. Negative => reduce, -yyn is rule number. Positive => shift, yyn is new state. New state is final state => don't bother to shift, just return success. 0, or most negative number => error. */ if (yyn < 0) { if (yyn == YYFLAG) goto yyerrlab; yyn = -yyn; goto yyreduce; } else if (yyn == 0) goto yyerrlab; if (yyn == YYFINAL) YYACCEPT; /* Shift the lookahead token. */ #if YYDEBUG != 0 if (yydebug) fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]); #endif /* Discard the token being shifted unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; *++yyvsp = yylval; #ifdef YYLSP_NEEDED *++yylsp = yylloc; #endif /* count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; yystate = yyn; goto yynewstate; /* Do the default action for the current state. */ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; /* Do a reduction. yyn is the number of a rule to reduce with. */ yyreduce: yylen = yyr2[yyn]; if (yylen > 0) yyval = yyvsp[1-yylen]; /* implement default value of the action */ #if YYDEBUG != 0 if (yydebug) { int i; fprintf (stderr, "Reducing via rule %d (line %d), ", yyn, yyrline[yyn]); /* Print the symbols being reduced, and their result. */ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++) fprintf (stderr, "%s ", yytname[yyrhs[i]]); fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]); } #endif $ /* the action file gets copied in in place of this dollarsign */ #line 465 "bison.simple" yyvsp -= yylen; yyssp -= yylen; #ifdef YYLSP_NEEDED yylsp -= yylen; #endif #if YYDEBUG != 0 if (yydebug) { short *ssp1 = yyss - 1; fprintf (stderr, "state stack now"); while (ssp1 != yyssp) fprintf (stderr, " %d", *++ssp1); fprintf (stderr, "\n"); } #endif *++yyvsp = yyval; #ifdef YYLSP_NEEDED yylsp++; if (yylen == 0) { yylsp->first_line = yylloc.first_line; yylsp->first_column = yylloc.first_column; yylsp->last_line = (yylsp-1)->last_line; yylsp->last_column = (yylsp-1)->last_column; yylsp->text = 0; } else { yylsp->last_line = (yylsp+yylen-1)->last_line; yylsp->last_column = (yylsp+yylen-1)->last_column; } #endif /* Now "shift" the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTBASE] + *yyssp; if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTBASE]; goto yynewstate; yyerrlab: /* here on detecting error */ if (! yyerrstatus) /* If not already recovering from an error, report this error. */ { ++yynerrs; #ifdef YYERROR_VERBOSE yyn = yypact[yystate]; if (yyn > YYFLAG && yyn < YYLAST) { int size = 0; char *msg; int x, count; count = 0; /* Start X at -yyn if nec to avoid negative indexes in yycheck. */ for (x = (yyn < 0 ? -yyn : 0); x < (sizeof(yytname) / sizeof(char *)); x++) if (yycheck[x + yyn] == x) size += strlen(yytname[x]) + 15, count++; msg = (char *) malloc(size + 15); if (msg != 0) { strcpy(msg, "parse error"); if (count < 5) { count = 0; for (x = (yyn < 0 ? -yyn : 0); x < (sizeof(yytname) / sizeof(char *)); x++) if (yycheck[x + yyn] == x) { strcat(msg, count == 0 ? ", expecting `" : " or `"); strcat(msg, yytname[x]); strcat(msg, "'"); count++; } } yyerror(msg); free(msg); } else yyerror ("parse error; also virtual memory exceeded"); } else #endif /* YYERROR_VERBOSE */ yyerror("parse error"); } goto yyerrlab1; yyerrlab1: /* here on error raised explicitly by an action */ if (yyerrstatus == 3) { /* if just tried and failed to reuse lookahead token after an error, discard it. */ /* return failure if at end of input */ if (yychar == YYEOF) YYABORT; #if YYDEBUG != 0 if (yydebug) fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]); #endif yychar = YYEMPTY; } /* Else will try to reuse lookahead token after shifting the error token. */ yyerrstatus = 3; /* Each real token shifted decrements this */ goto yyerrhandle; yyerrdefault: /* current state does not do anything special for the error token. */ #if 0 /* This is wrong; only states that explicitly want error tokens should shift them. */ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ if (yyn) goto yydefault; #endif yyerrpop: /* pop the current state because it cannot handle the error token */ if (yyssp == yyss) YYABORT; yyvsp--; yystate = *--yyssp; #ifdef YYLSP_NEEDED yylsp--; #endif #if YYDEBUG != 0 if (yydebug) { short *ssp1 = yyss - 1; fprintf (stderr, "Error: state stack now"); while (ssp1 != yyssp) fprintf (stderr, " %d", *++ssp1); fprintf (stderr, "\n"); } #endif yyerrhandle: yyn = yypact[yystate]; if (yyn == YYFLAG) goto yyerrdefault; yyn += YYTERROR; if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR) goto yyerrdefault; yyn = yytable[yyn]; if (yyn < 0) { if (yyn == YYFLAG) goto yyerrpop; yyn = -yyn; goto yyreduce; } else if (yyn == 0) goto yyerrpop; if (yyn == YYFINAL) YYACCEPT; #if YYDEBUG != 0 if (yydebug) fprintf(stderr, "Shifting error token, "); #endif *++yyvsp = yylval; #ifdef YYLSP_NEEDED *++yylsp = yylloc; #endif yystate = yyn; goto yynewstate; } *[SRC.TIN-1_22]CHANGES.;1+,m.// 4;-d0@123KPWO56[t7`Xꑗ89 G/HJCHANGES tin v1.2 PL1 -> tin 1.2 PL2 ----------------------------------- 1) Colin Perkins (csp@ohm.york.ac.uk) BUG. Compile fails on Next due to use of getcwd() in misc.c FIX. misc.c - applied supplied patch. 2) Richard Lloyd (rkl@csc.liv.ac.uk) BUG. 'make install' fails because of wrong variable names. FIX. makefile - applied supplied patch. 3) Iain Lea (iain.lea@erlm.siemens.de) BUG. Return code from external news is wrong from 1.20 change. FIX. inews.c - changed back to pre v1.2 PL1 behaviour. 4) Mike Glendinning (mikeg@ingres.co.uk) BUG. directory struct dirent explicitly used instead of DIR_BUF. FIX. save.c - applied supplied patch. 5) Pieter Immelman (pi@itu1.sun.ac.za) BUG. Reading xover Xref: line when not there causes SIGSEGV. FIX. art.c - applied supplied patch. 6) Mark Tomlinson (mark@garden.equinox.gen.nz) BUG. Would not compile on AmigaDOS. FIX. applied supplied patch. 7) Julian Thompson (jrt@oasis.icl.co.uk) BUG. input_peneding() routine does not work if HAVE_POLL is defined. FIX. art.c - applied supplied patch. 8) Clifton Royston (cliftonr@netcom.com) BUG. Lines not correctly formatted in check_article_to_be_posted(). FIX. post.c - adding flushing to stdout stream. 9) Richard Lloyd (rkl@csc.liv.ac.uk) BUG. Would not compile on HPUX 9.X machines. FIX. config.h - applied supplied patch. 10) Mike Glendinning (mikeg@ingres.co.uk) BUG. SHould make use of INN nntplib routines if available. FIX. tin.h init.c inews.c - applied supplied patch. 11) Earle Ake (akee@wpdis11.wpafb.af.mil) BUG. Default mailer does not understand '@' notation on u3b2 machines. FIX. tin.h - applied supplied patch. 12) Earle Ake (akee@wpdis11.wpafb.af.mil) BUG. Default mailer path is wrong on Pyramid machines. FIX. tin.h - applied supplied patch. 13) Clifton Royston (cliftonr@netcom.com) BUG. Entering groups takes a lot longer than required by tin v1.20. FIX. tin.h newsrc.c - applied supplied patch. 14) John Schmitz (schmitz@scd.hp.com) BUG. Tcp/ip socket buffer size is very small on hpux machines. FIX. nntplib.c - applied supplied patch. 15) John Schmitz (schmitz@scd.hp.com) BUG. valid_artnum() routine does a linear search routine which is a killer in big groups. Needs converting to use a binary search. FIX. art.c - applied supplied patch. 16) Iain Lea (iain.lea@erlm.siemens.de) BUG. v1.21 would not allow articles to be posted if the group was not present in the sites active file which caused problems for sites that did not take a full feed. FIX. INSTALL post.c - Added HAVE_FACIST_NEWSADMIN to allow the message to be a warning or an error (facist mode). 17) John Schmitz (schmitz@scd.hp.com) BUG. system() call does not work correctly on hpux machines. FIX. misc.c - applied supplied patch. 18) Olaf Mittelstaedt BUG. Needs c:\tmp on a HPFS partition FIX. Use envvar TMPDIR for location of tmp directory 19) Malkani Sanjay (sanjay@uxmail.ust.hk) [OS/2] ASK. support for local newsspool ADD. support for local newsspool 20) Richard Stanton (stanton@haas.berkeley.edu) [OS/2] BUG. Cannot use C:\ as HOME. FIX. checking for trailing '\' in path before adding '\' 21) Andreas Wrede (andreas@scilink.org) BUG. Would not compile on OS/2 FIX. applied supplied patch 22) John Schmitz (schmitz@scd.hp.com) ADD. page.c - contributed MIME support via call to metamail. 23) Phil Molyneux (molyneux@kingston.ac.uk) BUG. Manual page list bug report command to be 'R' when it is actually 'B'. FIX. tin.1 - corrected manual page. 24) Andreas Baumann (andresb@kbfi.ee) BUG. Crashes if a LIST OVERVIEW.FMT is done to an ANU nntp server. FIX. open.c - added check to see if xover command is supported. 25) Iain Lea (iain.lea@erlm.siemens.de) BUG. Specifying alternate news index directory does not work. FIX. init.c - re-added check for TIN_INDEX & -I cmd line option. 26) Werner Fleck (Fleck@tu-harburg.dbp.de) BUG. If a filename like "$FOO", e.g. an env var without a trailing "/" is used strfpath() will return a bogus filename. FIX. misc.c - applied supplied patch. 27) Bryan Dongray (btd@uk.cray.com) BUG. Too I/O bound when doing HEAD calls via NNTP. FIX. art.c open.c - applied supplied patch. 28) John Schmitz (schmitz@scd.hp.com) BUG. Article checking routine should not allow user to insert From: line. FIX. post.c - applied supplied patch. 30) Bryan Dongray (btd@uk.cray.com) BUG. Does not reconnect to nntp server to timed out nntp server. FIX. nntplib.c - applied supplied patch. 31) Kazushi Marukawa (kazushi@kubota.co.jp) BUG. errno is not defined in misc.c so fails on Mips machines. FIX. misc.c - applied supplied patch 32) Mark Tomlinson (mark@garden.equinox.gen.nz) BUG. sigdisp() does not always return a value from signal(). FIX. signal.c - applied supplied patch. 33) Otto Lind (otto@coactive.com) BUG. read_newsrc_line() wastefully calling find_group_index(). FIX. newsrc.c - applied supplied patch. 34) Alan Thew (Alan.Thew@liverpool.ac.uk) BUG. does not find xref lines in Cnews generated overview files. FIX. art.c - added extra check for lowercase xref header string. 35) Brett Carver (brett@hpsrjtc.sr.hp.com) BUG. Copy of users pwd data is a ptr to static buf that returned from getpwnam() and can be overwritten if called again. FIX. init.c - saved copy of pwd entry so that its not overwriiten. 36) Clifton Royston (cliftonr@netcom.com) BUG. The INSTALL document could be clearer in machine naming text. FIX. INSTALL - applied supplied patch. 37) Ollivier Robert (roberto@keltia.frmug.fr.net) BUG. Would not compile on 386BSD. FIX. config.h - applied supplied patch. 38) Iain Lea (iain.lea@erlm.siemens.de) BUG. When searching for a group name should also look at description. FIX. search.c - modified search_group() to also check description. 39) Clifton Royston (cliftonr@netcom.com) ADD. active.c - auto-subscribe/unsubscribe to new newsgroups. Checks for the env. variables. 40) Bryan Dongray (btd@uk.cray.com) ADD. Recognizes mouse buttons when running in an xterm window. 41) Tom Parry (parry@yoyo.cc.monash.edu.au) ADD. Added -n cmd line option to only read subscribed to groups from the active file and not the whole active file. This is a big win when reading via a slow line. 42) Iain Lea (iain.lea@erlm.siemens.de) ADD. When selecting a group/subject via a number it will automatically enter the group/article. *[SRC.TIN-1_22]COMMON.PATCH;1+,z.// 4-d0@123KPWO56t7c189]VG/HJ *** common/conf.h Wed Sep 30 14:34:38 1992 --- common/conf.h Wed Nov 18 18:43:44 1992 *************** *** 4,9 **** --- 4,22 ---- */ /* + * Extensions to NNTP RFC977 (I.Lea 18-11-92) + */ + + #define XOVERVIEW /* retieve .overview index files from server */ + #define XINDEX /* retieve tin index files from server */ + #define XMOTD /* retieve message of the day from server */ + #define XUSER /* log clients username to nntp logfile */ + + #define XINDEX_DIR "/usr/spool/news/.index" + #define XMOTD_FILE "/usr/lib/news/motd" + #define SUBSCRIPTIONS_FILE "/usr/lib/news/subscriptions" + + /* * Compile time options. */ *** common/conf.h.dist Wed Sep 30 14:34:35 1992 --- common/conf.h.dist Wed Nov 18 18:44:47 1992 *************** *** 4,13 **** */ /* * Compile time options. */ - #undef ALONE /* True if we're running without inetd */ #ifdef ALONE --- 4,25 ---- */ /* + * Extensions to NNTP RFC977 (I.Lea 18-11-92) + */ + + #define XOVERVIEW /* retieve .overview index files from server */ + #define XINDEX /* retieve tin index files from server */ + #define XMOTD /* retieve message of the day from server */ + #define XUSER /* log clients username to nntp logfile */ + + #define XINDEX_DIR "/usr/spool/news/.index" + #define XMOTD_FILE "/usr/lib/news/motd" + #define SUBSCRIPTIONS_FILE "/usr/lib/news/subscriptions" + + /* * Compile time options. */ #undef ALONE /* True if we're running without inetd */ #ifdef ALONE *** common/nntp.h Wed Sep 30 14:34:38 1992 --- common/nntp.h Wed Nov 18 19:19:00 1992 *************** *** 38,43 **** --- 38,52 ---- #define OK_GOODBYE 205 /* Closing connection */ #define OK_GROUP 211 /* Group selected */ #define OK_GROUPS 215 /* Newsgroups follow */ + #ifdef XMOTD + #define OK_XMOTD 217 /* Motd (message of the day) file follows */ + #endif + #ifdef XINDEX + #define OK_XINDEX 218 /* Tin style group index file follows */ + #endif + #ifdef XOVERVIEW + #define OK_XOVERVIEW 219 /* .overview style group index file follows */ + #endif #define OK_ARTICLE 220 /* Article (head & body) follows */ #define OK_HEAD 221 /* Head follows */ #define OK_BODY 222 /* Body follows */ *************** *** 57,62 **** --- 66,80 ---- #define ERR_GOODBYE 400 /* Have to hang up for some reason */ #define ERR_NOGROUP 411 /* No such newsgroup */ #define ERR_NCING 412 /* Not currently in newsgroup */ + #ifdef XMOTD + #define ERR_XMOTD 417 /* No motd (message of the day) file */ + #endif + #ifdef XINDEX + #define ERR_XINDEX 418 /* No tin style index file for newsgroup */ + #endif + #ifdef XOVERVIEW + #define ERR_XOVERVIEW 419 /* No .overview style index file for newsgroup */ + #endif #define ERR_NOCRNT 420 /* No current article selected */ #define ERR_NONEXT 421 /* No next article in this group */ #define ERR_NOPREV 422 /* No previous article in this group */ *[SRC.TIN-1_22]CONFIG.H;13+,.// 4-d0@123KPWO56@釗7`y釗89]VG/HJ/* * Project : tin - a Usenet reader * Module : config.h * Author : I.Lea * Created : 03-09-92 * Updated : 20-09-93 * Notes : #defines to determine different OS capabilites * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #if !defined(M_AMIGA) && !defined(M_OS2) && !defined(M_UNIX) && !defined(VMS) # define M_UNIX #endif #ifdef VMS # define unlink(file) remove(file) #endif #if defined(M_XENIX) # define HAVE_PROTOTYPES_H #endif #if defined(EPIX) || defined (__hpux) || defined(PTX) # define HAVE_TERMIO_H #endif #if defined(QNX4) || defined(UMAXV) # define HAVE_TERMIOS_H #endif #if defined(_POSIX_SOURCE) || defined(__386BSD__) || defined(DGUX) || \ defined(SVR4) || defined(UMAXV) # if !defined(M_OS2) # define HAVE_POSIX_JC # endif #endif #if defined(M_OS2) || defined(SVR4) # define HAVE_LOCALE_H #endif #if defined(SVR4) # define HAVE_SETLOCALE # undef sinix /* SNI Sinix (nsc32000) */ #endif #if !defined(M_AMIGA) # define HAVE_CURSES_H #endif #if (defined(SIGCHLD) && !defined(apollo)) || defined(__hpux) # define HAVE_SYS_WAIT_H #endif #if !defined(M_OS2) # define HAVE_PWD_H #endif #if !defined(M_OS2) # define HAVE_SYS_PARAM_H #endif #if !defined(apollo) && !defined(gould) && !defined(MACH) && \ !defined(mips) && !defined(__NeXT__) && !defined(M_OS2) # define HAVE_UNISTD_H #endif #if !defined(M_AMIGA) && !defined(COHERENT) && !defined(MINIX) && \ !defined(M_OS2) && !defined(VMS) # define HAVE_SYS_IOCTL_H #endif #if !defined(M_AMIGA) && !defined(apollo) && !defined(BSD) && \ !defined(M_OS2) && !defined(sinix) && !defined(RS6000) && !defined(VMS) # define HAVE_SYS_UTSNAME_H #endif #if !defined(apollo) && !defined(EPIX) && !defined(pyr) && \ !defined(sequent) && !defined(sysV68) && !defined(UTS) && \ !defined(u3b2) # define HAVE_STDLIB_H #endif #if defined(BSD) # define HAVE_STRINGS_H #else # define HAVE_STRING_H #endif #if defined(apollo) || defined(BSD) || defined(EPIX) || defined(M_AMIGA) || \ defined(M_OS2) || defined(__osf__) || defined(UMAXV) # define HAVE_FCNTL_H #endif #if !defined(__hpux) # define HAVE_SYS_STREAM_H #endif #if !defined(apollo) && !defined(COHERENT) && !defined(__hpux) && \ !defined(M_OS2) && !defined(sinix) && !defined(UMAXV) # define HAVE_SYS_PTEM_H #endif #if !defined(apollo) && !defined(COHERENT) && !defined(M_OS2) && \ !defined(SCO_UNIX) && !defined(sinix) && !defined(SVR4) # define HAVE_SYS_PTY_H # define XWIN /* stops ISC bitching */ #endif #if defined(__386BSD__) || defined(apollo) || defined(BSD) || \ defined(__hpux) || defined(linux) || defined(M_OS2) || \ defined(__osf__) || defined(RS6000) || defined(sinix) || \ defined(UMAXV) || defined(MULTINET) # define HAVE_NETDB_H #endif #if defined(M_OS2) # define HAVE_NETLIB_H #endif #if !defined(pyr) # define HAVE_TIME_H #endif #if !defined(apollo) && !defined(M_OS2) && !defined(SCO_UNIX) && \ !defined(u3b2) && !defined(VMS) # define HAVE_SYS_TIME_H #endif #if defined(SCO_UNIX) || defined(u3b2) # define HAVE_SYS_TIMES_H #endif #if defined(PTX) || defined(RS6000) || defined(SCO_UNIX) # define HAVE_SYS_SELECT_H #endif #if defined(M_AMIGA) || defined(COHERENT) || defined(M_OS2) || defined(QNX4) # define HAVE_ERRNO_H #endif #if defined(__GNUC__) || defined(HAVE_POSIX_JC) # define HAVE_SIGTYPE_VOID #else # if defined(sony) # define HAVE_SIGTYPE_INT # else # if __STDC__ || defined(atthcx) || defined(__hpux) || \ defined(__osf__) || defined(M_OS2) || defined(PTX) || \ defined(RS6000) || defined(sgi) || defined(sinix) || \ defined(sysV68) || defined(sun) || defined(SVR4) || \ defined(u3b2) || defined(ultrix) # define HAVE_SIGTYPE_VOID # else # define HAVE_SIGTYPE_INT # endif # endif #endif #if defined(M_OS2) # define HAVE_COMPTYPE_VOID #else # define HAVE_COMPTYPE_CHAR #endif #if defined(apollo) || defined(AUX) || defined(BSD) || defined(linux) || \ defined(__hpux) || defined(__osf__) || defined(PTX) || defined(QNX4) || \ defined(RS6000) || defined(sinix) || defined(SVR4) || defined(UMAXV) # define HAVE_LONG_FILENAMES #endif #if defined(apollo) || defined(BSD) || defined(__hpux) || defined(linux) || \ defined(__osf__) || defined(M_OS2) || defined(RS6000) || defined(sinix) || \ defined(UMAXV) || defined(MULTINET) # define HAVE_GETHOSTBYNAME #endif #if defined(M_AMIGA) || defined(apollo) || defined(BSD) || defined(MINIX) || defined(VMS) # define HAVE_CR_AS_CHAR #endif /* * Used in tin.h */ #if __STDC__ || defined(SVR4) # if !defined(apollo) && !defined(__hpux) # define HAVE_ANSI_ASSERT # endif #endif #if defined(M_UNIX) # define HAVE_COREFILE #endif #if defined(M_UNIX) # define HAVE_SET_GID_UID #endif #if defined(M_UNIX) && !defined(__386BSD__) # define HAVE_UNAME #endif #if defined(MACH) || defined(__NeXT__) || defined(M_OS2) # define DONT_HAVE_SIGWINCH #endif #if defined(BSD) || defined(EPIX) && !defined(__386BSD__) && \ !defined(sinix) # define DONT_HAVE_GETCWD #endif #if defined(pyr) || defined(sequent) # define DONT_HAVE_MEMCMP #endif #if defined(BSD) && !defined(__386BSD__) # define DONT_HAVE_STRCHR #endif #if defined(__arm) || defined(COHERENT) || defined(pyr) || defined(sequent) # define DONT_HAVE_TZSET #endif /* * Used in parsedate.y */ #if defined(apollo) || defined(__arm) || defined(__convex__) || \ defined(DGUX) || defined(pyr) || defined(sequent) || !defined(BSD) # define DONT_HAVE_TM_GMTOFF #endif /* * Use poll()/select() in input_pending() */ #if defined(SVR4) || defined(HAVE_POLL) # if !defined(__hpux) # define HAVE_STROPTS_H # endif # define HAVE_POLL_H # if !defined(HAVE_POLL) # define HAVE_POLL # endif # undef HAVE_SELECT #endif #if !defined(apollo) && !defined(M_AMIGA) && !defined(COHERENT) && \ !defined(M_OS2) && !defined(QNX4) && !defined(supermax) && \ !defined(u3b2) && !defined(HAVE_POLL) # define HAVE_SELECT #endif #if defined(M_AMIGA) || defined(QNX4) # define SMALL_MEMORY_MODEL #endif #if defined(M_AMIGA) # define DONT_REREAD_ACTIVE_FILE #endif #if defined(COHERENT) # define HAVE_SETTZ #endif #if defined(M_UNIX) # define HAVE_FORK #endif #if defined(M_OS2) # define HAVE_STRFTIME #endif #if !defined(M_OS2) && !defined(VMS) # define HAVE_SYSERRLIST #endif #if defined(__hpux) # define DONT_PROTOTYPE_PTR_TO_FUNC # define DONT_HAVE_SYS_BSDTYPES_H # define HAVE_KEYPAD #endif #if defined(RS6000) # define READ_CHAR_HACK #endif #if defined(sinix) # undef HAVE_SYS_STREAM_H # define DONT_HAVE_MKDIR #endif #if defined(supermax) # define HAVE_BROKEN_TGETSTR #endif #if defined(QNX4) # define HAVE_TCGETATTR # define HAVE_TCSETATTR #endif #if defined(M_AMIGA) || defined(QNX) # define HAVE_KEY_PREFIX #endif #if defined(M_UNIX) # define HAVE_METAMAIL #endif /* * Hack used to try and get a compile on Sun i386 & old SunOS 4.0.2 */ #if defined(sun) && defined(i386) # undef HAVE_STDLIB_H #endif #if defined(BSD) || defined(__osf__) || defined(_POSIX_SOURCE) # define HAVE_REWINDDIR #endif /* * Various hacks used to try and get a compile on the strange ones... */ #if defined(u3b2) # define size_t unsigned long int #endif /* * Lets try and be a wise ass and make a nntp able binary * for machines where the netlibs are in the libc library */ #if defined(BSD) || defined(linux) || defined(RS6000) # if !defined(NNTP_ABLE) # define NNTP_ABLE # endif #endif d*[SRC.TIN-1_22]CURSES.C;13+,.!// 4! {-d0@123KPWO"56N7`P89IʒG/HJp~ TIN-1_22.BCKd[SRC.TIN-1_22]CURSES.C;13!U0/* * Project : tin - a Usenet reader * Module : curses.c * Author : D.Taylor & I.Lea * Created : 01-01-86 * Updated : 22-09-93 * Notes : This is a screen management library borrowed with permission * from the Elm mail system (a great mailer--I highly recommend * it!).This library was hacked to provide what tin needs. * Copyright : Copyright (c) 1986-93 Dave Taylor & Iain Lea * The Elm Mail System - $Revision: 2.1 $ $State: Exp $ */ #include "tin.h" #ifndef ns32000 # undef sinix #endif #ifdef VMS #include #include #include #include #include #endif #if defined(HAVE_ERRNO_H) || defined(VMS) # include #else # include #endif #define DEFAULT_LINES_ON_TERMINAL 24 #define DEFAULT_COLUMNS_ON_TERMINAL 80 int cLINES = DEFAULT_LINES_ON_TERMINAL - 1; int cCOLS = DEFAULT_COLUMNS_ON_TERMINAL; int inverse_okay = TRUE; static int _inraw = FALSE; /* are we IN rawmode? */ int _hp_glitch = FALSE; /* standout not erased by overwriting on HP terms */ static int xclicks=FALSE; /* do we have an xterm? */ #ifndef INDEX_DAEMON #define BACKSPACE '\b' #define VERY_LONG_STRING 2500 #if (defined(M_AMIGA) && !defined(__SASC)) || defined(COHERENT) || defined(BSD) # ifndef BSD4_1 # include # else # include # endif #else # ifndef SYSV # ifndef MINIX # ifdef sinix # include # else # ifdef VMS # include # else # include # endif # endif # else # include # endif # else # if defined(__hpux) || (defined(sun) && defined(SVR4)) # include # endif # endif #endif #define TTYIN 0 #ifdef SHORTNAMES # define _clearinverse _clrinv # define _cleartoeoln _clrtoeoln # define _cleartoeos _clr2eos #endif #ifndef VMS #if (defined(M_AMIGA) && !defined(__SASC)) || defined(BSD) || defined(MINIX) # ifdef TCGETA # undef TCGETA # endif # define TCGETA TIOCGETP # ifdef TCSETAW # undef TCSETAW # endif # define TCSETAW TIOCSETP struct sgttyb _raw_tty, _original_tty; #else # if !defined(M_AMIGA) # if defined(HAVE_TERMIOS_H) || defined(sinix) # ifndef TCGETA # define TCGETA STCGETA # endif # ifndef TCSETA # define TCSETAW STCSETAW # endif struct termios _raw_tty, _original_tty; # else # ifndef M_OS2 struct termio _raw_tty, _original_tty; # endif # endif # endif #endif #endif static char *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos, *_setinverse, *_clearinverse, *_setunderline, *_clearunderline, *_xclickinit, *_xclickend, *_terminalinit, *_terminalend, *_keypadlocal, *_keypadxmit; static int _columns, _line, _lines; #ifdef M_UNIX static char _terminal[1024]; /* Storage for terminal entry */ static char _capabilities[1024]; /* String for cursor motion */ static char *ptr = _capabilities; /* for buffering */ extern char *tgetstr (); /* Get termcap capability (string) */ extern char *tgoto (); /* and the goto stuff */ extern int tgetflag (); /* Get termcap capability (boolean) */ extern int tgetnum (); /* Get termcap capability (number) */ #endif /* M_UNIX */ static int in_inverse; /* 1 when in inverse, 0 otherwise */ #if __STDC__ int outchar (int c); /* char output for tputs */ #else int outchar (); #endif #endif /* INDEX_DAEMON */ void setup_screen () { #ifndef INDEX_DAEMON /* * get screen size from termcap entry & setup sizes */ _line = 1; ScreenSize (&cLINES, &cCOLS); cmd_line = FALSE; Raw (TRUE); set_win_size (&cLINES, &cCOLS); #endif /* INDEX_DAEMON */ } #ifdef M_UNIX int InitScreen () { #ifndef INDEX_DAEMON extern int tgetent(); /* get termcap entry */ char termname[40], *p; if ((p = (char *) getenv ("TERM")) == NULL) { fprintf (stderr, "%s: TERM variable must be set to use screen capabilities\n", progname); return (FALSE); } if (strcpy (termname, p) == NULL) { fprintf (stderr,"%s: Can't get TERM variable\n", progname); return (FALSE); } if (tgetent (_terminal, termname) != 1) { fprintf (stderr,"%s: Can't get entry for TERM\n", progname); return (FALSE); } /* load in all those pesky values */ _clearscreen = tgetstr ("cl", &ptr); _moveto = tgetstr ("cm", &ptr); _cleartoeoln = tgetstr ("ce", &ptr); _cleartoeos = tgetstr ("cd", &ptr); _lines = tgetnum ("li"); _columns = tgetnum ("co"); _setinverse = tgetstr ("so", &ptr); _clearinverse = tgetstr ("se", &ptr); _setunderline = tgetstr ("us", &ptr); _clearunderline = tgetstr ("ue", &ptr); _hp_glitch = tgetflag ("xs"); #ifdef HAVE_BROKEN_TGETSTR _terminalinit = ""; _terminalend = ""; _keypadlocal = ""; _keypadxmit = ""; #else _terminalinit = tgetstr ("ti", &ptr); _terminalend = tgetstr ("te", &ptr); _keypadlocal = tgetstr ("ke", &ptr); _keypadxmit = tgetstr ("ks", &ptr); #endif if (strcmp (termname, "xterm") == 0) { xclicks = TRUE; _xclickinit = "\033[?9h"; _xclickend = "\033[?9l"; } InitWin (); if (!_clearscreen) { fprintf (stderr, "%s: Terminal must have clearscreen (cl) capability\n",progname); return (FALSE); } if (!_moveto) { fprintf (stderr, "%s: Terminal must have cursor motion (cm)\n", progname); return (FALSE); } if (!_cleartoeoln) { fprintf (stderr, "%s: Terminal must have clear to end-of-line (ce)\n", progname); return (FALSE); } if (!_cleartoeos) { fprintf (stderr, "%s: Terminal must have clear to end-of-screen (cd)\n", progname); return (FALSE); } if (_lines == -1) _lines = DEFAULT_LINES_ON_TERMINAL; if (_columns == -1) _columns = DEFAULT_COLUMNS_ON_TERMINAL; /* * kludge to workaround no inverse */ if (_setinverse == 0) { _setinverse = _setunderline; _clearinverse = _clearunderline; if (_setinverse == 0) draw_arrow_mark = 1; } return (TRUE); #else return (FALSE); #endif /* INDEX_DAEMON */ } #else /* ! M_UNIX */ int InitScreen () { #ifndef INDEX_DAEMON char *ptr; /* * we're going to assume a terminal here... */ _clearscreen = "\033[1;1H\033[J"; _moveto = "\033[%d;%dH"; /* not a termcap string! */ _cleartoeoln = "\033[K"; _setinverse = "\033[7m"; _clearinverse = "\033[0m"; _setunderline = "\033[4m"; _clearunderline = "\033[0m"; _keypadlocal = ""; _keypadxmit = ""; #ifdef M_AMIGA _terminalinit = "\033c"; _terminalend = "\033c"; _cleartoeos = "\033[J"; #endif #if defined(M_OS2) || defined(VMS) _cleartoeos = NULL; _terminalinit = NULL; _terminalend = NULL; #ifndef VMS initscr (); #endif #endif InitWin (); _lines = _columns = -1; /* * Get lines and columns from environment settings - useful when * you're using something other than an Amiga window */ if (ptr = getenv ("LINES")) { _lines = atol (ptr); } if (ptr = getenv ("COLUMNS")) { _columns = atol (ptr); } /* * If that failed, try get a response from the console itself */ #ifdef M_AMIGA if (_lines == -1 || _columns == -1) { Raw (TRUE); tputs ("\2330 q",1,outchar); /* identify yourself */ fflush (stdout); getsize: while (ReadCh () != 0x9b) { ; /* Look for escape */ } /* get top */ ptr = buf; do { c = *ptr++ = ReadCh (); } while (isdigit(c)); if (c != ';') { goto getsize; } /* get right */ ptr = buf; do { c = *ptr++ = ReadCh (); } while (isdigit(c)); if (c != ';') { goto getsize; } /* get bottom */ ptr = buf; do { c = *ptr++ = ReadCh (); } while (isdigit(c)); if (c != ';') { goto getsize; } *ptr = 0; _lines = atol (buf); /* get right */ ptr = buf; do { c = *ptr++ = ReadCh (); } while (isdigit(c)); if (c != ' ') { goto getsize; } if (ReadCh () != 'r') { goto getsize; } *ptr = 0; _columns = atol (buf); } #endif /* M_AMIGA */ #ifdef M_OS2 if (_lines == -1 || _columns == -1) { _lines = LINES; _columns = COLS; } #endif /* M_OS2 */ #ifdef VMS { int input_chan, status; struct sensemode { short status; unsigned char xmit_baud; unsigned char rcv_baud; unsigned char crfill; unsigned char lffill; unsigned char parity; unsigned char unused; char class; char type; short scr_wid; unsigned long tt_char : 24, scr_len : 8; unsigned long tt2_char; } tty; $DESCRIPTOR (input_dsc, "TT"); status = SYS$ASSIGN (&input_dsc, &input_chan, 0, 0); if (! (status & 1)) LIB$STOP (status); SYS$QIOW (0, input_chan, IO$_SENSEMODE, &tty, 0, 0, &tty.class, 12, 0, 0, 0, 0); _columns = tty.scr_wid; _lines = tty.scr_len; } #endif Raw (FALSE); return (TRUE); #else return (FALSE); #endif /* INDEX_DAEMON */ } #endif /* M_UNIX */ /* * returns the number of lines and columns on the display. */ void ScreenSize (num_lines, num_columns) int *num_lines, *num_columns; { #ifndef INDEX_DAEMON if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL; if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL; *num_lines = _lines - 1; /* assume index from zero*/ *num_columns = _columns; /* assume index from one */ #endif /* INDEX_DAEMON */ } void InitWin () { #ifndef INDEX_DAEMON if (_terminalinit) { tputs (_terminalinit, 1, outchar); fflush (stdout); } set_keypad_on (); set_xclick_on (); #endif /* INDEX_DAEMON */ } void EndWin () { #ifndef INDEX_DAEMON if (_terminalend) { tputs (_terminalend, 1, outchar); fflush (stdout); } set_keypad_off (); set_xclick_off (); #endif /* INDEX_DAEMON */ } void set_keypad_on () { #ifndef INDEX_DAEMON # ifdef HAVE_KEYPAD if (use_keypad && _keypadxmit) { tputs (_keypadxmit, 1, outchar); fflush (stdout); } # endif #endif /* INDEX_DAEMON */ } void set_keypad_off () { #ifndef INDEX_DAEMON # ifdef HAVE_KEYPAD if (use_keypad && _keypadlocal) { tputs (_keypadlocal, 1, outchar); fflush (stdout); } # endif #endif /* INDEX_DAEMON */ } /* * clear the screen: returns -1 if not capable */ void ClearScreen () { #ifndef INDEX_DAEMON tputs (_clearscreen, 1, outchar); fflush (stdout); /* clear the output buffer */ _line = 1; #endif /* INDEX_DAEMON */ } /* * move cursor to the specified row column on the screen. * 0,0 is the top left! */ #ifdef M_UNIX void MoveCursor (row, col) int row, col; { #ifndef INDEX_DAEMON char *stuff, *tgoto(); stuff = tgoto (_moveto, col, row); tputs (stuff, 1, outchar); fflush (stdout); _line = row + 1; #endif /* INDEX_DAEMON */ } #else /* ! M_UNIX */ void MoveCursor (row, col) int row, col; { #ifndef INDEX_DAEMON char stuff[12], *tgoto(); sprintf (stuff, _moveto, row+1, col+1); tputs (stuff, 1, outchar); fflush (stdout); _line = row + 1; #endif /* INDEX_DAEMON */ } #endif /* M_UNIX */ /* * clear to end of line */ void CleartoEOLN () { #ifndef INDEX_DAEMON tputs (_cleartoeoln, 1, outchar); fflush (stdout); /* clear the output buffer */ #endif /* INDEX_DAEMON */ } /* * clear to end of screen */ void CleartoEOS () { #ifndef INDEX_DAEMON int i; if (_cleartoeos) { tputs (_cleartoeos, 1, outchar); } else { for (i=_line - 1 ; i < _lines ; i++) { MoveCursor (i, 0); CleartoEOLN (); } } fflush (stdout); /* clear the output buffer */ #endif /* INDEX_DAEMON */ } /* * set inverse video mode */ void StartInverse () { #ifndef INDEX_DAEMON in_inverse = 1; if (_setinverse && inverse_okay) tputs (_setinverse, 1, outchar); fflush (stdout); #endif /* INDEX_DAEMON */ } /* * compliment of startinverse */ void EndInverse () { #ifndef INDEX_DAEMON in_inverse = 0; if (_clearinverse && inverse_okay) tputs (_clearinverse, 1, outchar); fflush (stdout); #endif /* INDEX_DAEMON */ } /* * toggle inverse video mode */ void ToggleInverse () { #ifndef INDEX_DAEMON if (in_inverse == 0) StartInverse(); else EndInverse(); #endif /* INDEX_DAEMON */ } /* * returns either 1 or 0, for ON or OFF */ int RawState() { return (_inraw); } /* * state is either TRUE or FALSE, as indicated by call */ void Raw (state) int state; { #ifdef VMS if (state == FALSE && _inraw) { /* vmsnoraw();*/ _inraw = 0; } else if (state == TRUE && ! _inraw) { /* vmsraw();*/ _inraw = 1; } #else #if !defined(INDEX_DAEMON) && !defined(M_OS2) #if defined(M_AMIGA) && defined(__SASC) rawcon (state); #else if (state == FALSE && _inraw) { # ifdef HAVE_TCSETATTR (void) tcsetattr (TTYIN, TCSANOW, &_original_tty); # else (void) ioctl (TTYIN, TCSETAW, &_original_tty); # endif _inraw = 0; } else if (state == TRUE && ! _inraw) { #ifdef HAVE_TCGETATTR (void) tcgetattr (TTYIN, &_original_tty); (void) tcgetattr (TTYIN, &_raw_tty); #else (void) ioctl (TTYIN, TCGETA, &_original_tty); /* current setting */ (void) ioctl (TTYIN, TCGETA, &_raw_tty); /* again! */ #endif #if defined(BSD) || defined(M_AMIGA) || defined(MINIX) _raw_tty.sg_flags &= ~(ECHO | CRMOD); /* echo off */ _raw_tty.sg_flags |= CBREAK; /* raw on */ #ifdef M_AMIGA _raw_tty.sg_flags |= RAW; /* Manx-C 5.2 does not support CBREAK */ #endif #else #ifdef QNX4 /* noecho raw mode */ _raw_tty.c_lflag &= ~(ICANON |ISIG| ECHO |ECHOK | ECHONL); _raw_tty.c_oflag &= ~(OPOST); _raw_tty.c_cc[VMIN] = 1; /* minimum # of chars to queue */ _raw_tty.c_cc[VTIME] = 0; /* minimum time to wait for input */ #else _raw_tty.c_lflag &= ~(ICANON | ECHO); /* noecho raw mode */ _raw_tty.c_cc[VMIN] = '\01'; /* minimum # of chars to queue */ _raw_tty.c_cc[VTIME] = '\0'; /* minimum time to wait for input */ #endif /* QNX4 */ #endif #ifdef HAVE_TCSETATTR (void) tcsetattr (TTYIN, TCSANOW, &_raw_tty); #else (void) ioctl (TTYIN, TCSETAW, &_raw_tty); #endif _inraw = 1; } #endif /* M_AMIGA */ #endif /* VMS */ #endif /* INDEX_DAEMON */ } /* * read a character with Raw mode set! */ #ifdef M_OS2 int ReadCh () { #ifndef INDEX_DAEMON extern int errno; char ch; KBDKEYINFO os2key; int rc; register int result = 0; static secondkey = 0; if (secondkey) { result = secondkey; secondkey = 0; } else { rc = KbdCharIn((PKBDKEYINFO)&os2key, IO_WAIT, 0); result = os2key.chChar; if (result == 0xe0) { result = 0x1b; switch (os2key.chScan) { case 'H': secondkey = 'A'; break; case 'P': secondkey = 'B'; break; case 'K': secondkey = 'D'; break; case 'M': secondkey = 'C'; break; case 'I': secondkey = 'I'; break; case 'Q': secondkey = 'G'; break; case 'G': secondkey = 'H'; break; case 'O': secondkey = 'F'; break; default: secondkey = '?'; } } else if (result == 0x0d) { result = 0x0a; } } return ((result == EOF) ? EOF : result & 0xFF); #endif /* INDEX_DAEMON */ } #else /* ! M_OS2 */ #ifndef VMS int ReadCh () { #ifndef INDEX_DAEMON extern int errno; char ch; register int result = 0; #ifdef READ_CHAR_HACK #undef getc while ((result = getc(stdin)) == EOF) { if (feof(stdin)) break; #ifdef EINTR if (ferror(stdin) && errno != EINTR) #else if (ferror(stdin)) #endif break; clearerr(stdin); } return ((result == EOF) ? EOF : result & 0xFF); #else # ifdef EINTR while ((result = read (0, &ch, 1)) < 0 && errno == EINTR) ; /* spin on signal interrupts */ # else result = read (0, &ch, 1); # endif return((result <= 0 ) ? EOF : ch & 0xFF); #endif #endif /* INDEX_DAEMON */ } #endif /* VMS */ #endif /* M_OS2 */ /* * output a character. From tputs... (Note: this CANNOT be a macro!) */ int outchar (c) int c; { return fputc (c, stdout); } /* * setup to monitor mouse buttons if running in a xterm */ void xclick (state) int state; { #ifndef INDEX_DAEMON static int prev_state = 999; if (xclicks && prev_state != state) { if (state == TRUE) { tputs (_xclickinit, 1, outchar); } else { tputs (_xclickend, 1, outchar); } fflush (stdout); prev_state = state; } #endif /* INDEX_DAEMON */ } /* * switch on monitoring of mouse buttons */ void set_xclick_on () { xclick (TRUE); } /* * switch off monitoring of mouse buttons */ void set_xclick_off () { xclick (FALSE); } *[SRC.TIN-1_22]DEBUG.C;1+,.// 4-d0@123KPWO56`t7ei189]VG/HJ/* * Project : tin - a Usenet reader * Module : debug.c * Author : I.Lea * Created : 01-04-91 * Updated : 02-10-92 * Notes : debug routines * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" int debug; /* * nntp specific debug routines */ void debug_nntp (func, line) char *func; char *line; { #ifdef DEBUG char file[PATH_LEN]; FILE *fp; if (debug == 0) return; sprintf (file, "%sNNTP", TMPDIR); if ((fp = fopen (file, "a+")) != NULL) { fprintf (fp,"%s: %s\n", func, line); fclose (fp); chmod (file, 0666); } #endif } void debug_nntp_respcode (respcode) int respcode; { #ifdef DEBUG debug_nntp ("get_respcode", nntp_respcode (respcode)); #endif } /* * tin specific debug routines */ void debug_print_arts () { #ifdef DEBUG int i; if (debug != 2) return; for (i = 0; i < top; i++) { /* for each group */ debug_print_header (&arts[i]); } #endif } void debug_print_header (s) struct t_article *s; { #ifdef DEBUG char file[PATH_LEN]; FILE *fp; if (debug != 2) return; sprintf (file, "%sARTS", TMPDIR); if ((fp = fopen (file, "a+")) != NULL) { fprintf (fp,"art=[%5ld] tag=[%s] kill=[%s] hot=[%s]\n", s->artnum, (s->tagged ? "TRUE" : "FALSE"), (s->killed ? "TRUE" : "FALSE"), (s->hot ? "TRUE" : "FALSE")); fprintf (fp,"subj=[%-38s]\n", s->subject); fprintf (fp,"date=[%ld] from=[%s] name=[%s]\n", s->date, s->from, s->name); if (s->archive) { fprintf (fp, "arch=[%-38s] ", s->archive); } else { fprintf (fp, "arch=[] "); } if (s->part) { fprintf (fp, "part=[%s] ", s->part); } else { fprintf (fp, "part=[] "); } if (s->patch) { fprintf (fp, "patch=[%s]\n", s->patch); } else { fprintf (fp, "patch=[]\n"); } fprintf (fp,"thread=[%d] inthread=[%d] unread=[%d]\n\n", s->thread, s->inthread, s->unread); /* fprintf (fp,"thread=[%s] inthread=[%s] unread=[%s]\n", (s->thread == ART_NORMAL ? "ART_NORMAL" : "ART_EXPIRED"), (s->inthread ? "TRUE" : "FALSE"), (s->unread ? "TRUE" : "FALSE")); */ fflush (fp); fclose (fp); chmod (file, 0666); } #endif } void debug_save_comp () { #ifdef DEBUG char file[PATH_LEN]; FILE *fp; int i; if (debug != 2) return; sprintf (file, "%sSAVE_COMP", TMPDIR); if ((fp = fopen (file, "a+")) != NULL) { for (i = 0 ; i < num_save ; i++) { fprintf (fp,"subj=[%-38s]\n", save[i].subject); fprintf (fp,"dir=[%s] file=[%s]\n", save[i].dir, save[i].file); if (save[i].archive) { fprintf (fp, "arch=[%-38s] ", save[i].archive); } else { fprintf (fp, "arch=[] "); } if (save[i].part) { fprintf (fp, "part=[%s] ", save[i].part); } else { fprintf (fp, "part=[] "); } if (save[i].patch) { fprintf (fp, "patch=[%s]\n", save[i].patch); } else { fprintf (fp, "patch=[]\n"); } fprintf (fp,"index=[%d] saved=[%d] mailbox=[%d]\n\n", save[i].index, save[i].saved, save[i].is_mailbox); } fflush (fp); fclose (fp); chmod (file, 0666); } #endif } void debug_print_comment (comment) char *comment; { #ifdef DEBUG char file[PATH_LEN]; FILE *fp; if (debug != 2) return; sprintf (file, "%sBASE", TMPDIR); if ((fp = fopen (file, "a+")) != NULL) { fprintf (fp,"\n%s\n\n", comment); fflush (fp); fclose (fp); chmod (file, 0666); } #endif } void debug_print_base () { #ifdef DEBUG char file[PATH_LEN]; FILE *fp; int i; if (debug != 2) return; sprintf (file, "%sBASE", TMPDIR); if ((fp = fopen (file, "a+")) != NULL) { for (i = 0; i < top_base; i++) { fprintf (fp, "base[%3d]=[%5ld]\n",i,base[i]); } fflush (fp); fclose (fp); chmod (file, 0666); } #endif } void debug_print_active () { #ifdef DEBUG char file[PATH_LEN]; FILE *fp; int i; if (debug != 2) return; sprintf (file, "%sACTIVE", TMPDIR); if ((fp = fopen (file, "w")) != NULL) { for (i = 0; i < num_active; i++) { /* for each group */ fprintf (fp, "[%4d]=[%-28s] type=[%d] spooldir=[%s]\n", i, active[i].name, active[i].type, active[i].spooldir); fprintf (fp, "max=[%4ld] min=[%4ld] mod=[%c] nxt=[%4d] my_group=[%d]\n", active[i].max, active[i].min, active[i].moderated, active[i].next, active[i].my_group); fprintf (fp, "hash=[%ld] description=[%s]\n", hash_groupname (active[i].name), (active[i].description ? active[i].description : "")); fprintf (fp, "read=[%d] show=[%d] thread=[%d] sort=[%d] author=[%d] auto=[%d] process=[%d]\n", active[i].attribute.read_during_session, active[i].attribute.show_only_unread, active[i].attribute.thread_arts, active[i].attribute.sort_art_type, active[i].attribute.show_author, active[i].attribute.auto_save, active[i].attribute.post_proc_type); fprintf (fp, "maildir=[%s] savedir=[%s]\n", (active[i].attribute.maildir == (char *) 0 ? "" : active[i].attribute.maildir), (active[i].attribute.savedir == (char *) 0 ? "" : active[i].attribute.savedir)); fprintf (fp, "sigfile=[%s] followup_to=[%s]\n\n", (active[i].attribute.sigfile == (char *) 0 ? "" : active[i].attribute.sigfile), (active[i].attribute.followup_to == (char *) 0 ? "" : active[i].attribute.followup_to)); } fflush (fp); fclose (fp); chmod (file, 0666); } #endif } /* * Prints out hash distribution of active[] */ void debug_print_active_hash () { #ifdef DEBUG int empty = 0, number = 0; int collisions[32]; register i, j; for (i = 0; i < 32; i++) { collisions[i] = 0; } for (i = 0; i < TABLE_SIZE; i++) { /* printf ("HASH[%4d] ", i); */ if (group_hash[i] == -1) { /* printf ("EMPTY\n"); */ empty++; } else { number = 1; for (j=group_hash[i]; active[j].next >= 0; j=active[j].next) { number++; } if (number > 31) { printf ("MEGA HASH COLLIS ION > 31 HASH[%d]=[%d]!!!\n", i, number); } else { collisions[number]++; } } } printf ("HashTable Active=[%d] Size=[%d] Filled=[%d] Empty=[%d]\n", num_active, TABLE_SIZE, TABLE_SIZE-empty, empty); printf ("01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32\n"); printf ("-----------------------------------------------------------------------------------------------\n"); for (i = 0; i < 32; i++) { if (i) { printf ("%2d ", collisions[i]); } } printf ("\n"); #endif } *[SRC.TIN-1_22]ENVARG.C;1+,.// 4 -d0@123KPWO56`nt7ei189]VG/HJ /* * Project : tin - a Usenet reader * Module : envarg.c * Author : B.Davidson * Created : 10-13-91 * Updated : 01-10-92 * Notes : Adds default options from environment to command line * Copyright : (c) Copyright 1991-93 by Bill Davidsen * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" int count_args (s) char *s; { int count = 0; int ch; do { /* * count and skip args */ ++count; while ((ch = *s) != '\0' && ch != ' ') ++s; while ((ch = *s) != '\0' && ch == ' ') ++s; } while (ch); return count; } void envargs (Pargc, Pargv, envstr) int *Pargc; char ***Pargv; char *envstr; { char *envptr; /* value returned by getenv */ char *bufptr; /* copy of env info */ int argc = 0; /* internal arg count */ int ch; /* spare temp value */ char **argv; /* internal arg vector */ char **argvect; /* copy of vector address */ /* * see if anything in the environment */ envptr = getenv (envstr); if (envptr == (char *) 0 || *envptr == 0) { return; } /* * count the args so we can allocate room for them */ argc = count_args (envptr); bufptr = (char *) my_malloc (1+strlen (envptr)); strcpy (bufptr, envptr); /* * allocate a vector large enough for all args */ argv = (char **) my_malloc ((argc+*Pargc+1)*sizeof(char *)); argvect = argv; /* * copy the program name first, that's always true */ *(argv++) = *((*Pargv)++); /* * copy the environment args first, may be changed */ do { *(argv++) = bufptr; /* * skip the arg and any trailing blanks */ while ((ch = *bufptr) != '\0' && ch != ' ') ++bufptr; if (ch == ' ') *(bufptr++) = '\0'; while ((ch = *bufptr) != '\0' && ch == ' ') ++bufptr; } while (ch); /* * now save old argc and copy in the old args */ argc += *Pargc; while (--(*Pargc)) *(argv++) = *((*Pargv)++); /* * finally, add a NULL after the last arg, like UNIX */ *argv = (char *) 0; /* * save the values and return */ *Pargv = argvect; *Pargc = argc; } #ifdef TEST void main (argc, argv) int argc; /* number of tokens in command line */ char **argv; /* command line tokens */ { int ch; envargs (&argc, &argv, "OPTS"); while ((ch = getopt (argc, argv, "abc")) != EOF) { switch (ch) { case 'a': puts ("Option 'a' specified"); break; case 'b': puts ("Option 'b' specified"); break; case 'c': puts ("Option 'c' specified"); break; default: puts ("Option %c unknown"); break; } } } #endif *[SRC.TIN-1_22]EXTERN.H;1+,.-// 4---d0@123KPWO.56 ht7|e189]VG/HJ/* * Project : tin - a Usenet reader * Module : extern.h * Author : I.Lea * Created : 01-04-91 * Updated : 22-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ /* * Library prototypes */ #if defined(__GNUC__) && defined(DEBUG) extern unsigned int alarm (unsigned int seconds); extern int close (int fildes); extern int fork (void); extern int getpid (void); extern char *getcwd (char *buf, int size); #ifndef DGUX extern unsigned short getuid (void); extern unsigned short getegid (void); extern unsigned short geteuid (void); #endif /* DGUX */ extern int getopt (int argc, char **argv, char *optstring); extern int kill (int pid, int sig); extern int link (char *path1, char *path2); extern int open (char *path, int oflag); extern int pclose (FILE *stream); extern int read (int fildes, char *buf, unsigned int nbyte); extern int setgid (int gid); extern int setuid (int uid); extern int tgetent (char *bp, char *name); extern int tputs (register char *cp, int count, int (*outc)(int)); extern int unlink (char *path); #ifndef sony # ifndef DGUX extern unsigned short getgid (void); # endif /* DGUX */ extern void setpgrp (void); extern unsigned int sleep (unsigned int seconds); #endif #endif #if !__STDC__ extern char *getenv (); #endif extern int optind; extern char *optarg; /* * Local variables */ extern char *help_group[]; extern char *help_page[]; extern char *help_select[]; extern char *help_spooldir[]; extern char *help_thread[]; extern char active_times_file[PATH_LEN]; extern char add_addr[LEN]; extern char article[PATH_LEN]; extern char attributes_file[PATH_LEN]; extern char bug_addr[LEN]; extern char cmd_line_printer[PATH_LEN]; extern char cvers[LEN]; extern char dead_article[PATH_LEN]; extern char default_art_search[LEN]; extern char default_body_search[LEN]; extern char default_author_search[LEN]; extern char default_crosspost_group[LEN]; extern char default_editor_format[PATH_LEN]; extern char default_mail_address[LEN]; extern char default_organization[PATH_LEN]; extern char default_pipe_command[LEN]; extern char default_post_newsgroups[PATH_LEN]; extern char default_post_subject[PATH_LEN]; extern char default_regex_pattern[LEN]; extern char default_save_file[PATH_LEN]; extern char default_select_pattern[LEN]; extern char default_shell_command[LEN]; extern char delgroups[LEN]; extern char default_goto_group[LEN]; extern char default_group_search[LEN]; extern char default_maildir[PATH_LEN]; extern char default_savedir[PATH_LEN]; extern char default_sigfile[PATH_LEN]; extern char default_signature[PATH_LEN]; extern char default_subject_search[LEN]; extern char homedir[PATH_LEN]; extern char index_maildir[PATH_LEN]; extern char index_newsdir[PATH_LEN]; extern char killfile[PATH_LEN]; extern char killfrom[LEN]; extern char killsubj[LEN]; extern char libdir[PATH_LEN]; extern char lock_file[PATH_LEN]; extern char local_newsgroups_file[PATH_LEN]; extern char mail_active_file[PATH_LEN]; extern char mail_quote_format[PATH_LEN]; extern char news_active_file[PATH_LEN]; extern char news_quote_format[PATH_LEN]; extern char mail_news_user[LEN]; extern char mailbox[PATH_LEN]; extern char mailer[PATH_LEN]; extern char motd_file[PATH_LEN]; extern char motd_file_info[PATH_LEN]; extern char msg[LEN]; extern char my_distribution[LEN]; extern char new_active_file_attribute[32]; extern char new_active_file_server[PATH_LEN]; extern char mailgroups_file[PATH_LEN]; extern char newsgroups_file[PATH_LEN]; extern char newnewsrc[PATH_LEN]; extern char newsrc[PATH_LEN]; extern char *nntp_server; extern char page_header[LEN]; extern char postfile[PATH_LEN]; extern char default_printer[LEN]; extern char novrootdir[PATH_LEN]; extern char proc_ch_default; /* set in change_rcfile () */ extern char progname[PATH_LEN];7~ TIN-1_22.BCKd[SRC.TIN-1_22]EXTERN.H;1-J extern char quote_chars[PATH_LEN]; extern char rcdir[PATH_LEN]; extern char rcfile[PATH_LEN]; extern char redirect_output[LEN]; extern char reply_to[LEN]; extern char save_active_file[PATH_LEN]; extern char spooldir[PATH_LEN]; extern char spooldir_alias[PATH_LEN]; extern char subscriptions_file[PATH_LEN]; extern char userid[PATH_LEN]; #ifdef M_OS2 extern char TMPDIR[PATH_LEN]; #endif extern char txt_1_resp[]; extern char txt_abort_indexing[]; extern char txt_abort_searching[]; extern char txt_active_file_is_empty[]; extern char txt_added_groups[]; extern char txt_append_to_file[]; extern char txt_lines[]; extern char txt_art_deleted[]; extern char txt_art_cannot_delete[]; extern char txt_art_marked_as_unread[]; extern char txt_art_not_saved[]; extern char txt_art_pager_com[]; extern char txt_art_posted[]; extern char txt_art_rejected[]; extern char txt_art_saved_to[]; extern char txt_art_thread_regex_tag[]; extern char txt_art_unavailable[]; extern char txt_author_search_backwards[]; extern char txt_author_search_forwards[]; extern char txt_bad_active_file[]; extern char txt_bad_article[]; extern char txt_bad_command[]; extern char txt_begin_of_art[]; extern char txt_deleting[]; extern char txt_cannot_find_base_art[]; extern char txt_cannot_get_nntp_server_name[]; extern char txt_cannot_open[]; extern char txt_cannot_open_active_file[]; extern char txt_cannot_open_art[]; extern char txt_cannot_post[]; extern char txt_cannot_change_spooldir[]; extern char txt_catchup_update_info[]; extern char txt_changing_spooldir_to[]; extern char txt_check_article[]; extern char txt_checking[]; extern char txt_checking_active_file[]; extern char txt_checking_for_news[]; extern char txt_checksum_of_file[]; extern char txt_command_failed[]; extern char txt_command_failed_s[]; extern char txt_connecting[]; extern char txt_reconnect_to_news_server[]; extern char txt_connection_to_server_broken[]; extern char txt_continue[]; extern char txt_copyright_notice[]; extern char txt_corrupt_index[]; extern char txt_corrupt_kill_file[]; extern char txt_creating_newsrc[]; extern char txt_crosspost_an_article[]; extern char txt_crosspost_group[]; extern char txt_del_group_in_newsrc[]; extern char txt_delete_bogus_group[]; extern char txt_deleting_art[]; extern char txt_deleting_from_newsrc[]; extern char txt_delete_processed_files[]; extern char txt_end_of_arts[]; extern char txt_end_of_groups[]; extern char txt_end_of_thread[]; extern char txt_env_var_not_found[]; extern char txt_error_header_line_blank[]; extern char txt_error_header_line_colon[]; extern char txt_error_header_line_space[]; extern char txt_error_header_line_empty_newsgroups[]; extern char txt_error_header_line_missing_newsgroups[]; extern char txt_error_header_line_missing_subject[]; extern char txt_warn_art_line_too_long[]; extern char txt_error_header_and_body_not_seperate[]; extern char txt_error_from_in_header_not_allowed[]; extern char txt_art_newsgroups[]; extern char txt_warn_not_valid_newsgroup[]; extern char txt_error_not_valid_newsgroup[]; extern char txt_error_header_line_comma[]; extern char txt_extracting_archive[]; extern char txt_extracting_shar[]; extern char txt_failed_to_connect_to_server[]; extern char txt_feed_pattern[]; extern char txt_group[]; extern char txt_group_deleted[]; extern char txt_group_is_moderated[]; extern char txt_group_select_com[]; extern char txt_select_pattern[]; extern char txt_spooldir_com[]; extern char txt_group_selection[]; extern char txt_group_undeleted[]; extern char txt_help_bug_report[LEN]; extern char txt_help_4[]; extern char txt_help_D[]; extern char txt_help_I[]; extern char txt_help_K[]; extern char txt_help_M[]; extern char txt_help_S[]; extern char txt_help_T[]; extern char txt_help_U[]; extern char txt_help_W[]; extern char txt_help_X[]; extern char txt_help_a[]; extern char txt_help_autosave[]; extern char txt_help_b[]; extern char txt_help_B[]; extern char txt_help_bug[]; extern char txt_help_c[]; extern char txt_help_cC[]; extern char txt_help_ck[]; extern char txt_help_cr[]; extern char txt_help_catchup_groups[]; extern char txt_help_i_coma[]; extern char txt_help_confirm_action[]; extern char txt_help_ctrl_d[]; extern char txt_help_ctrl_f[]; extern char txt_help_ctrl_h[]; extern char txt_help_ctrl_k[]; extern char txt_help_ctrl_l[]; extern char txt_help_d[]; extern char txt_help_dash[]; extern char txt_help_draw_arrow[]; extern char txt_help_equal[]; extern char txt_help_g[]; extern char txt_help_g_4[]; extern char txt_help_g_c[]; extern char txt_help_g_d[]; extern char txt_help_g_cr[]; extern char txt_help_cr[]; extern char txt_help_g_ctrl_k[]; extern char txt_help_g_ctrl_r[]; extern char txt_help_g_l[]; extern char txt_help_g_q[]; extern char txt_help_g_r[]; extern char txt_help_g_search[]; extern char txt_help_g_tab[]; extern char txt_help_g_y[]; extern char txt_help_g_z[]; extern char txt_help_h[]; extern char txt_help_i[]; extern char txt_help_i_4[]; extern char txt_help_i_coma[]; extern char txt_help_i_cr[]; extern char txt_help_i_dot[]; extern char txt_help_i_n[]; extern char txt_help_i_p[]; extern char txt_help_i_search[]; extern char txt_help_i_star[]; extern char txt_help_i_tab[]; extern char txt_help_i_tilda[]; extern char txt_help_j[]; extern char txt_help_kill_from[]; extern char txt_help_kill_group[]; extern char txt_help_kill_how[]; extern char txt_help_kill_subject[]; extern char txt_help_kill_text[]; extern char txt_help_kill_text_type[]; extern char txt_help_l[]; extern char txt_help_m[]; extern char txt_help_maildir[]; extern char txt_help_mark_saved_read[]; extern char txt_help_n[]; extern char txt_help_o[]; extern char txt_help_p_0[]; extern char txt_help_p_4[]; extern char txt_help_p_coma[]; extern char txt_help_p_cr[]; extern char txt_help_p_ctrl_r[]; extern char txt_help_p_d[]; extern char txt_help_p_dot[]; extern char txt_help_p_f[]; extern char txt_help_p_g[]; extern char txt_help_p_k[]; extern char txt_help_p_m[]; extern char txt_help_p_n[]; extern char txt_help_p_p[]; extern char txt_help_p_r[]; extern char txt_help_p_s[]; extern char txt_help_p_search[]; extern char txt_help_p_star[]; extern char txt_help_p_tab[]; extern char txt_help_p_tilda[]; extern char txt_help_p_z[]; extern char txt_help_page_scroll[]; extern char txt_help_pipe[]; extern char txt_help_plus[]; extern char txt_help_pos_first_unread[]; extern char txt_help_post_proc_type[]; extern char txt_help_print_header[]; extern char txt_help_printer[]; extern char txt_help_q[]; extern char txt_help_r[]; extern char txt_help_s[]; extern char txt_help_savedir[]; extern char txt_help_sel_c[]; extern char txt_help_semicolon[]; #ifndef NO_SHELL_ESCAPE extern char txt_help_shell[]; #endif extern char txt_help_show_author[]; extern char txt_help_show_description[]; extern char txt_help_show_only_unread[]; extern char txt_help_sort_type[]; extern char txt_help_start_editor_offset[]; extern char txt_help_t[]; extern char txt_help_t_0[]; extern char txt_help_t_4[]; extern char txt_help_t_K[]; extern char txt_help_t_cr[]; extern char txt_help_t_tab[]; extern char txt_help_thread[]; extern char txt_help_thread_arts[]; extern char txt_help_u[]; extern char txt_help_v[]; extern char txt_help_w[]; extern char txt_help_x[]; extern char txt_help_y[]; extern char txt_hit_any_key[]; extern char txt_cmdline_hit_any_key[]; extern char txt_hit_space_for_more[]; extern char txt_index_page_com[]; extern char txt_ispell_define_not_compiled[]; extern char txt_inverse_off[]; extern char txt_inverse_on[]; extern char txt_kill_from[]; extern char txt_kill_group[]; extern char txt_kill_how[]; extern char txt_kill_menu[]; extern char txt_kill_subject[]; extern char txt_kill_text[]; extern char txt_kill_text_type[]; extern char txt_killing_arts[]; extern char txt_last_resp[]; extern char txt_listing_archive[]; extern char txt_mail_art_to[]; extern char txt_mail_bug_report[]; extern char txt_mail_bug_report_confirm[]; extern char txt_mail_quote[]; extern char txt_mailed[]; extern char txt_mailing_to[]; extern char txt_mark_all_read[]; extern char txt_mark_thread_read[]; extern char txt_mark_group_read[]; extern char txt_mini_select_1[]; extern char txt_mini_select_2[]; extern char txt_mini_select_3[]; extern char txt_mini_spooldir_1[]; extern char txt_mini_group_1[]; extern char txt_mini_group_2[]; extern char txt_mini_group_3[]; extern char txt_mini_thread_1[]; extern char txt_mini_thread_2[]; extern char txt_mini_page_1[]; extern char txt_mini_page_2[]; extern char txt_mini_page_3[]; extern char txt_more[]; extern char txt_more_percent[]; extern char txt_moving[]; extern char txt_news_quote[]; extern char txt_newsgroup[]; extern char txt_newsgroup_position[]; extern char txt_next_resp[]; extern char txt_nntp_authorization_failed[]; extern char txt_nntp_to_fd_cannot_reopen[]; extern char txt_nntp_to_fp_cannot_reopen[]; extern char txt_no[]; extern char txt_no_arts[]; extern char txt_no_arts_posted[]; extern char txt_no_command[]; extern char txt_no_filename[]; extern char txt_no_group[]; extern char txt_no_groups[]; extern char txt_no_groups_to_delete[]; extern char txt_no_groups_to_read[]; extern char txt_no_groups_to_yank_in[]; extern char txt_no_index_file[]; extern char txt_no_last_message[]; extern char txt_no_mail_address[]; extern char txt_no_match[]; extern char txt_no_more_groups[]; extern char txt_no_newsgroups[]; extern char txt_no_next_unread_art[]; extern char txt_no_prev_group[]; extern char txt_no_prev_unread_art[]; extern char txt_no_quick_newsgroups[]; extern char txt_no_quick_subject[]; extern char txt_no_resp[]; extern char txt_no_responses[]; extern char txt_no_resps_in_thread[]; extern char txt_no_search_string[]; extern char txt_no_spooldirs[]; extern char txt_no_subject[]; extern char txt_not_active_newsfeed[]; extern char txt_not_in_active_file[]; extern char txt_opt_autosave[]; extern char txt_opt_catchup_groups[]; extern char txt_opt_confirm_action[]; extern char txt_opt_draw_arrow[]; extern char txt_opt_maildir[]; extern char txt_opt_mark_saved_read[]; extern char txt_opt_page_scroll[]; extern char txt_opt_pos_first_unread[]; extern char txt_opt_post_process[]; extern char txt_opt_print_header[]; extern char txt_opt_printer[]; extern char txt_opt_process_type[]; extern char txt_opt_savedir[]; extern char txt_opt_show_author[]; extern char txt_opt_show_description[]; extern char txt_opt_show_only_unread[]; extern char txt_opt_sort_type[]; extern char txt_opt_start_editor_offset[]; extern char txt_opt_thread_arts[]; extern char txt_option_not_enabled[]; extern char txt_options_menu[]; extern char txt_out_of_memory[]; extern char txt_pipe_to_command[]; extern char txt_piping[]; extern char txt_piping_not_enabled[]; extern char txt_plural[]; extern char txt_post_a_followup[]; extern char txt_post_an_article[]; extern char txt_post_history_menu[]; extern char txt_post_newsgroup[]; extern char txt_post_newsgroups[]; extern char txt_post_process_none[]; extern char txt_post_process_sh[]; extern char txt_post_process_type[]; extern char txt_post_process_uud_ext_zoo[]; extern char txt_post_process_uud_lst_zoo[]; extern char txt_post_process_uud_ext_zip[]; extern char txt_post_process_uud_lst_zip[]; extern char txt_post_process_uudecode[]; extern char txt_post_processing[]; extern char txt_post_processing_failed[]; extern char txt_post_processing_finished[]; extern char txt_post_subject[]; extern char txt_posting[]; extern char txt_processing_xrefs[]; extern char txt_purge[]; extern char txt_printed[]; extern char txt_printing[]; extern char txt_quit[]; extern char txt_quit_edit_delete[]; extern char txt_quit_edit_post[]; extern char txt_quit_edit_xpost[]; extern char txt_quit_edit_save_killfile[]; extern char txt_quit_edit_ispell_send[]; extern char txt_quit_edit_send[]; extern char txt_read_art[]; extern char txt_read_resp[]; extern char txt_reading_all_arts[]; extern char txt_reading_new_arts[]; extern char txt_reading_all_groups[]; extern char txt_reading_new_groups[]; extern char txt_reading_article[]; extern char txt_reading_news_active_file[]; extern char txt_reading_mail_active_file[]; extern char txt_reading_attributes_file[]; extern char txt_reading_mailgroups_file[]; extern char txt_reading_newsgroups_file[]; extern char txt_reconnecting[]; extern char txt_rejected_by_nntpserver[]; extern char txt_rename_error[]; extern char txt_reply_to_author[]; extern char txt_reset_newsrc[]; extern char txt_resizing_window[]; extern char txt_resp_redirect[]; extern char txt_resp_to_poster[]; extern char txt_resp_x_of_n[]; extern char txt_s_at_s[]; extern char txt_save_filename[]; extern char txt_save_pattern[]; extern char txt_saved[]; extern char txt_saved_pattern_to[]; extern char txt_saved_to_mailbox[]; extern char txt_saving[]; extern char txt_screen_init_failed[]; extern char txt_search_backwards[]; extern char txt_search_forwards[]; extern char txt_search_body[]; extern char txt_searching[]; extern char txt_searching_body[]; extern char txt_select_group[]; extern char txt_select_rcfile_option[]; extern char txt_select_spooldir[]; extern char txt_server_name_in_file_env_var[]; extern char txt_shell_escape[]; extern char txt_show_from_addr[]; extern char txt_show_from_both[]; extern char txt_show_from_name[]; extern char txt_show_from_none[]; extern char txt_spooldir_selection[]; extern char txt_sort_by_date_ascend[]; extern char txt_sort_by_date_descend[]; extern char txt_sort_by_from_ascend[]; extern char txt_sort_by_from_descend[]; extern char txt_sort_by_nothing[]; extern char txt_sort_by_subj_ascend[]; extern char txt_sort_by_subj_descend[]; extern char txt_spooldir_server_error_1[]; extern char txt_spooldir_server_error_2[]; extern char txt_spooldirs_not_supported[]; extern char txt_stuff_nntp_cannot_open[]; extern char txt_subscribe_pattern[]; extern char txt_subscribe_to_new_group[]; extern char txt_subscribed_num_groups[]; extern char txt_subscribed_to[]; extern char txt_subscribing[]; extern char txt_subscribing_to[]; extern char txt_suspended_message[]; extern char txt_tagged_art[]; extern char txt_tagged_thread[]; extern char txt_testing_archive[]; extern char txt_there_is_no_news[]; extern char txt_threading_arts[]; extern char txt_thread_com[]; extern char txt_thread_marked_as_unread[]; extern char txt_thread_not_saved[]; extern char txt_thread_page[]; extern char txt_thread_resp_page[]; extern char txt_thread_saved_to[]; extern char txt_thread_saved_to_many[]; extern char txt_thread_x_of_n[]; extern char txt_toggled_rot13[]; extern char txt_type_h_for_help[]; extern char txt_unkilling_arts[]; extern char txt_unsubscribe_pattern[]; extern char txt_unsubscribed_num_groups[]; extern char txt_unsubscribed_to[]; extern char txt_unsubscribing[]; extern char txt_unsubscribing_from[]; extern char txt_untagged_art[]; extern char txt_untagged_thread[]; extern char txt_unthreading_arts[]; extern char txt_use_mime[]; extern char txt_uudecoding[]; extern char txt_writing_attributes_file[]; extern char txt_writing_index_file[]; extern char txt_x_resp[]; extern char txt_yanking_all_groups[]; extern char txt_yanking_sub_groups[]; extern char txt_yes[]; extern char txt_you_have_mail[]; extern int auto_cc; extern int _hp_glitch; extern int unread_art_mark; extern int hot_art_mark; extern int return_art_mark; extern int cCOLS, cLINES; extern int MORE_POS; extern int NOTESLINES; extern int RIGHT_POS; extern int *my_group; extern int old_active_file_size; extern int beginner_level; extern int can_post; extern int catchup; extern int catchup_read_groups; extern int check_for_new_newsgroups; extern int cmd_line; extern int compiled_with_nntp; extern int confirm_action; extern int created_rcdir; extern int cur_active_size; extern int cur_groupnum; extern int debug; extern int default_auto_save; extern int default_batch_save; extern int default_move_group; extern int default_post_proc_type; extern int default_show_author; extern int default_show_only_unread; extern int default_sort_art_type; extern int default_thread_arts; extern int display_reading_prompt; #ifdef SIGTSTP extern int do_sigtstp; #endif extern int draw_arrow_mark; extern int force_screen_redraw; extern int full_page_scroll; extern int groupname_max_length; extern int group_hash[TABLE_SIZE]; extern int group_top; extern int groupname_len; extern int index_file_killed; extern int inn_nntp_server; extern int inverse_okay; extern int use_keypad; extern int killed_articles; extern int kill_level; extern int local_index; extern int mail_news; extern int mark_saved_read; extern int max_active; extern int max_active_size; extern int max_art; extern int max_from; extern int max_subj; extern int max_kill; extern int max_save; extern int max_spooldir; extern int newsrc_active; extern int nntp_codeno; extern int num_active; extern int num_active_size; extern int num_kill; extern int num_of_hot_arts; extern int num_of_killed_arts; extern int num_of_tagged_arts; extern int num_save; extern int num_spooldir; extern int post_article_and_exit; extern int pos_first_unread; extern int print_header; extern int purge_index_files; extern int process_id; extern int read_local_newsgroups_file; extern int read_news_via_nntp; extern int real_gid; extern int real_uid; extern int real_umask; extern int reread_active_file; extern int reread_active_file_secs; extern int save_news; extern int save_to_mmdf_mailbox; extern int show_author; extern int show_description; extern int show_last_line_prev_page; extern int show_only_unread_groups; extern int slow_speed_terminal; extern int space_mode; extern int spooldir_is_active; extern int start_editor_offset; extern int start_line_offset; extern int system_status; extern int tin_gid; extern int tin_uid; extern int tab_after_X_selection; extern int tab_goto_next_unread; extern int top; extern int top_base; extern int unlink_article; extern int update; extern int use_builtin_inews; extern int verbose; extern int update_fork; extern int check_any_unread; extern int start_any_unread; extern int xref_supported; extern int xindex_supported; extern int xover_supported; extern int xuser_supported; extern int xspooldir_supported; extern int xmouse; extern int xcol; extern int xrow; extern long *base; extern struct passwd *myentry; extern struct t_article *arts; extern struct t_group *active; extern struct t_active_size *active_size; extern struct t_kill *killf; extern struct t_posted *posted; extern struct t_save *save; extern struct t_spooldir *spooldirs; extern struct t_screen *screen; #ifdef HAVE_POSIX_JC extern struct sigaction art_act; extern struct sigaction group_act; extern struct sigaction kill_act; extern struct sigaction main_act; extern struct sigaction old_act; extern struct sigaction page_act; extern struct sigaction rcfile_act; extern struct sigaction select_act; extern struct sigaction thread_act; #endif *[SRC.TIN-1_22]FEED.C;1+,.!// 4! -d0@123KPWO"56Tt7i189]VG/HJ/* * Project : tin - a Usenet reader * Module : feed.c * Author : I.Lea * Created : 31-08-91 * Updated : 22-08-93 * Notes : provides same interface to mail,pipe,print and save commands * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" extern char *glob_group; /* Group name */ extern char note_h_date[PATH_LEN]; /* Date: */ extern char note_h_newsgroups[LEN]; /* Newsgroups: */ extern char note_h_subj[LEN]; /* Subject: */ extern FILE *note_fp; /* the body of the current article */ extern int note_end; /* end of article ? */ extern int note_page; /* what page we're on */ extern long note_mark[MAX_PAGES]; /* ftells on beginnings of pages */ char default_mail_address[LEN]; char default_pipe_command[LEN]; char default_save_file[PATH_LEN]; char default_regex_pattern[LEN]; char default_crosspost_group[LEN]; char proc_ch_default; /* set in change_rcfile () */ void feed_articles (function, level, prompt, respnum, group_path) int function; int level; char *prompt; int respnum; char *group_path; { #ifndef INDEX_DAEMON char address[LEN]; char command[LEN]; char filename[PATH_LEN], *p; char group[LEN]; char mailbox[LEN]; char pattern[LEN]; char proc_ch = proc_ch_default; FILE *fp = (FILE *) 0; int ch = 'a', ch_default = 'a'; int b, i, j; int confirm = TRUE; int processed_ok = TRUE; int proceed = FALSE; int is_mailbox = FALSE; int orig_note_end = 0; int orig_note_page = 0; int processed = 0; int redraw_screen = FALSE; int ret1 = FALSE; int ret2 = FALSE; int retcode; #ifdef NO_PIPING if (function == FEED_PIPE) { error_message (txt_piping_not_enabled, ""); clear_message (); return; } #endif set_xclick_off (); if (level == PAGE_LEVEL) { orig_note_end = note_end; orig_note_page = note_page; } b = which_thread (respnum); /* * try and work out what default the user wants */ if (num_of_tagged_arts) { ch_default = 'T'; } else if (num_of_hot_arts && default_auto_save == FALSE) { ch_default = 'h'; } else if (num_of_responses (b)) { ch_default = 't'; } else { ch_default = 'a'; } i = my_group[cur_groupnum]; filename[0] = '\0'; if ((default_auto_save == FALSE || arts[respnum].archive == (char *) 0) || (default_auto_save == TRUE && function != FEED_SAVE) || ch_default == 'T') { do { sprintf (msg, "%s%s%c", prompt, txt_art_thread_regex_tag, ch_default); wait_message (msg); MoveCursor (cLINES, (int) strlen (msg)-1); if ((ch = ReadCh ()) == CR) ch = ch_default; } while (! strchr ("ahpqtT\033", ch)); } else { filename[0] = '\0'; ch = ch_default; if (proc_ch != 'n') { /* * Set up default for *source* / *binaries* group */ if (str_str (glob_group, "sources", 7)) { proc_ch = 's'; } else if (str_str (glob_group, "binaries", 8)) { proc_ch = get_post_proc_type (active[i].attribute.post_proc_type); if (proc_ch < POST_PROC_UUDECODE) { proc_ch = 'u'; } } else { proc_ch = 's'; } } } if (ch == 'q' || ch == ESC) { /* exit */ clear_message (); return; } if (ch == 'p') { sprintf (msg, txt_feed_pattern, default_regex_pattern); if (! prompt_string (msg, pattern)) { clear_message (); return; } if (strlen (pattern)) { my_strncpy (default_regex_pattern, pattern, sizeof (default_regex_pattern)); } else { if (default_regex_pattern[0]) { my_strncpy (pattern, default_regex_pattern, sizeof (default_regex_pattern)); } else { info_message (txt_no_match); return; } } } switch (function) { case FEED_MAIL: sprintf (msg, txt_mail_art_to, cCOLS-(strlen(txt_mail_art_to)+30), default_mail_address); if (! prompt_string (msg, address)) { clear_message (); return; } if (strlen (address)) { strcpy (default_mail_address, address); } else { if (default_mail_address[0]) { strcpy (address, default_mail_address); } else { info_message (txt_no_mail_address); return; } } break; case FEED_PIPE: sprintf (msg, txt_pipe_to_command, cCOLS-(strlen(txt_pipe_to_command)+30), default_pipe_command); if (! prompt_string (msg, command)) { clear_message (); return; } if (strlen (command)) { strcpy (default_pipe_command, command); } else { if (default_pipe_command[0]) { strcpy (command, default_pipe_command); } else { info_message (txt_no_command); return; } } if ((fp = (FILE *) popen (command, "w")) == NULL) { perror_message (txt_command_failed_s, command); return; } wait_message (txt_piping); Raw (FALSE); break; case FEED_PRINT: if (cmd_line_printer[0]) { sprintf (command, "%s %s", cmd_line_printer, REDIRECT_OUTPUT); } else { sprintf (command, "%s %s", active[i].attribute.printer, REDIRECT_OUTPUT); } break; case FEED_SAVE: /* ask user for filename */ free_save_array (); if ((default_auto_save == FALSE || arts[respnum].archive == (char *) 0)) { sprintf (msg, txt_save_filename, default_save_file); if (! prompt_string (msg, filename)) { clear_message (); return; } if (strlen (filename)) { my_strncpy (default_save_file, filename, sizeof (default_save_file)); } else { if (default_save_file[0]) { my_strncpy (filename, default_save_file, sizeof (filename)); } else { info_message (txt_no_filename); return; } } for (p = filename; *p && (*p == ' ' || *p == '\t'); p++) { continue; } if (! *p) { info_message (txt_no_filename); return; } if ((filename[0] == '~' || filename[0] == '+') && strlen (filename) == 1) { info_message (txt_no_filename); return; } is_mailbox = create_path (filename); if (is_mailbox) { if ((int) st rlen (filename) > 1) { my_strncpy (mailbox, filename+1, sizeof (mailbox)); } else { my_strncpy (mailbox, glob_group, sizeof (mailbox)); /* * convert 1st letter to uppercase */ if (mailbox[0] >= 'a' && mailbox[0] <= 'z') { mailbox[0] = mailbox[0] - 32; } } my_strncpy (filename, mailbox, sizeof (filename)); } else { /* ask for post processing type */ do { sprintf (msg, "%s%c", txt_post_process_type, proc_ch_default); wait_message (msg); MoveCursor (cLINES, (int) strlen (msg)-1); if ((proc_ch = ReadCh ()) == CR) proc_ch = proc_ch_default; } while (! strchr ("eElLnqsu\033", proc_ch)); if (proc_ch == 'q' || proc_ch == ESC) { /* exit */ clear_message (); return; } } } wait_message (txt_saving); break; case FEED_XPOST: /* ask user for newsgroups */ sprintf (msg, txt_crosspost_group, default_crosspost_group); if (! prompt_string (msg, group)) { clear_message (); return; } if (strlen (group)) { my_strncpy (default_crosspost_group, group, sizeof (default_crosspost_group)); } else { if (default_crosspost_group[0]) { my_strncpy (group, default_crosspost_group, sizeof (group)); } else { info_message (txt_no_group); return; } } break; } switch (ch) { case 'a': /* article */ if (level == GROUP_LEVEL) { if (! does_article_exist (function, arts[respnum].artnum, group_path)) { break; } } switch (function) { case FEED_MAIL: redraw_screen = mail_to_someone (respnum, address, FALSE, TRUE, &processed_ok); break; case FEED_PIPE: fseek (note_fp, 0L, 0); copy_fp (note_fp, fp, ""); break; case FEED_PRINT: processed_ok = print_file (command, respnum, 1); break; case FEED_SAVE: note_page = art_open (arts[respnum].artnum, group_path); if (note_page != ART_UNAVAILABLE) { add_to_save_list (0, &arts[respnum], is_mailbox, TRUE, filename); processed_ok = save_art_to_file (respnum, 0, FALSE, ""); } break; case FEED_XPOST: redraw_screen = crosspost_article (group, respnum); break; } if (processed_ok) { processed++; } if (mark_saved_read) { if (processed_ok) { arts[respnum].unread = ART_READ; } } if (level == GROUP_LEVEL) { art_close (); } break; case 't': /* thread */ confirm = TRUE; for (i = (int) base[b]; i >= 0; i = arts[i].thread) { if (level == PAGE_LEVEL) { art_close (); } if (! does_article_exist (function, arts[i].artnum, group_path)) { continue; } switch (function) { case FEED_MAIL: processed_ok = TRUE; mail_to_someone (respnum, address, FALSE, confirm, &processed_ok); confirm = FALSE; break; case FEED_PIPE: fseek (note_fp, 0L, 0); copy_fp (note_fp, fp, ""); break; case FEED_PRINT: processed_ok = print_file (command, i, processed+1); break; case FEED_SAVE: add_to_save_list (i, &arts[i], is_mailbox, TRUE, filename); break; case FEED_XPOST: redraw_screen = crosspost_article (group, i); break; } if (processed_ok) { processed++; } if (mark_saved_read) { if (processed_ok) { arts[i].unread = ART_READ; } } art_close (); } if (function == FEED_SAVE) { sort_save_list (); (void) save_thread_to_file (is_mailbox, group_path); } break; case 'T': /* tagged articles */ confirm = TRUE; for (i=1 ; i <= num_of_tagged_arts ; i++) { for (j=0 ; j < top ; j++) { if (arts[j].tagged && arts[j].tagged == i) { if (level == PAGE_LEVEL) { art_close (); } if (! does_article_exist (function, arts[j].artnum, group_path)) { continue; } switch (function) { case FEED_MAIL: processed_ok = TRUE; mail_to_someone (respnum, address, FALSE, confirm, &processed_ok); confirm = FALSE; break; case FEED_PIPE: fseek (note_fp, 0L, 0); copy_fp (note_fp, fp, ""); break; case FEED_PRINT: processed_ok = print_file (command, j, processed+1); break; case FEED_SAVE: add_to_save_list (j, &arts[j], is_mailbox, TRUE, filename); break; case FEED_XPOST: redraw_screen = crosspost_article (group, j); break; } if (processed_ok) { processed++; } if (mark_saved_read) { if (processed_ok) { arts[j].unread = ART_READ; } } art_close (); } } } if (function == FEED_SAVE) { (void) save_regex_arts (is_mailbox, group_path); } untag_all_articles (); break; case 'h': /* hot (auto-selected) articles */ case 'p': /* regex pattern matched articles */ confirm = TRUE; for (i = 0 ; i < top_base ; i++) { for (j = (int) base[i]; j >= 0; j = arts[j].thread) { proceed = FALSE; if (ch == 'p') { if (STR_MATCH(arts[j].subject, pattern)) { proceed = TRUE; } } else if (arts[j].hot) { proceed = TRUE; } if (proceed) { if (level == PAGE_LEVEL) { art_close (); } if (! does_article_exist (function, arts[j].artnum, group_path)) { continue; } switch (function) { case FEED_MAIL: processed_ok = TRUE; mail_to_someone (respnum, address, FALSE, confirm, &processed_ok); /* confirm = FALSE; */ break; case FEED_PIPE: fseek (note_fp, 0L, 0); copy_fp (note_fp, fp, ""); break; case FEED_PRINT: processed_ok = print_file (command, j, processed+1); break; case FEED_SAVE: add_to_save_list (j, &arts[j], is_mailbox, TRUE, filename); break; case FEED_XPOST: redraw_screen = crosspost_article (group, j); break; } if (processed_ok) { processed++; } if (mark_saved_read) { if (process7Q~ TIN-1_22.BCKd[SRC.TIN-1_22]FEED.C;1!Csed_ok) { arts[j].unread = ART_READ; if (ch == 'h') { arts[j].hot = FALSE; num_of_hot_arts--; } } } art_close (); } } } if (function == FEED_SAVE) { (void) save_regex_arts (is_mailbox, group_path); } break; } if (debug == 2) { printf ("REDRAW=[%d] ", redraw_screen); fflush (stdout); } redraw_screen = mail_check (); /* in case of sending to oneself */ if (debug == 2) { printf ("REDRAW=[%d]", redraw_screen); fflush (stdout); sleep (2); } switch (function) { case FEED_PIPE: #if defined(SIGCHLD) && !defined(RS6000) pclose (fp); retcode = system_status; #else retcode = pclose (fp); #endif Raw (TRUE); continue_prompt (); redraw_screen = TRUE; break; case FEED_SAVE: if (proc_ch != 'n' && is_mailbox == FALSE) { ret2 = post_process_files (proc_ch); } free_save_array (); break; } if (level == GROUP_LEVEL) { ret1 = (mark_saved_read ? TRUE : FALSE); } if ((ret1 || ret2) && is_mailbox == FALSE) { redraw_screen = TRUE; } if (level == PAGE_LEVEL) { if (ch != 'a') { note_page = art_open (arts[respnum].artnum, group_path); } else if (force_screen_redraw) { redraw_screen = TRUE; } note_end = orig_note_end; note_page = orig_note_page; fseek (note_fp, note_mark[note_page], 0); if (redraw_screen) { if (note_page == 0) { show_note_page (respnum, glob_group); } else { redraw_page (respnum, glob_group); } } else { if (function  == FEED_PIPE) { clear_message (); } } } else { if (redraw_screen) { show_group_page (); } } if (function == FEED_MAIL) { sprintf (msg, txt_mailed, processed); info_message (msg); } else if (function == FEED_PRINT) { sprintf (msg, txt_printed, processed); info_message (msg); } else if (function == FEED_SAVE) { if (ch == 'a') { sprintf (msg, txt_saved, processed); info_message (msg); } } #endif /* INDEX_DAEMON */ } int print_file (command, respnum, count) char *command; int respnum; int count; { FILE *fp; sprintf (msg, "%s%d", txt_printing, count); wait_message (msg); if ((fp = (FILE *) popen (command, "w")) == NULL) { perror_message (txt_command_failed_s, command); return FALSE; } if (print_header) { fseek(note_fp, 0L, 0); } else { fprintf (fp, "Newsgroups: %s\n", note_h_newsgroups); if (arts[respnum].from == arts[respnum].name) { fprintf (fp, "From: %s\n", arts[respnum].from); } else { fprintf (fp, "From: %s (%s)\n", arts[respnum].from, arts[respnum].name); } fprintf (fp, "Subject: %s\n", note_h_subj); fprintf (fp, "Date: %s\n\n", note_h_date); fseek (note_fp, note_mark[0], 0); } copy_fp (note_fp, fp, ""); pclose (fp); return (TRUE); /* a hack that will check if file was really checked later */ } int get_post_proc_type (proc_type) int proc_type; { int type; switch (proc_type) { case POST_PROC_SHAR: type = 's'; break; case POST_PROC_UUDECODE: type = 'u'; break; case POST_PROC_UUD_LST_ZOO: type = 'l'; break; case POST_PROC_UUD_EXT_ZOO: type = 'e'; break; case POST_PROC_UUD_LST_ZIP: type = 'L'; break; case POST_PROC_UUD_EXT_ZIP: type = 'E'; break; case POST_PROC_NONE: default: type = 'n'; break; } return type; } /* * Opening an article here & also later in the save * routine is a real performance (bandwidth) killer * as both times the art will be transfered (Ouch!) * * So if function is to save an article only stat * it the first time which saves a lot and almost * gets us the elusive free lunch! */ int does_article_exist (function, artnum, path) int function; long artnum; char *path; { int retcode = FALSE; if (function == FEED_SAVE) { if (stat_article (artnum, path)) { retcode = TRUE; } } else { note_page = art_open (artnum, path); if (note_page != ART_UNAVAILABLE) { retcode = TRUE; } } return retcode; } *[SRC.TIN-1_22]FTP.;1+,q.// 4-d0@123KPWO56%t7`rꑗ89 G/HJ This document informs you how to get tin source & binaries in one of 3 ways. 1) Via FTP. 2) Source code via mail. 3) Pre-compiled Binaries. -------------------------------------------------------------------------------- 1) FTP sites that carry the latest version of tin are: 1.1) Unix Reference site: Germany [192.76.144.129] ftp.Germany.EU.net:pub/news/tin Mirror sites: UK [146.169.2.1] src.doc.ic.ac.uk:computing/usenet/software/readers/tin USA [131.210.1.4] ftp.uwp.edu:pub/tin Oz [130.194.9.1] yoyo.cc.monash.edu.au:pub/tin NOTE: There will be a small time lag before each of these sites makes the newest version available as I usually post first to alt.sources 1.2) AmigaDOS Reference sites: Switzland [128.178.151.32] litaamiga.epfl.ch:/pub/amiga/aminet/local/uucp 1.3) OS/2 Reference sites: USA [128.123.35.151] ftp-os2.nmsu.edu:/os2/2_x/network/tin120.zip ?? [192.153.46.2] ftp-os2.cdrom.com:/os2/2_x/network/tin120.zip NOTE: Requires TCP/IP version 1.2.1. This release may trail the Unix release. -------------------------------------------------------------------------------- 2) For people that do not have FTP access I am willing to send a copy of the latest released version (or a beta version of the next patchlevel if so desired). 2.1) I can handle the following media formats. Specify: 5.25 & 3.5 inch floppy disks. 0.25 (1/4) inch cartridge tapes (you supply it). 2.2) I can handle the following software formats. Specify: Unix *.tar *.tar.Z *.tar.z *.zoo *.zip Msdos *.zoo *.zip formats 2.3) I make a small handling charge for this service as listed below: Germany 15 Deustche Marks England 5 Pounds Sterling USA 10 Dollars If you find the above points 2.1 - 2.3 OK send money/cheque with a self-addressed envelope stating which media & software format you want to the following address: Iain J. Lea BrueckenStr. 12 8500 Nuernberg 90 Germany. Phone. +49-911-331963 (home) +49-911-3089-407 (work) +49-911-3089-290 (FAX) Email. iain.lea@erlm.siemens.de -------------------------------------------------------------------------------- 3) For people that are having problems compiling tin I am willing to send a copy of a pre-compiled binary for the following machines: i386/486 + Linux i386/486 + SCO Xenix 2.3.2 i386/486 + SCO Unix 3.2.2 i386/486 + ISC Unix 3.2 v3.0 SunSparc + SunOS 4.1 DecMIPS + Ultrix 4.2 Siemens MX330 + Sinix v5.23 Siemens MX350i + Sinix v5.4 NOTE: Specify how you want tin to be compiled to access news: 1) local spool directory (specify path for newsspool & newslib) 2) local spool directory & via NNTP 3) via NNTP only 4) local spool directory & via news archived on CD-ROM For conditions & shipping read points 2.1 - 2.3 above. Enjoy Iain. *[SRC.TIN-1_22]GETLINE.C;1+,.// 4-d0@123KPWO56`Ct7Aj189]VG/HJ./* * Project : tin - a Usenet reader * Module : getline.c * Author : Chris Thewalt & Iain Lea * Created : 09-11-91 * Updated : 11-07-93 * Notes : emacs style line editing input package. * Copyright : (c) Copyright 1991-93 by Chris Thewalt & Iain Lea * Permission to use, copy, modify, and distribute this * software for any purpose and without fee is hereby * granted, provided that the above copyright notices * appear in all copies and that both the copyright * notice and this permission notice appear in supporting * documentation. This software is provided "as is" without * express or implied warranty. */ #include "tin.h" #define BUF_SIZE 1024 #define SCROLL 30 #define TABSIZE 4 #ifndef HIST_SIZE #define HIST_SIZE 100 #endif #define CTRL_A '\001' #define CTRL_B '\002' #define CTRL_D '\004' #define CTRL_E '\005' #define CTRL_F '\006' #define CTRL_H '\010' #define CTRL_K '\013' #define CTRL_L '\014' #define CTRL_R '\022' #define CTRL_N '\016' #define CTRL_P '\020' #define TAB '\t' #define DEL '\177' char *hist_buf[HIST_SIZE]; int hist_pos, hist_last; static char gl_buf[BUF_SIZE]; /* input buffer */ static char *gl_prompt; /* to save the prompt string */ static int gl_init_done = 0; /* -1 is terminal, 1 is batch */ static int gl_width = 0; /* net size available for input */ static int gl_pos, gl_cnt = 0; /* position and size of input */ #if __STDC__ static int gl_tab (char *, int, int *); static void gl_redraw (void); static void gl_addchar (int); static void gl_newline (void); static void gl_fixup (int, int); static void gl_del (int); static void gl_kill (void); static void hist_add (void); static void hist_init (void); static void hist_next (void); static void hist_prev (void); int (*gl_in_hook)(char *) = 0; int (*gl_out_hook)(char *) = 0; int (*gl_tab_hook)(char *, int, int *) = gl_tab; #else static int gl_tab (); static void gl_redraw (); static void gl_addchar (); static void gl_newline (); static void gl_fixup (); static void gl_del (); static void gl_kill (); static void hist_add (); static void hist_init (); static void hist_next (); static void hist_prev (); int (*gl_in_hook)() = 0; int (*gl_out_hook)() = 0; int (*gl_tab_hook)() = gl_tab; #endif #if __STDC__ char * getline (char *prompt, int number_only, char *str) #else char * getline (prompt, number_only, str) char *prompt; int number_only; char *str; #endif { int c, i, loc, tmp; set_xclick_off (); if (! gl_init_done) { gl_init_done = 1; hist_init (); } if (prompt == (char *) 0) { prompt = ""; } gl_buf[0] = 0; /* used as end of input indicator */ gl_fixup (-1, 0); /* this resets gl_fixup */ gl_width = cCOLS - strlen (prompt); gl_prompt = prompt; gl_pos = gl_cnt = 0; fputs (prompt, stdout); fflush (stdout); if (gl_in_hook) { loc = gl_in_hook (gl_buf); if (loc >= 0) gl_fixup (0, BUF_SIZE); } if (str != (char *) 0) { for (i=0 ; str[i] ; i++) gl_addchar (str[i]); } while ((c = ReadCh ()) != EOF) { c &= 0xff; if (isprint (c)) { if (number_only) { if (isdigit (c) && gl_cnt < 6) { /* num < 100000 */ gl_addchar (c); } else { ring_bell (); } } else { gl_addchar (c); } } else { switch (c) { case ESC: /* abort */ return (char *) 0; case '\n': /* newline */ case '\r': gl_newline (); return gl_buf; case CTRL_A: gl_fixup (-1, 0); break; case CTRL_B: gl_fixup (-1, gl_pos-1); break; case CTRL_D: if (gl_cnt == 0) { gl_buf[0] = 0; fputc ('\n', stdout); return gl_buf; } else { gl_del (0); } break; case CTRL_E: gl_fixup (-1, gl_cnt); break; case CTRL_F: gl_fixup (-1, gl_pos+1); break; case CTRL_H: case DEL: gl_del (-1); break; case TAB: if (gl_tab_hook) { tmp = gl_pos; loc = gl_tab_hook (gl_buf, strlen (gl_prompt), &tmp); if (loc >= 0 || tmp != gl_pos) gl_fixup (loc, tmp); } break; case CTRL_K: gl_kill (); break; case CTRL_L: case CTRL_R: gl_redraw (); break; case CTRL_N: hist_next (); break; case CTRL_P: hist_prev (); break; default: ring_bell (); break; } } } return gl_buf; } /* * adds the character c to the input buffer at current location if * the character is in the allowed template of characters */ #if __STDC__ static void gl_addchar (int c) #else static void gl_addchar (c) int c; #endif { int i; if (gl_cnt >= BUF_SIZE - 1) { error_message ("getline: input buffer overflow", ""); exit (1); } for (i=gl_cnt; i >= gl_pos; i--) { gl_buf[i+1] = gl_buf[i]; } gl_buf[gl_pos] = c; gl_fixup (gl_pos, gl_pos+1); } /* * Cleans up entire line before returning to caller. A \n is appended. * If line longer than screen, we redraw starting at beginning */ static void gl_newline () { int change = gl_cnt; int len = gl_cnt; int loc = gl_width - 5; /* shifts line back to start position */ if (gl_cnt >= BUF_SIZE - 1) { error_message ("getline: input buffer overflow", ""); exit (1); } hist_add (); /* only adds if nonblank */ if (gl_out_hook) { change = gl_out_hook (gl_buf); len = strlen (gl_buf); } if (loc > len) loc = len; gl_fixup (change, loc); /* must do this before appending \n */ gl_buf[len] = '\0'; } /* * Delete a character. The loc variable can be: * -1 : delete character to left of cursor * 0 : delete character under cursor */ #if __STDC__ static void gl_del (int loc) #else static void gl_del (loc) int loc; #endif { int i; if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) { for (i=gl_pos+loc; i < gl_cnt; i++) gl_buf[i] = gl_buf[i+1]; gl_fixup (gl_pos+loc, gl_pos+loc); } else { ring_bell (); } } /* * delete from current position to the end of line */ static void gl_kill () { if (gl_pos < gl_cnt) { gl_buf[gl_pos] = '\0'; gl_fixup (gl_pos, gl_pos); } else { ring_bell (); } } /* * emit a newline, reset and redraw prompt and current input line */ static void gl_redraw () { if (gl_init_done == -1) { fputc ('\n', stdout); fputs (gl_prompt, stdout); gl_pos = 0; gl_fixup (0, BUF_SIZE); } } /* * This function is used both for redrawing when input changes or for * moving within the input line. The parameters are: * change : the index of the start of changes in the input buffer, * with -1 indicating no changes. * cursor : the desired location of the cursor after the call. * A value of BUF_SIZE can be used to indicate the cursor * should move just past the end of the input line. */ #if __STDC__ static void gl_fixup (int change, int cursor) #else static void gl_fixup (change, cursor) int change; int cursor; #endif { static int gl_shift; /* index of first on screen character */ static int off_right; /* true if more text right of screen */ static int off_left; /* true if more text left of screen */ int left = 0, right = -1; /* bounds for redraw */ int pad; /* how much to erase at end of line */ int backup; /* how far to backup before fixing */ int new_shift; /* value of shift based on cursor */ int extra; /* adjusts when shift (scroll) happens */ int i; if (change == -1 && cursor == 0 && gl_buf[0] == 0) { /* reset */ gl_shift = off_right = off_left = 0; return; } pad = (off_right) ? gl_width - 1 : gl_cnt - gl_shift; /* old length */ backup = gl_pos - gl_shift; if (change >= 0) { gl_cnt = strlen (gl_buf); if (change > gl_cnt) change = gl_cnt; } if (cursor > gl_cnt) { if (cursor != BUF_SIZE) /* BUF_SIZE means end of line */ ring_bell (); cursor = gl_cnt; } if (cursor < 0) { ring_bell (); cursor = 0; } if (off_right || off_left && (cursor < gl_shift + gl_width - SCROLL / 2)) extra = 2; /* shift the scrolling boundary */ else extra = 0; new_shift = cursor + extra + SCROLL - gl_width; if (new_shift > 0) { new_shift /= SCROLL; new_shift *= SCROLL; } else new_shift = 0; if (new_shift != gl_shift) { /* scroll occurs */ gl_shift = new_shift; off_left = (gl_shift) ? 1 : 0; off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0; left = gl_shift; right = (off_right) ? gl_shift + gl_width - 2 : gl_cnt; } else if (change >= 0) { /* no scroll, but text changed */ if (change < gl_shift + off_left) { left = gl_shift; } else { left = change; backup = gl_pos - change; } off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0; right = (off_right) ? gl_shift + gl_width - 2 : gl_cnt; } pad -= (off_right) ? gl_width - 1 : gl_cnt - gl_shift; pad = (pad < 0)? 0 : pad; if (left <= right) { /* clean up screen */ for (i=0; i < backup; i++) fputc ('\b', stdout); if (left == gl_shift && off_left) { fputc ('$', stdout); left++; } for (i=left; i < right; i++) fputc (gl_buf[i], stdout); if (off_right) { fputc ('$', stdout); gl_pos = right + 1; } else { for (i=0; i < pad; i++) /* erase remains of prev line */ fputc (' ', stdout); gl_pos = right + pad; } } i = gl_pos - cursor; /* move to final cursor location */ if (i > 0) { while (i--) fputc ('\b', stdout); } else { for (i=gl_pos; i < cursor; i++) fputc (gl_buf[i], stdout); } fflush (stdout); gl_pos = cursor; } /* * default tab handler, acts like tabstops every TABSIZE cols */ #if __STDC__ static int gl_tab (char *buf, int offset, int *loc) #else static int gl_tab (buf, offset, loc) char *buf; int offset; int *loc; #endif { int i, count, len; len = strlen (buf); count = TABSIZE - (offset + *loc) % TABSIZE; for (i=len; i >= *loc; i--) buf[i+count] = buf[i]; for (i=0; i < count; i++) buf[*loc+i] = ' '; i = *loc; *loc = i + count; return i; } /* * History functions */ static void hist_init () { int i; for (i=0; i < HIST_SIZE; i++) hist_buf[i] = (char *) 0; } static void hist_add () { char *p = gl_buf; while (*p == ' ' || *p == '\t') /* only save nonblank line */ p++; if (*p) { hist_buf[hist_last] = str_dup (gl_buf); hist_last = (hist_last + 1) % HIST_SIZE; if (hist_buf[hist_last]) { /* erase next location */ free(hist_buf[hist_last]); hist_buf[hist_last] = (char *) 0; } } hist_pos = hist_last; } /* * loads previous hist entry into input buffer, sticks on first */ static void hist_prev () { int next; next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE; if (next != hist_last) { if (hist_buf[next]) { hist_pos = next; strcpy (gl_buf, hist_buf[hist_pos]); } else { ring_bell (); } } else { ring_bell (); } if (gl_in_hook) gl_in_hook (gl_buf); gl_fixup (0, BUF_SIZE); } /* * loads next hist entry into input buffer, clears on last */ static void hist_next () { if (hist_pos != hist_last) { hist_pos = (hist_pos + 1) % HIST_SIZE; if (hist_buf[hist_pos]) { strcpy (gl_buf, hist_buf[hist_pos]); } else { gl_buf[0] = 0; } } else { ring_bell (); } if (gl_in_hook) gl_in_hook (gl_buf); gl_fixup (0, BUF_SIZE); } *[SRC.TIN-1_22]GROUP.C;3+,.H// 4HF-d0@123KPWOI56~ 7 K 89]VG/HJ/* * Project : tin - a Usenet reader * Module : group.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 24-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #define MARK_OFFSET 8 extern char cvers[LEN]; extern char proc_ch_type; /* feed.c */ extern int last_resp; /* page.c */ extern int this_resp; /* page.c */ extern int note_page; /* page.c */ char *glob_group; int index_point; int first_subj_on_screen; int last_subj_on_screen; static int len_from; static int len_subj; static char *spaces = "XXXX"; #if __STDC__ static int bld_sline (int i); static int draw_sline (int i, int full); #else static int bld_sline (); static int draw_sline (); #endif #ifndef ART_ADJUST /* what we do here is bizarre */ #define ART_ADJUST(n) (active[my_group[cur_groupnum]].attribute.show_only_unread \ ? ((n) > 1 ? (n) : 0) \ : ((n) > 0 ? (n) - 1 : 0)) #endif #define INDEX2SNUM(i) ((i) % NOTESLINES) #define SNUM2LNUM(i) (INDEX_TOP + (i)) #define INDEX2LNUM(i) (SNUM2LNUM(INDEX2SNUM(i))) void decr_tagged (tag) int tag; { int i, j; for (i = 0; i < top_base; ++i) { for (j = (int) base[i] ; j != -1 ; j = arts[j].thread) if (arts[j].tagged > t(ag) --arts[j].tagged; } } void group_page (group) char *group; { #ifndef INDEX_DAEMON char group_path[LEN]; char buf[128]; char pat[128]; int ch; int dummy = 0; int flag, i; int n = -1; int kill_state; int old_top = 0; int old_hot_arts = 0; int posted; int sav_groupnum; int scroll_lines; long old_artnum = 0L; int xflag = 0; struct t_art_stat sbuf; int old_group_top; /* * Set the group attributes */ active[my_group[cur_groupnum]].attribute.read_during_session = TRUE; show_author = active[my_group[cur_groupnum]].attribute.show_author; proc_ch_default = get_post_proc_type (active[my_group[cur_groupnum]].attribute.post_proc_type); glob_group = group; sav_groupnum = cur_groupnum; num_of_tagged_arts = 0; make_group_path (group, group_path); last_resp = -1; this_resp = -1; /* * update index file. quit group level if user aborts indexing */ if (! index_group (group, group_path)) { return; } if (space_mode) { for (i = 0; i < top_base; i++) { if (new_responses (i)) { break; } } if (i < top_base) { index_point = i; } else { index_point = top_base - 1; } } else { index_point = top_base - 1; } if (index_point < 0) { index_point = 0; } set_subj_from_size (cCOLS); clear_note_area (); show_group_page (); while (TRUE) { set_xclick_on (); ch = ReadCh (); if (ch > '0' && ch <= '9') { /* 0 goes to basenote */ if (prompt_subject_num (ch, group)) { goto group_tab_pressed; } continue; } switch (ch) { case ESC: /* common arrow keys */ #ifdef HAVE_KEY_PREFIX case KEY_PREFIX: #endif switch (get_arrow_key ()) { case KEYMAP_UP: goto group_up; case KEYMAP_DOWN: goto group_down; case KEYMAP_LEFT: goto group_done; case KEYMAP_RIGHT: goto group_read_basenote; case KEYMAP_PAGE_UP: goto group_page_up; case KEYMAP_PAGE_DOWN: goto group_page_down; case KEYMAP_HOME: if (! top_base) { break; } if (index_point != 0) { if (0 < first_subj_on_screen) { #ifndef USE_CLEARSCREEN erase_subject_arrow (); #endif index_point = 0; show_group_page (); } else { erase_subject_arrow (); index_point = 0; draw_subject_arrow (); } } break; case KEYMAP_END: goto end_of_list; case KEYMAP_MOUSE: if (xrow < INDEX2LNUM(first_subj_on_screen)) { goto group_page_up; } if (xrow > INDEX2LNUM(last_subj_on_screen-1)) { goto group_page_down; } erase_subject_arrow (); index_point = xrow-INDEX2LNUM(first_subj_on_screen)+first_subj_on_screen; draw_subject_arrow (); if (xmouse == 1) { goto group_tab_pressed; } if (xmouse == 2) { goto group_read_basenote; } } break; #ifndef NO_SHELL_ESCAPE case '!': shell_escape (); show_group_page (); break; #endif case '$': /* show last page of articles */ end_of_list: if (! top_base) { break; } if (index_point != top_base - 1) { if (top_base - 1 > last_subj_on_screen) { #ifndef USE_CLEARSCREEN erase_subject_arrow (); #endif index_point = top_base - 1; show_group_page (); } else { erase_subject_arrow (); index_point = top_base - 1; draw_subject_arrow (); } } break; case '-': /* go to last viewed article */ if (this_resp < 0) { info_message (txt_no_last_message); break; } index_point = show_page (this_resp, &dummy, group, group_path); if (index_point == GRP_NOREDRAW) { index_point = which_thread (this_resp); clear_message (); } else { if (index_point < 0) { space_mode = (index_point == GRP_CONTINUE); goto group_done; } clear_note_area (); show_group_page (); } break; case '|': /* pipe article/thread/tagged arts to command */ if (index_point >= 0) { feed_articles (FEED_PIPE, GROUP_LEVEL, "Pipe", (int) base[index_point], group_path); } break; case '/': /* forward/backward search */ case '?': i = (ch == '/'); search_subject (i, group); break; case 'B': /* search article body */ if (index_point < 0) { info_message (txt_no_arts); break; } n = search_body (group_path, (int) base[index_point]); if (n != -1) { index_point = show_page (n, &dummy, group, group_path); if (index_point < 0) { space_mode = FALSE; goto group_done; } show_group_page (); } break; case '\r': case '\n': /* read current basenote */ group_read_basenote: if (index_point < 0) { info_message(txt_no_arts); break; } i = (int) base[index_point]; index_point = show_page (i, &dummy, group, group_path); if (index_point == GRP_NOREDRAW) { index_point = which_thread (i); clear_message (); } else { if (index_point < 0) { space_mode = (index_point == GRP_CONTINUE); goto group_done; } clear_note_area (); show_group_page (); } break; case '\t': /* goto next unread article/group */ group_tab_pressed: space_mode = TRUE; if (index_point < 0) { n = -1; } else { n = next_unread ((int) base[index_point]); } if (index_point < 0 || n < 0) { for (i = cur_groupnum+1 ; i < group_top ; i++) { if (active[my_group[i]].unread > 0) { break; } } if (i >= group_top) { goto group_done; } cur_groupnum = i; index_point = GRP_GOTONEXT; goto group_done; } index_point = show_page (n, &dummy, group, group_path); if (index_point == GRP_NOREDRAW) { index_point = which_thread (n); goto group_tab_pressed; /* repeat TAB */ } else { if (index_point < 0) { goto group_done; } clear_note_area (); show_group_page (); } break; case ' ': /* page down */ case ctrl('D'): case ctrl('F'): /* vi style */ group_page_down: if (! top_base) { break; } if (index_point == top_base - 1) { if (0 < first_subj_on_screen) { # ifndef USE_CLEARSCREEN erase_subject_arrow (); # endif index_point = 0; show_group_page (); } else { erase_subject_arrow (); index_point = 0; draw_subject_arrow (); } break; } erase_subject_arrow (); scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2); index_point = ((index_point + scroll_lines) / scroll_lines) * scroll_lines; if (index_point >= top_base) { index_point = (top_base / scroll_lines) * scroll_lines; if (index_point < top_base - 1) { index_point = top_base - 1; } } if (index_point < first_subj_on_screen || index_point >= last_subj_on_screen) show_group_page (); else draw_subject_arrow (); break; case ctrl('K'): /* kill article */ if (index_point < 0) { info_message (txt_no_arts); break; } old_top = top; n = (int) base[index_point]; old_artnum = arts[n].artnum; if (kill_art_menu (group, (int) base[index_point])) { kill_any_articles (my_group[cur_groupnum]); make_threads (FALSE); find_base (my_group[cur_groupnum]); index_point = find_new_pos (old_top, old_artnum, index_point); } show_group_page (); break; case ctrl('L'): /* redraw screen */ case ctrl('R'): case ctrl('W'): #ifndef USE_CLEARSCREEN ClearScreen (); #endif set_xclick_off (); show_group_page (); break; case ctrl('N'): case 'j': /* line down */ group_down: if (! top_base) { break; } if (index_point + 1 >= top_base) { if (_hp_glitch) { erase_subject_arrow (); } if (0 < first_subj_on_screen) { index_point = 0; show_group_page (); } else { erase_subject_arrow (); index_point = 0; draw_subject_arrow (); } break; } if (index_point + 1 >= last_subj_on_screen) { #ifndef USE_CLEARSCREEN erase_subject_arrow(); #endif index_point++; show_group_page (); } else { erase_subject_arrow (); index_point++; draw_subject_arrow (); } break; case ctrl('P'): case 'k': /* line up */ group_up: if (! top_base) { break; } if (index_point == 0) { if (_hp_glitch) { erase_subject_arrow (); } if (top_base > last_subj_on_screen) { index_point = top_base - 1; show_group_page (); } else { erase_subject_arrow (); index_point = top_base - 1; draw_subject_arrow (); } break; } if (_hp_glitch) { erase_subject_arrow (); } if (index_point <= first_subj_on_screen) { index_point--; show_group_page (); } else { erase_subject_arrow (); index_point--; draw_subject_arrow (); } break; case 'b': /* page up */ case ctrl('U'): case ctrl('B'): /* vi style */ group_page_up: if (! top_base) { break; } if (index_point == 0) { if (_hp_glitch) { erase_subject_arrow (); } if (top_base > last_subj_on_screen) { index_point = top_base - 1; show_group_page (); } else { erase_subject_arrow (); index_point = top_base - 1; draw_subject_arrow (); } break; } #ifndef USE_CLEARSCREEN clear_message (); #endif erase_subject_arrow (); scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2); if ((n = index_point % scroll_lines) > 0) { index_point = index_point - n; } else { index_point = ((index_point - scroll_lines) / scroll_lines) * scroll_lines; } if (index_point < 0) { index_point = 0; } if (index_point < first_subj_on_screen || index_point >= last_subj_on_screen) show_group_page (); else draw_subject_arrow (); break; case 'a': /* author search forward */ case 'A': /* author search backward */ if (index_point < 0) { info_message (txt_no_arts); break; } i = (ch == 'a'); n = search_author (my_group[cur_groupnum], (int) base[index_point] 5~ TIN-1_22.BCKd[SRC.TIN-1_22]GROUP.C;3H^, i); if (n < 0) break; index_point = show_page (n, &dummy, group, group_path); if (index_point != GRP_NOREDRAW) { if (index_point < 0) { space_mode = FALSE; goto group_done; } clear_note_area (); show_group_page (); } break; case 'c': /* catchup - mark all articles as read */ case 'C': /* catchup - and goto next unread group */ if (! active[my_group[cur_groupnum]].unread || ! confirm_action || prompt_yn (cLINES, txt_mark_all_read, 'y')) { for (n = 0; n < top; n++) { if (arts[n].unread != ART_READ) { mark_all_xref_read (&arts[n], group_path); } arts[n].unread = ART_READ; } if (ch == 'c') { if (cur_groupnum + 1 < group_top) { cur_groupnum++; } goto group_done; } else { goto group_tab_pressed; } } break; case 'd': /* toggle display of subject & subj/author */ toggle_subject_from (); show_group_page (); break; case 'g': /* choose a new group by name */ old_group_top = group_top; n = choose_new_group (); if (n >= 0 && n != cur_groupnum) { /* * if we added a group, set the length as appropriate * for the group selection display */ if (old_group_top != group_top) set_groupname_len(FALSE); cur_groupnum = n; index_point = GRP_GOTONEXT; goto group_done; } break; case 'h': /* help */ show_info_page (HELP_INFO, help_group, txt_index_page_com); show_group_page (); break; case 'H': /* toggle mini help menu */ toggle_mini_help (GROUP_LEVEL); show_group_page(); break; case 'I': /* toggle inverse video */ toggle_inverse_video (); show_group_page (); break; case 'K': /* mark rest of thread as read */ if (index_point < 0) { info_message (txt_no_next_unread_art); break; } old_hot_arts = num_of_hot_arts; for (i = (int) base[index_point]; i >= 0; i = arts[i].thread) { if (arts[i].unread != ART_READ) { arts[i].unread = ART_READ; mark_all_xref_read (&arts[i], group_path); if (arts[i].hot) { if (num_of_hot_arts) { num_of_hot_arts--; } } } } if (num_of_hot_arts != old_hot_arts) { show_group_title (TRUE); } bld_sline (index_point); draw_sline (index_point, FALSE); n = next_unread (next_response ((int) base[index_point])); if (n < 0) { draw_subject_arrow (); info_message (txt_no_next_unread_art); break; } if ((n = which_thread (n)) < 0) { error_message ("Internal error: K which_thread < 0", ""); break; } if (n < first_subj_on_screen || n >= last_subj_on_screen) { if (_hp_glitch) erase_subject_arrow (); index_point = n; show_group_page (); } else { erase_subject_arrow (); index_point = n; draw_subject_arrow (); } break; case 'l': /* list articles within current thread */ if (index_point < 0) { info_message (txt_no_arts); break; } space_mode = TRUE; n = show_thread ((int) base[index_point], group, group_path); if (n == GRP_QUIT) { index_point = n; space_mode = FALSE; goto group_done; } else { if (index_point < 0) { space_mode = FALSE; goto group_done; } clear_note_area (); show_group_page (); } break; case 'm': /* mail article to somebody */ if (index_point >= 0) { feed_articles (FEED_MAIL, GROUP_LEVEL, "Mail", (int) base[index_point], group_path); } break; case 'M': /* options menu */ if (top_base > 0) { old_top = top; n = (int) base[index_point]; old_artnum = arts[n].artnum; } n = default_sort_art_type; kill_state = change_rcfile (group, TRUE); if (kill_state == NO_KILLING && n != default_sort_art_type) { make_threads (TRUE); find_base (my_group[cur_groupnum]); } set_subj_from_size (cCOLS); index_point = find_new_pos (old_top, old_artnum, index_point); show_group_page (); break; case 'n': /* goto next group */ clear_message (); if (cur_groupnum + 1 >= group_top) info_message (txt_no_more_groups); else { cur_groupnum++; index_point = GRP_GOTONEXT; space_mode = pos_first_unread; goto group_done; } break; case 'N': /* goto next unread article */ if (index_point < 0) { info_message(txt_no_next_unread_art); break; } n = next_unread ((int) base[index_point]); if (n == -1) { info_message (txt_no_next_unread_art); } else { index_point = show_page (n, &dummy, group, group_path); if (index_point != GRP_NOREDRAW) { if (index_point < 0) { space_mode = pos_first_unread; goto group_done; } clear_note_area (); show_group_page (); } } break; case 'o': /* output art/thread/tagged arts to printer */ if (index_point >= 0) { feed_articles (FEED_PRINT, GROUP_LEVEL, "Print", (int) base[index_point], group_path); } break; case 'p': /* previous group */ clear_message(); if (cur_groupnum <= 0) info_message(txt_no_prev_group); else { cur_groupnum--; index_point = GRP_GOTONEXT; space_mode = pos_first_unread; goto group_done; } break; case 'P': /* go to previous unread article */ if (index_point < 0) { info_message(txt_no_prev_unread_art); break; } n = prev_response ((int) base[index_point]); n = prev_unread (n); if (n == -1) { info_message(txt_no_prev_unread_art); } else { index_point = show_page (n, &dummy, group, group_path); if (index_point != GRP_NOREDRAW) { if (index_point < 0) { space_mode = pos_first_unread; goto group_done; } clear_note_area (); show_group_page (); } } break; case 'q': /* return to group selection page */ case 'i': goto group_done; case 'Q': /* quit */ index_point = GRP_QUIT; space_mode = FALSE; goto group_done; case 'r': /* * If in show_only_unread mode or there are * unread articles we know this thread will * exist after toggle. Otherwise we find the * next closest */ if (active[my_group[cur_groupnum]].attribute.show_only_unread) { wait_message (txt_reading_all_arts); } else { wait_message (txt_reading_new_arts); } i = -1; if (index_point >= 0) { if (active[my_group[cur_groupnum]].attribute.show_only_unread || new_responses (index_point)) { i = base[index_point]; } else if ((n = prev_unread ((int)base[index_point])) >= 0) { i = n; } else if ((n = next_unread ((int)base[index_point])) >= 0) { i = n; } } active[my_group[cur_groupnum]].attribute.show_only_unread = !active[my_group[cur_groupnum]].attribute.show_only_unread; auto_select_articles (my_group[cur_groupnum]); find_base (my_group[cur_groupnum]); if (i >= 0 && (n = which_thread (i)) >= 0) index_point = n; else if (top_base > 0) index_point = top_base - 1; show_group_page (); break; case 'R': /* bug/gripe/comment mailed to author */ mail_bug_report (); #ifndef USE_CLEARSCREEN ClearScreen (); #endif show_group_page (); break; case 's': /* save regex pattern to file/s */ if (index_point >= 0) { feed_articles (FEED_SAVE, GROUP_LEVEL, "Save", (int) base[index_point], group_path); } break; case 't': /* tag/untag art for mailing/piping/printing/saving */ if (index_point >= 0) { int tagged = TRUE; n = (int) base[index_point]; if (active[my_group[cur_groupnum]].attribute.thread_arts) { int i; for (i = n; i != -1 && tagged; i = arts[i].thread) { if (! arts[i].tagged) tagged = FALSE; } if (tagged) { /* * Here we repeat the tagged test in both blocks * to leave the choice of tagged/untagged * determination politic in the previous lines. */ info_message (txt_untagged_thread); for (i = n; i != -1; i = arts[i].thread) { if (arts[i].tagged) { tagged = TRUE; decr_tagged (arts[i].tagged); arts[i].tagged = 0; --num_of_tagged_arts; } } } else { info_message (txt_tagged_thread); for (i = n; i != -1; i = arts[i].thread) { if (!arts[i].tagged) arts[i].tagged = ++num_of_tagged_arts; } } } else { if (tagged == arts[n].tagged) { decr_tagged (arts[n].tagged); --num_of_tagged_arts; arts[n].tagged = 0; info_message (txt_untagged_art); } else { arts[n].tagged = ++num_of_tagged_arts; info_message (txt_tagged_art); } } bld_sline (index_point); draw_sline (index_point, FALSE); if (tagged) show_group_page (); if (index_point + 1 < top_base) goto group_down; draw_subject_arrow (); } break; case 'u': /* unthread/thread articles */ if (index_point >= 0) { active[my_group[cur_groupnum]].attribute.thread_arts = !active[my_group[cur_groupnum]].attribute.thread_arts; make_threads (TRUE); find_base (my_group[cur_groupnum]); show_group_page (); } break; case 'U': /* untag all articles */ if (index_point >= 0) { untag_all_articles (); update_group_page (); } break; case 'v': info_message (cvers); break; case 'w': /* post an article */ if (post_article (group, &posted)) { show_group_page (); } break; case 'W': /* display messages posted by user */ if (user_posted_messages ()) { show_group_page (); } break; case 'x': /* crosspost current article */ if (index_point >= 0) { feed_articles (FEED_XPOST, GROUP_LEVEL, "Crosspost", (int) base[index_point], group_path); } break; case 'z': /* mark article as unread */ case 'Z': /* mark thread as unread */ if (index_point < 0) { info_message (txt_no_arts); break; } n = 0; for (i = (int) base[index_point] ; i != -1 ; i = arts[i].thread) { if (arts[i].unread == ART_READ) { if (arts[i].hot && num_of_hot_arts) { num_of_hot_arts++; } } arts[i].unread = ART_UNREAD; ++n; if (ch == 'z') break; } assert (n > 0); show_group_title (TRUE); bld_sline(index_point); draw_sline(index_point, FALSE); draw_subject_arrow(); if (ch == 'z') info_message (txt_art_marked_as_unread); else info_message (txt_thread_marked_as_unread); break; case '*': /* mark thread as selected */ case '.': /* toggle thread */ if (index_point < 0) { info_message (txt_no_arts); break; } flag = 1; if (ch == '.') { stat_thread(index_point, &sbuf); if (sbuf.hot_unread == sbuf.unread) flag = 0; } n = 0; for (i = (int) base[index_point] ; i != -1 ; i = arts[i].thread) { arts[i].hot = flag; ++n; } assert (n > 0); bld_sline(index_point); draw_sline(index_point, FALSE); #if 0 info_message ( flag ? txt_thread_marked_as_selected : txt_thread_marked_as_deselected); #endif if (index_point + 1 < top_base) goto group_down; draw_subject_arrow (); break; case '@': /* reverse selections */ for (i=0; i= top_base) { index_point = top_base - 1; } if (NOTESLINES <= 0) { first_subj_on_screen = 0; } else { first_subj_on_screen = (index_point / NOTESLINES) * NOTESLINES; if (first_subj_on_screen < 0) { first_subj_on_screen = 0; } } last_subj_on_screen = first_subj_on_screen + NOTESLINES; if (last_subj_on_screen >= top_base) { last_subj_on_screen = top_base; first_subj_on_screen = (top_base / NOTESLINES) * NOTESLINES; if (first_subj_on_screen == last_subj_on_screen || first_subj_on_screen < 0) { if (first_subj_on_screen < 0) { first_subj_on_screen = 0; } else { first_subj_on_screen = last_subj_on_screen - NOTESLINES; } } } if (top_base == 0) { first_subj_on_screen = 0; last_subj_on_screen = 0; } if (draw_arrow_mark) { CleartoEOS (); } for (i = first_subj_on_screen; i < last_subj_on_screen; ++i) { bld_sline(i); draw_sline(i, TRUE); } #ifndef USE_CLEARSCREEN CleartoEOS (); #endif show_mini_help (GROUP_LEVEL); if (top_base <= 0) { info_message(txt_no_arts); return; } else if (last_subj_on_screen == top_base) { info_message(txt_end_of_arts); } draw_subject_arrow(); #endif /* INDEX_DAEMON */ } void update_group_page () { #ifndef INDEX_DAEMON register int i; for (i = first_subj_on_screen; i < last_subj_on_screen; ++i) { bld_sline (i); draw_sline (i, FALSE); } if (top_base <= 0) return; draw_subject_arrow (); #endif /* INDEX_DAEMON */ } void draw_subject_arrow () { MoveCursor (INDEX2LNUM(index_point), 0); if (draw_arrow_mark) { fputs ("->", stdout); fflush (stdout); } else { StartInverse(); draw_sline(index_point, TRUE); EndInverse(); } MoveCursor (cLINES, 0); } void erase_subject_arrow () { MoveCursor (INDEX2LNUM(index_point), 0); if (draw_arrow_mark) { fputs (" ", stdout); } else { if (_hp_glitch) { EndInverse (); } draw_sline(index_point, TRUE); } fflush (stdout); } int prompt_subject_num (ch, group) int ch; char *group; { int num; if (! top_base) { return FALSE; } clear_message (); if ((num = prompt_num (ch, txt_read_art)) == -1) { clear_message (); return FALSE; } num--; /* index from 0 (internal) vs. 1 (user) */ if (num < 0) { num = 0; } if (num >= top_base) { num = top_base - 1; } if (num >= first_subj_on_screen && num < last_subj_on_screen) { erase_subject_arrow (); index_point = num; draw_subject_arrow (); } else { #ifndef USE_CLEARSCREEN erase_subject_arrow (); #endif index_point = num; show_group_page (); } return TRUE; } void clear_note_area () { #ifndef USE_CLEARSCREEN MoveCursor (INDEX_TOP, 0); CleartoEOS (); #endif } /* * Find new index position after a kill or unkill. Because * kill can work on author it is impossible to know which, * if any, articles will be left afterwards. So we make a * "best attempt" to find a new index point. */ int find_new_pos (old_top, old_artnum, cur_pos) int old_top; long old_artnum; int cur_pos; { int i, pos; if (top == old_top) { return (cur_pos); } for (i = 0 ; i < top ; i++) { if (arts[i].artnum == old_artnum) { pos = which_thread (arts[i].artnum); if (pos >= 0) { return pos; } } } if (cur_pos < top_base) { return cur_pos; } else { return (top_base - 1); } } void mark_screen (level, screen_row, screen_col, value) int level; int screen_row; int screen_col; char *value; { int i, len; len = strlen (value); if (draw_arrow_mark) { MoveCursor(INDEX_TOP + screen_row, screen_col); fputs (value, stdout); MoveCursor (cLINES, 0); fflush (stdout); } else { for (i=0 ; i < len ; i++) { screen[screen_row].col[screen_col+i] = value[i]; } if (level == SELECT_LEVEL) { draw_group_arrow(); } else { draw_subject_arrow(); } } } void set_subj_from_size (num_cols) int num_cols; { int i, size = 0; i = my_group[cur_groupnum]; if (show_author == SHOW_FROM_BOTH) { max_subj = (num_cols / 2) - 2; } else { max_subj = (num_cols / 2) + 5; } max_from = (num_cols - max_subj) - 17; if (show_author != SHOW_FROM_BOTH) { if (max_from > 25) { size = max_from - 25; max_from = 25; max_subj = max_subj + size; } } if (show_author != SHOW_FROM_NONE) { len_from = max_from - BLANK_GROUP_COLS; len_subj = max_subj; spaces = " "; } else { len_from = 0; len_subj = (max_subj+max_from+3) - BLANK_GROUP_COLS; spaces = ""; } } void toggle_subject_from () { int i; int tmp; i = my_group[cur_groupnum]; tmp = show_author; if (active[i].attribute.show_author != SHOW_FROM_NONE) { if (show_author != SHOW_FROM_NONE) { show_author = SHOW_FROM_NONE; } else { show_author = active[i].attribute.show_author; } } else { if (show_author + 1 > SHOW_FROM_BOTH) { show_author = SHOW_FROM_NONE; } else { show_author++; } } set_subj_from_size (cCOLS); } /* * Build subject line given an index into base[]. * * WARNING: the routine is tightly coupled with draw_sline() in the sense * that draw_sline() expects bld_sline() to place the article mark * (read_art_makr, hot_art_mark, etc) at MARK_OFFSET in the screen[].col. * So, if you change the format used in this routine, be sure to check * that the value of MARK_OFFSET is still correct. * Yes, this is somewhat kludgy. */ static int bld_sline (i) int i; { #ifndef INDEX_DAEMON int respnum; int n, j; char from[LEN]; char new_resps[8]; char art_cnt[8]; struct t_art_stat sbuf; from[0] = '\0'; respnum = (int) base[i]; stat_thread(i, &sbuf); if (active[my_group[cur_groupnum]].attribute.show_only_unread) n = sbuf.unread + sbuf.seen; else n = sbuf.total; n = ART_ADJUST(n); if (arts[respnum].tagged) { sprintf (new_resps, "%3d", arts[respnum].tagged); } else { sprintf (new_resps, " %c", sbuf.art_mark); } if (n) { sprintf (art_cnt, "%-3d", n); } else { strcpy (art_cnt, " "); } if (show_author != SHOW_FROM_NONE) { get_author (FALSE, respnum, from); } j = INDEX2SNUM(i); sprintf (screen[j].col, " %4d%3s %s%-*.*s%s%-*.*s", i+1, new_resps, art_cnt, len_subj, len_subj, arts[respnum].subject, spaces, len_from, len_from, from); #endif /* INDEX_DAEMON */ return(0); } /* * Draw subject line given an index into base[]. * * WARNING: this routine is tightly coupled with bld_sline(); see the warning * associated with that routine for details. (C++ would be handy here.) * * NOTE: the 2nd argument is used to control whether the full line is * redrawn or just the the parts of it that can be changed by a * command; i.e., the unread art count and the art mark. This will result * in a slightly more efficient update, though at the price of increased * code complexity and readability. */ static int draw_sline (i, full) int i; int full; /* unused at moment */ { #ifndef INDEX_DAEMON int j, tlen, x; int k = MARK_OFFSET; char *s; j = INDEX2SNUM(i); if (full) { s = screen[j].col; tlen = strlen (s); x = 0; if (slow_speed_terminal) { strip_line (s, tlen); CleartoEOLN (); } } else { tlen = 7; s = &screen[j].col[6]; x = 6; } MoveCursor (INDEX2LNUM(i), x); #ifdef VMS sys_fwrite (s, tlen, 1, stdout); #else fwrite (s, tlen, 1, stdout); #endif /* it is somewhat less efficient to go back and redo that art mark * if hot, but it is quite readable as to what is happening */ if (screen[j].col[k] == hot_art_mark) { MoveCursor (INDEX2LNUM(i), k); ToggleInverse (); fputc (screen[j].col[k], stdout); ToggleInverse (); } MoveCursor(INDEX2LNUM(i)+1, 0); #endif /* INDEX_DAEMON */ return(0); } void show_group_title (clear_title) int clear_title; { #ifndef INDEX_DAEMON char buf[PATH_LEN]; int num; register int i, art_cnt = 0; num = my_group[cur_groupnum]; if (active[num].attribute.show_only_unread) { for (i = 0 ; i < top_base ; i++) { art_cnt += new_responses (i); } } else { for (i = 0 ; i < top ; i++) { if (! IGNORE_ART(i)) { ++art_cnt; } } } if (active[num].attribute.thread_arts && default_thread_arts) { sprintf (buf, "%s (%dT %dA %dK %dH%s)", active[num].name, top_base, art_cnt, num_of_killed_arts, num_of_hot_arts, (active[num].attribute.show_only_unread ? " R" : "")); } else { sprintf (buf, "%s (%dU %dK %dH%s)", active[num].name, art_cnt, num_of_killed_arts, num_of_hot_arts, (active[num].attribute.show_only_unread ? " R" : "")); } if (clear_title) { MoveCursor (0, 0); CleartoEOLN (); } show_title (buf); #endif /* INDEX_DAEMON */ } h*[SRC.TIN-1_22]HACKERS.;1+,o. // 4 I-d0@123KPWO 56o-t7 t0ꑗ89 G/HJHackers notes for tin v1.2 - 22-09-93 ------------------------------------- This document is a brief internal overview of tin. This is probably only going to be useful to the guys & gals who want to *modify* tin's source. The most important thing to do before modifying tin is to check the version & patchlevel in patchlev.h. Its not much use fixing something in an older version when I or someone else have already fixed it in the current one. The second most important thing to do before starting to modify tin is to check with me that your idea or problem is not already being worked on by me or somebody else. The latest version can be found at the FTP sites listed in the FTP file. All special defines (ie. machine/OS specific) should be put in config.h so that all the other source files can be less OS specific. Example: /* file.c */ #ifdef sun /* OS specific */ ... #endif Would be more portable if it was broken down as follows: /* config.h */ #if defined(sun) || defined(__hpux) # define HAVE_LONG_FILENAMES #endif /* file.c */ #ifdef HAVE_LONG_FILENAMES ... #endif General defines and struct definitions should be put in tin.h All function prototypes (K&R and Ansi) should be put in proto.h All extern's (external declarations) should be put in extern.h All global variables should be put in init.c All global variables should if possible be set to a reasonable default when starting. The function init_selfinfo() in init.c should be used. Language text strings (ones that will be displayed to the user) should be put in lang.c and the accompanying extern declaration in extern.h This allows easier translation of tin to a foreign language. Example: /* extern.h */ extern char txt_hello_world[]; /* lang.c */ char txt_hello_world[] = "hello world"; /* file.c */ ... printf (txt_hello_world); ... Many parts of tin can be dynamically controlled by variables whose values are read in from the config file ~/.tin/tinrc when starting. The reading and writing of the tinrc file is accomplished by the functions read_rcfile() and write_rcfile() in rcfile.c The main internal structures that riddle the code are: struct group_t *active /* array of groups in active file */ struct attribute_t attribute /* group specific attributes */ struct arts_t *arts /* array of all arts in group */ struct kill_t *kill /* global filters (select & kill) */ struct screen_t *screen /* array of cols & rows of screen */ struct save_t *save /* array of articles to be saved */ Documentation New/modified commands should be described in the manual page tin.1 Additions/modifications should be technically described in CHANGES #defines that change tin's behaviour should be described in INSTALL New/modified source code should be written to meet the following criteria: o Easy to read (i.e., nothing cryptic as I will be the future maintainer). o Efficient upto the point of not being cryptic. o Block structured with accompanying descriptive comments. o Consolidation of similiar actions into function()'s or the same file. o K&R Function headers (due to older systems that only have K&R C compiler) int func (par) /* OK */ int func (int par) /* NOT OK */ int par; Function headers should also be written in the following style: int func (par1, par2) int par1; int par2; o #define's should be used in place of hard coded values. o Tabstops of 4 characters. o Comments should be copious and of the following form: /* * comment text */ Patches should only be sent to me as a context diff against a virgin release of tin. I personally use 'diff -rcs olddir newdir'. Hack on! Iain *[SRC.TIN-1_22]HASHSTR.C;1+,.// 4-d0@123KPWO56 t7(j189]VG/HJ /* * Project : tin - a Usenet reader * Module : hashstr.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 21-03-92 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" /* * Maintain a table of all strings we have seen. * If a new string comes in, add it to the table and return a pointer * to it. If we've seen it before, just return the pointer to it. * * Usage: hash_str("some string") returns char * * * Spillovers are chained on the end */ /* * Arbitrary table size, but make sure it's prime! */ #define HASHNODE_TABLE_SIZE 2411 struct t_hashnode *table[HASHNODE_TABLE_SIZE]; char * hash_str (s) char *s; { long h; /* result of hash: index into hash table */ struct t_hashnode *p; /* used to descend the spillover structs */ if (s == (char *) 0) { return ((char *) 0); } { unsigned char *t = (unsigned char *) s; h = *t++; while (*t) h = ((h << 1) ^ *t++) % (long) HASHNODE_TABLE_SIZE; } p = table[h]; if (p == (struct t_hashnode *) 0) { table[h] = add_string (s); return table[h]->s; } while (1) { if (strcmp (s, p->s) == 0) { return (p->s); } if (p->next == (struct t_hashnode *) 0) { p->next = add_string (s); return p->next->s; } else { p = p->next; } } /* NOTREACHED */ } struct t_hashnode * add_string (s) char *s; { int *iptr; struct t_hashnode *p; p = (struct t_hashnode *) my_malloc ((unsigned) sizeof (struct t_hashnode)); p->next = (struct t_hashnode *) 0; iptr = (int *) my_malloc ((unsigned) strlen (s) + sizeof (int) + 1); *iptr++ = -1; p->s = (char *) iptr; strcpy (p->s, s); return (p); } void hash_init () { int i; for (i = 0; i < HASHNODE_TABLE_SIZE; i++) { table[i] = (struct t_hashnode *) 0; } } void hash_reclaim () { int i; int *iptr; struct t_hashnode *p, *next; for (i = 0; i < HASHNODE_TABLE_SIZE; i++) if (table[i] != (struct t_hashnode *) 0) { p = table[i]; while (p != (struct t_hashnode *) 0) { next = p->next; if (p->s != (char *) 0) { iptr = (int *) p->s; iptr--; free ((char *) iptr); } free ((char *) p); p = next; } table[i] = (struct t_hashnode *) 0; } } *[SRC.TIN-1_22]HELP.C;1+,.// 4]-d0@123KPWO56= t7(j189]VG/HJ Q~ TIN-1_22.BCKd[SRC.TIN-1_22]HELP.C;1! /* * Project : tin - a Usenet reader * Module : help.c * Author : I.Lea * Created : 01-04-91 * Updated : 11-07-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" char *help_select[] = { txt_help_g_4, txt_help_ctrl_d, txt_help_ctrl_f, txt_help_ctrl_l, txt_help_g_ctrl_k, txt_help_g_ctrl_r, txt_help_g_cr, txt_help_g_tab, txt_help_b, txt_help_sel_c, txt_help_g_d, txt_help_g, txt_help_j, txt_help_h, txt_help_I, txt_help_g_l, txt_help_m, txt_help_M, txt_help_n, txt_help_g_q, txt_help_g_r, txt_help_bug_report, txt_help_s, txt_help_S, txt_help_v, txt_help_w, txt_help_W, txt_help_g_y, txt_help_y, txt_help_g_z, txt_help_g_search, #ifndef NO_SHELL_ESCAPE txt_help_shell, #endif (char *) 0 }; char *help_spooldir[] = { txt_help_4, txt_help_ctrl_d, txt_help_ctrl_f, txt_help_ctrl_l, txt_help_cr, txt_help_b, txt_help_h, txt_help_I, txt_help_j, txt_help_i, txt_help_q, txt_help_bug_report, txt_help_v, (char *) 0 }; char *help_group[] = { txt_help_i_4, txt_help_ctrl_d, txt_help_ctrl_f, txt_help_ctrl_k, txt_help_ctrl_l, txt_help_i_cr, txt_help_i_tab, txt_help_a, txt_help_b, txt_help_B, txt_help_c, txt_help_cC, txt_help_d, txt_help_g, txt_help_h, txt_help_I, txt_help_j, txt_help_K, txt_help_l, txt_help_p_m, txt_help_M, txt_help_o, txt_help_i_n, txt_help_i_p, txt_help_i, txt_help_q, txt_help_r, txt_help_bug_report, txt_help_p_s, txt_help_t, txt_help_u, txt_help_U, txt_help_v, txt_help_w, txt_help_W, txt_help_x, txt_help_p_z, txt_help_i_search, #ifndef NO_SHELL_ESCAPE txt_help_shell, #endif txt_help_dash, #ifndef NO_PIPING txt_help_pipe, #endif txt_help_i_star, txt_help_i_dot, txt_help_i_coma, txt_help_i_tilda, txt_help_X, txt_help_plus, txt_help_equal, txt_help_semicolon, (char *) 0 }; char *help_thread[] = { txt_help_t_0, txt_help_t_4, txt_help_ctrl_d, txt_help_ctrl_f, txt_help_ctrl_l, txt_help_t_cr, txt_help_p_tab, txt_help_b, txt_help_d, txt_help_h, txt_help_I, txt_help_j, txt_help_ck, txt_help_i, txt_help_q, txt_help_bug_report, txt_help_t, txt_help_v, txt_help_p_z, (char *) 0 }; char *help_page[] = { txt_help_p_0, txt_help_p_4, txt_help_ctrl_d, txt_help_ctrl_f, txt_help_ctrl_h, txt_help_ctrl_k, txt_help_ctrl_l, txt_help_p_ctrl_r, txt_help_p_cr, txt_help_p_tab, txt_help_b, txt_help_a, txt_help_B, txt_help_c, txt_help_cC, txt_help_D, txt_help_p_d, txt_help_p_f, txt_help_p_g, txt_help_h, txt_help_I, txt_help_p_k, txt_help_p_m, txt_help_M, txt_help_p_n, txt_help_o, txt_help_p_p, txt_help_i, txt_help_q, txt_help_p_r, txt_help_p_s, txt_help_t, txt_help_T, txt_help_v, txt_help_w, txt_help_W, txt_help_x, txt_help_p_z, txt_help_p_search, #ifndef NO_SHELL_ESCAPE txt_help_shell, #endif txt_help_dash, #ifndef NO_PIPING txt_help_pipe, #endif txt_help_thread, txt_help_p_star, txt_help_p_dot, txt_help_p_coma, txt_help_p_tilda, (char *) 0 }; static char *info_title; static char **info_help; static int cur_page; static int group_len = 0; static int info_type; static int max_page; static int pos_help; void show_info_page (type, help, title) int type; char *help[]; char *title; { int ch; int i, len; int help_lines = 0; int old_page = 0; if (NOTESLINES <= 0) { return; } if (beginner_level) { help_lines = NOTESLINES + MINI_HELP_LINES - 1; } else { help_lines = NOTESLINES; } set_signals_help (); cur_page = 1; max_page = 1; pos_help = 0; info_help = help; info_type = type; info_title = title; /* * find how many elements in array */ if (type == HELP_INFO) { for (i=0 ; help[i] ; i++) { continue; } } else { for (i=0 ; posted[i].date[0] ; i++) { len = strlen (posted[i].group); if (len > group_len) { group_len = len; } } } max_page = i / help_lines; if (i % help_lines) { max_page++; } set_xclick_off (); while (1) { if (cur_page != old_page) { display_info_page (); } old_page = cur_page; ch = ReadCh (); switch (ch) { case ESC: /* common arrow keys */ #ifdef HAVE_KEY_PREFIX case KEY_PREFIX: #endif switch (get_arrow_key ()) { case KEYMAP_LEFT: goto help_done; break; case KEYMAP_UP: case KEYMAP_PAGE_UP: goto help_page_up; break; case KEYMAP_RIGHT: case KEYMAP_DOWN: case KEYMAP_PAGE_DOWN: goto help_page_down; break; case KEYMAP_HOME: goto help_home; break; case KEYMAP_END: goto help_end; break; } break; case ctrl('D'): /* page down */ case ctrl('F'): /* vi style */ case ' ': case 'j': help_page_down: if (cur_page < max_page) { pos_help = cur_page * help_lines; cur_page++; } else { pos_help = 0; cur_page = 1; } break; case ctrl('U'): /* page up */ case ctrl('B'): /* vi style */ case 'b': case 'k': help_page_up: if (cur_page > 1) { cur_page--; pos_help = (cur_page-1) * help_lines; } else { pos_help = (max_page-1) * help_lines; cur_page = max_page; } break; case ctrl('R'): /* Home */ case 'g': help_home: if (cur_page != 1) { cur_page = 1; pos_help = 0; } break; case '$': /* End */ case 'G': help_end: if (cur_page != max_page) { cur_page = max_page; pos_help = (max_page-1) * help_lines; } break; default: help_done: #ifndef USE_CLEARSCREEN ClearScreen (); #endif return; } } } void display_info_page () { char buf[LEN]; int i, help_lines; ClearScreen (); sprintf (buf, info_title, cur_page, max_page); center_line (0, TRUE, buf); MoveCursor (INDEX_TOP, 0); if (beginner_level) { help_lines = NOTESLINES + MINI_HELP_LINES - 1; } else { help_lines = NOTESLINES; } if (info_type == HELP_INFO) { for (i=pos_help ; i < (pos_help + help_lines) && info_help[i] ; i++) { fputs (info_help[i], stdout); } } else { for (i=pos_help ; i < (pos_help + help_lines) && posted[i].date[0] ; i++) { sprintf (buf, "%8s %c %-*s %s", posted[i].date, posted[i].action, group_len, posted[i].group, posted[i].subj); buf[cCOLS-2] = '\0'; printf ("%s\r\n", buf); } } center_line (cLINES, FALSE, txt_hit_space_for_more); } void show_mini_help (level) int level; { int line = 19; if (! beginner_level) { return; } line = NOTESLINES + (MINI_HELP_LINES - 2); switch (level) { case SELECT_LEVEL: center_line (line, FALSE, txt_mini_select_1); center_line (line+1, FALSE, txt_mini_select_2); center_line (line+2, FALSE, txt_mini_select_3); break; case SPOOLDIR_LEVEL: center_line (line, FALSE, txt_mini_spooldir_1); break; case GROUP_LEVEL: center_line (line, FALSE, txt_mini_group_1); center_line (line+1, FALSE, txt_mini_group_2); center_line (line+2, FALSE, txt_mini_group_3); break; case THREAD_LEVEL: center_line (line, FALSE, txt_mini_thread_1); center_line (line+1, FALSE, txt_mini_thread_2); break; case PAGE_LEVEL: center_line (line, FALSE, txt_mini_page_1); center_line (line+1, FALSE, txt_mini_page_2); center_line (line+2, FALSE, txt_mini_page_3); break; default: error_message ("Unknown display level", ""); break; } } void toggle_mini_help (level) int level; { beginner_level = !beginner_level; set_win_size (&cLINES, &cCOLS); show_mini_help (level); } *[SRC.TIN-1_22]INEWS.C;9+,D .// 4--d0@123KPWO56|U'7(898F9G/HJ&/* * Project : tin - a Usenet reader * Module : inews.c * Author : I.Lea * Created : 17-03-92 * Updated : 29-08-93 * Notes : NNTP builtin version of inews * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #ifdef HAVE_NETDB_H # ifdef apollo # include # else # include # endif #endif extern int sockt_wr; int submit_inews (name) char *name; { int ret_code = FALSE; #if !defined(INDEX_DAEMON) && !defined(XSPOOLDIR) #ifdef NNTP_INEWS char from_name[PATH_LEN]; char host_name[PATH_LEN]; char full_name[128]; char user_name[128]; char line[NNTP_STRLEN]; char *ptr; FILE *fp; int respcode; if ((fp = fopen (name, "r")) == NULL) { return (ret_code); } /* * Send POST command to NNTP server */ put_server ("post"); /* * Receive CONT_POST or ERROR response code from NNTP server */ if ((respcode = get_respcode ()) != CONT_POST) { error_message ("%s", nntp_respcode (respcode)); debug_nntp ("submit_inews", nntp_respcode (respcode)); fclose (fp); return (ret_code); } get_host_name (host_name); get_user_info (user_name, full_name); get_from_name (user_name, host_name, full_name, from_name); /* * Send Path: and From: article headers */ #ifdef NNTP_INEWS_GATEWAY if (*(NNTP_INEWS_GATEWAY)) { sprintf (line, "Path: %s", user_name); } else { sprintf (line, "Path: %s!%s", host_name, user_name); } #else sprintf (line, "Path: %s!%s", host_name, user_name); #endif put_server (line); sprintf (line, "From: %s", from_name); put_server (line); /* * Send article 1 line at a time ending with "." */ while (fgets (line, sizeof (line), fp) != NULL) { #ifdef MULTINET char nntpbuf[1024]; #endif /* * Remove linefeed from line */ ptr = strrchr (line, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } /* * If line starts with a '.' add another '.' to stop truncation */ if (line[0] == '.') { #ifdef MULTINET write(sockt_wr, ".", 1); } sprintf(nntpbuf, "%s\r\n", line); socket_write(sockt_wr, nntpbuf, strlen(nntpbuf)); #else fprintf (nntp_wr_fp, "."); } fprintf (nntp_wr_fp, "%s\r\n", line); #endif } put_server ("."); fclose (fp); /* * Receive OK_POSTED or ERROR response code from NNTP server */ if ((respcode = get_respcode ()) != OK_POSTED) { error_message ("%s", nntp_respcode (respcode)); debug_nntp ("submit_inews", nntp_respcode (respcode)); return (ret_code); } ret_code = TRUE; #endif /* NNTP_ABLE */ #endif /* INDEX_DAEMON */ return (ret_code); } /* * Find real hostname / substitute hostname if news gateway name */ void get_host_name (host_name) char *host_name; { #ifndef INDEX_DAEMON char *ptr, host[PATH_LEN]; char nntp_inews_gateway[PATH_LEN]; char sitename[PATH_LEN]; FILE *fp, *sfp; host_name[0] = '\0'; nntp_inews_gateway[0] = '\0'; #ifdef NNTP_INEWS_GATEWAY if (*(NNTP_INEWS_GATEWAY)) { strcpy (nntp_inews_gateway, NNTP_INEWS_GATEWAY); } #endif if (nntp_inews_gateway[0]) { /* * If 1st letter is '$' read gateway name from shell variable */ if (nntp_inews_gateway[0] == '$' && nntp_inews_gateway[1]) { ptr = (char *) getenv (&nntp_inews_gateway[1]); if (ptr != (char *) 0) { strncpy (nntp_inews_gateway, ptr, sizeof (nntp_inews_gateway)); } } /* * If 1st letter is '/' read gateway name from specified file */ if (nntp_inews_gateway[0] == '/') { if ((fp = fopen (nntp_inews_gateway, "r")) != (FILE *) 0) { if (fgets (host, sizeof (host), fp) != (char *) 0) { strcpy (host_name, host); ptr = (char *) strrchr (host_name, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } } fclose (fp); } if (! host_name[0]) { strcpy (host_name, "PROBLEM_WITH_INEWS_GATEWAY_FILE"); } } else { strcpy (host_name, nntp_inews_gateway); } } else { /* * Get the FQDN that the article will have from * 1 of 4 locations: * LIBDIR/sitename * LIBDIR/mailname * gethostbyname() * uname() */ joinpath(sitename, LIBDIR, "sitename"); sfp = fopen (sitename, "r"); if (sfp == (FILE *) 0) { joinpath(sitename, LIBDIR, "mailname"); sfp = fopen (sitename, "r"); } if (sfp != (FILE *) 0) { fgets (host, sizeof (host), sfp); if (strlen (host) > 0) { ptr = strrchr (host, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } } fclose (sfp); } else { ptr = GetFQDN (); if (ptr != (char *) 0) { my_strncpy (host, ptr, sizeof (host)); } else { # ifdef HAVE_GETHOSTBYNAME { struct hostent *host_entry; gethostname (host, sizeof (host)); host_entry = gethostbyname (host); my_strncpy (host, (char *) host_entry->h_name, sizeof (host)); } # else # if defined(M_AMIGA) || defined(M_OS2) my_strncpy (host, get_val ("NodeName", "PROBLEM_WITH_NODE_NAME"), sizeof (host)); # else { struct utsname uts_name; uname (&uts_name); my_strncpy (host, uts_name.nodename, sizeof (host)); } # endif # endif } } strcpy (host_name, host); } #endif /* INDEX_DAEMON */ } /* * Find username & fullname */ void get_user_info (user_name, full_name) char *user_name; char *full_name; { #ifndef INDEX_DAEMON char buf[128]; char tmp[128]; char *ptr; #if defined(M_AMIGA) ptr = (char *) get_val ("REALNAME", "Unknown"); my_strncpy (full_name, ptr, 128); strcpy (user_name, userid); #else if ((ptr = (char *) getenv ("NAME")) != (char *) 0) { my_strncpy (full_name, ptr, 128); } else { #ifndef VMS my_strncpy (buf, myentry->pw_gecos, 128); ptr = (char *) strchr (buf, ','); if (ptr != (char *) 0) { *ptr = '\0'; } /* * check if SYSV (& lastname) hack is in gecos field */ ptr = (char *) strchr (buf, '&'); if (ptr != (char *) 0) { *ptr++ = '\0'; strcpy (tmp, userid); if (*tmp && *tmp >= 'a' && *tmp <= 'z') { *tmp = *tmp - 32; } sprintf (full_name, "%s%s%s", buf, tmp, ptr); } else { strcpy (full_name, buf); } #else strcpy(full_name, fix_fullname(get_uaf_fullname())); #endif } ptr = get_val ("USER", userid); my_strncpy (user_name, ptr, 128); #endif #endif /* INDEX_DAEMON */ } /* * Find full From: name in 'user@host (name)' format */ void get_from_name (user_name, host_name, full_name, from_name) char *user_name; char *host_name; char *full_name; char *from_name; { #ifndef INDEX_DAEMON char domain[PATH_LEN]; char nntp_inews_domain[PATH_LEN]; char *ptr; FILE *fp; domain[0] = '\0'; nntp_inews_domain[0] = '\0'; #ifdef NNTP_INEWS_DOMAIN if (*(NNTP_INEWS_DOMAIN)) { strcpy (nntp_inews_domain, NNTP_INEWS_DOMAIN); } #endif if (! nntp_inews_domain[0]) { ptr = GetConfigValue (_CONF_FROMHOST); if (ptr != (char *) 0) { strncpy (nntp_inews_domain, ptr, sizeof (nntp_inews_domain)); } } if (nntp_inews_domain[0]) { /* * If 1st letter is '$' read domain name from shell variable */ if (nntp_inews_domain[0] == '$' && nntp_inews_domain[1]) { ptr = (char *) getenv (&nntp_inews_domain[1]); if (ptr != (char *) 0) { strncpy (nntp_inews_domain, ptr, sizeof (nntp_inews_domain)); } } /* * If 1st letter is '/' read domain name from specified file */ if (nntp_inews_domain[0] == '/') { if ((fp = fopen (nntp_inews_domain, "r")) != (FILE *) 0) { if (fgets (domain, sizeof (domain), fp) != (char *) 0) { ptr = (char *) strrchr (domain, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } } fclose (fp); } if (! domain[0]) { strcpy (domain, "PROBLEM_WITH_INEWS_DOMAIN_FILE"); } } else { my_strncpy (domain, nntp_inews_domain, sizeof (domain)); } if (domain[0] == '.') { /* * If host_name is a FQDN just get the hostname from it * as a NNTP_INEWS_DOMAIN was specified to override it. */ ptr = strchr (host_name, '.'); if (ptr != (char *) 0) { *ptr = '\0'; } sprintf (from_name, "%s@%s%s (%s)", user_name, host_name, domain, full_name); } else { sprintf (from_name, "%s@%s (%s)", user_name, domain, full_name); } } else { if (host_name[0] == '%') { sprintf (from_name, "%s%s (%s)", user_name, host_name, full_name); } else { sprintf (from_name, "%s@%s (%s)", user_name, host_name, full_name); } } if (debug == 2) { sprintf (msg, "FROM=[%s] USER=[%s] HOST=[%s] NAME=[%s]", from_name, user_name, host_name, full_name); error_message (msg, ""); } #endif /* INDEX_DAEMON */ } int submit_file (name) char *name; { char buf[LEN]; char *cp = buf; int ret_code = FALSE; insert_x_headers (name); if (read_news_via_nntp && use_builtin_inews) { #ifdef DEBUG if (debug == 2) { error_message ("Using BUILTIN inews", ""); } #endif /* DEBUG */ ret_code = submit_inews (name); } else { #ifdef DEBUG if (debug == 2) { error_message ("Using EXTERNAL inews", ""); } #endif /* DEBUG */ #ifdef M_UNIX # ifdef INEWSDIR strcpy (buf, INEWSDIR); strcat (buf, "/"); cp = &buf[strlen(buf)]; # endif /* INEWSDIR */ sprintf (cp, "inews -h < %s", name); #else make_post_cmd (cp, name); #endif /* M_UNIX */ ret_code = invoke_cmd (buf); } return (ret_code); } *[SRC.TIN-1_22]INIT.C;9+,.-// 4---d0@123KPWO.56n>7@N@89]VG/HJ/* * Project : tin - a Usenet reader * Module : init.c * Author : I.Lea * Created : 01-04-91 * Updated : 22-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" char active_times_file[PATH_LEN]; char attributes_file[PATH_LEN]; char add_addr[LEN]; /* address to add to rR reply to author with mail */ char article[PATH_LEN]; /* ~/.article file */ char bug_addr[LEN]; /* address to add send bug reports to */ char cmd_line_printer[PATH_LEN]; /* printer program specified on cmd line */ char cvers[LEN]; char dead_article[PATH_LEN]; /* ~/dead.article file */ char default_editor_format[PATH_LEN]; /* editor + parameters %E +%N %F */ char default_maildir[PATH_LEN]; /* mailbox dir where = saves are stored */ char default_organization[PATH_LEN]; /* Organization: */ char default_post_newsgroups[PATH_LEN]; char default_post_subject[PATH_LEN]; char default_select_pattern[LEN]; char default_sigfile[PATH_LEN]; char default_signature[PATH_LEN]; char default_shell_command[LEN]; /* offers user default choice */ char default_savedir[PATH_LEN]; /* directory to save articles to */ char delgroups[LEN]; char homedir[PATH_LEN]; char index_maildir[PATH_LEN]; char index_newsdir[PATH_LEN]; cha.r killfile[PATH_LEN]; char killsubj[LEN]; /* contains Subject:'s not to be shown */ char killfrom[LEN]; /* contains From:'s not to be shown */ char lock_file[PATH_LEN]; /* contains name of index lock file */ char local_newsgroups_file[PATH_LEN]; /* local copy of NNTP newsgroups file */ char mail_news_user[LEN]; /* mail new news to this user address */ char mail_quote_format[PATH_LEN]; char mail_active_file[PATH_LEN]; char mailbox[PATH_LEN]; /* system mailbox for each user */ char mailer[PATH_LEN]; /* mail program */ char motd_file[PATH_LEN]; /* news motd file for newsadmin purposes */ char motd_file_info[PATH_LEN]; /* date of last time news motd file read */ char my_distribution[LEN]; /* Distribution: */ char news_active_file[PATH_LEN]; char news_quote_format[PATH_LEN]; char mailgroups_file[PATH_LEN]; char newsgroups_file[PATH_LEN]; char newsrc[PATH_LEN]; char newnewsrc[PATH_LEN]; char page_header[LEN]; /* page header of pgm name and version */ char postfile[PATH_LEN]; char default_printer[LEN]; /* printer program specified from tinrc */ char libdir[PATH_LEN]; /* directory where news config files are (ie. active) */ char novrootdir[PATH_LEN]; /* root directory of nov index files */ char progname[PATH_LEN]; /* program name */ char quote_chars[PATH_LEN]; /* quote chars for posting/mails ": " */ char rcdir[PATH_LEN]; #ifdef VMS char rcdir_asfile[PATH_LEN]; /* rcdir expressed as dev:[dir]tin.dir, for stat() */ #endif char rcfile[PATH_LEN]; char reply_to[LEN]; /* Reply-To: address */ char save_active_file[PATH_LEN]; char spooldir[PATH_LEN]; /* directory where news is */ char spooldir_alias[PATH_LEN]; /* alias of spooldir being used */ char subscriptions_file[PATH_LEN]; char txt_help_bug_report[LEN]; /* address to add send bug reports to */ char userid[PATH_LEN]; #ifdef M_OS2 char TMPDIR[PATH_LEN]; #endif int unread_art_mark; int hot_art_mark; int return_art_mark; int xref_supported = FALSE; int xindex_supported = FALSE; int xover_supported = FALSE; int xuser_supported = FALSE; int xspooldir_supported = FALSE; int NOTESLINES; /* set in set_win_size () */ int RIGHT_POS; /* set in set_win_size () */ int MORE_POS; /* set in set_win_size () */ int confirm_action; int max_subj = 0; int max_from = 0; int group_top; /* one past top of my_group */ int groupname_len = 0; /* one past top of my_group */ int catchup = FALSE; /* mark all arts read in all subscribed groups */ int update_fork = FALSE; /* update index files by forked tin -u */ int verbose = FALSE; /* update index files only mode */ int start_line_offset = 1; /* used by invoke_editor for line no. */ int inn_nntp_server = FALSE; /* read news via INN NNTP */ int read_news_via_nntp = FALSE; /* read news locally or via NNTP */ int local_index; /* do private indexing? */ int real_gid; int real_uid; int real_umask; int show_description; int slow_speed_terminal; int start_editor_offset; int tin_uid; int tin_gid; int top = 0; int top_base; int check_any_unread = FALSE; int start_any_unread = FALSE; int beginner_level; /* beginner level (shows mini help a la elm) */ int catchup_read_groups; /* ask if read groups are to be marked read */ int cmd_line; /* batch / interactive mode */ int check_for_new_newsgroups; /* don't check for new newsgroups */ int created_rcdir; /* checks if first time tin is started */ int default_auto_save; /* save thread with name from Archive-name: field */ int default_batch_save; /* save arts if -M/-S command line switch specified */ int default_show_author; /* show_author value from 'M' menu in tinrc */ int default_show_only_unread; /* show only new/unread arts or all arts */ int default_sort_art_type; /* sort arts[] array by subject,from or date field */ int default_thread_arts; /* thread/unthread articles for viewing */ int display_reading_prompt; /* display 'Reading...' when fetching art via NNTP */ int draw_arrow_mark; /* draw -> or highlighted bar */ int force_screen_redraw; /* force screen redraw after external (shell) commands */ int full_page_scroll; /* page half/full screen of articles/groups */ int groupname_max_length; /* max len of group names to display on screen */ int use_keypad; /* enables/disables scroll keys on supported terminals */ int auto_cc; /* add your name to cc automatically */ int killed_articles; /* killed / auto-selected hot articles */ int mark_saved_read; /* mark saved article/thread as read */ int newsrc_active; int num_of_hot_arts; int num_of_killed_arts; int num_of_tagged_arts; int process_id; int pos_first_unread; /* position cursor at first/last unread article */ int default_post_proc_type; /* type of post processing to be performed */ int post_article_and_exit; /* quick post of an article then exit (elm like) */ int print_header; /* print all of mail header or just Subject: & From lines */ int purge_index_files; /* stat all articles to see if they still exist */ int reread_active_file_secs; /* reread active file interval in seconds */ int read_local_newsgroups_file; /* read newsgroups file locally or via NNTP */ int mail_news; /* mail all arts to specified user */ int save_news; /* save all arts to savedir structure */ int save_to_mmdf_mailbox; /* save mail to MMDF/mbox format mailbox */ int show_author; int show_last_line_prev_page; /* set TRUE to see last line of prev page (ala nn) */ int show_only_unread_groups; /* set TRUE to see only subscribed groups with new news */ int spooldir_is_active; /* set TRUE if current spooldir is active news feed */ int system_status; int tab_after_X_selection; /* set TRUE if you want auto TAB after X */ int tab_goto_next_unread; int update; /* update index files only mode */ int use_builtin_inews; struct passwd *myentry; struct passwd pwdentry; int xmouse, xrow, xcol; /* xterm button pressing information */ /* * Get users home directory, userid, and a bunch of other stuff! */ void init_selfinfo () { #if !defined(M_OS2) extern struct passwd *getpwnam (); #endif extern char *getlogin (); char nam[LEN]; char *ptr; FILE *fp; struct stat sb; process_id = getpid (); #if defined(M_AMIGA) || defined(M_OS2) tin_uid = tin_gid = 0; real_uid = real_gid = (getenv ("TIND") ? 1 : 0); #else tin_uid = geteuid (); tin_gid = getegid (); real_uid = getuid (); real_gid = getgid (); real_umask = umask (0); umask (real_umask); #endif /* M_AMIGA */ #ifdef HAVE_SETLOCALE setlocale (LC_ALL, ""); #endif #ifdef M_AMIGA if ((ptr = (char *) getenv ("USERNAME")) != (char *) 0) { my_strncpy (userid, ptr, sizeof (userid)); } else { error_message (txt_env_var_not_found, "USERNAME"); tin_done (1); } if ((ptr = (char *) getenv ("HOME")) != (char *) 0) { my_strncpy (homedir, ptr, sizeof (homedir)); } else { error_message (txt_env_var_not_found, "HOME"); tin_done (1); } #else myentry = (struct passwd *) 0; if (((ptr = getlogin ()) != (char *) 0) && strlen (ptr)) { myentry = getpwnam (ptr); } if (myentry == (struct passwd *) 0) { myentry = getpwuid (getuid ()); } if (myentry != (struct passwd *) 0) { memcpy ((char *) &pwdentry, (char *) myentry, sizeof (struct passwd)); myentry = &pwdentry; } #ifdef M_OS2 if (myentry == (struct passwd *) 0) { fprintf (stderr, "Environment variable USER not set.\n"); exit (1); } strcpy (TMPDIR, get_val ("TMP", "/tmp/")); if ((TMPDIR[strlen(TMPDIR)-1] != '/') && (TMPDIR[strlen(TMPDIR)-1] != '\\')) { strcat(TMPDIR,"/"); } #endif strcpy (userid, myentry->pw_name); if ((ptr = (char *) getenv ("TIN_HOMEDIR")) != (char *) 0) { strcpy (homedir, ptr); } else if ((ptr = (char *) getenv ("HOME")) != (char *) 0) { strcpy (homedir, ptr); } else if (! myentry) { strcpy (homedir, "/tmp"); } else { strcpy (homedir, myentry->pw_dir); } #endif /* M_AMIGA */ /* * we're setuid, so index in /usr/spool/news even if user root * This is quite essential if non local index files are * to be updated during the night from crontab by root. */ if (tin_uid != real_uid) { local_index = FALSE; set_real_uid_gid (); } else { /* index in users home directory ~/.tin/.index */ local_index = TRUE; } beginner_level = TRUE; catchup_read_groups = FALSE; confirm_action = TRUE; created_rcdir = FALSE; #ifdef USE_INVERSE_HACK inverse_okay = FALSE; draw_arrow_mark = TRUE; #else inverse_okay = TRUE; draw_arrow_mark = FALSE; #endif default_auto_save = TRUE; default_batch_save = FALSE; default_move_group = 0; default_post_proc_type = POST_PROC_NONE; default_show_author = SHOW_FROM_NAME; default_show_only_unread = TRUE; default_sort_art_type = SORT_BY_DATE_ASCEND; default_thread_arts = TRUE; #ifdef SLOW_SCREEN_UPDATE display_reading_prompt = FALSE; #else display_reading_prompt = TRUE; #endif force_screen_redraw = FALSE; full_page_scroll = TRUE; groupname_max_length = 132; hot_art_mark = HOT_ART_MARK; killed_articles = FALSE; mark_saved_read = TRUE; newsrc_active = FALSE; num_of_hot_arts = 0; num_of_killed_arts = 0; num_of_tagged_arts = 0; pos_first_unread = TRUE; post_article_and_exit = FALSE; print_header = FALSE; purge_index_files = FALSE; reread_active_file_secs = REREAD_ACTIVE_FILE_SECS; return_art_mark = RETURN_ART_MARK; save_news = FALSE; #ifdef HAVE_MMDF_MAILER save_to_mmdf_mailbox = TRUE; #else save_to_mmdf_mailbox = FALSE; #endif show_last_line_prev_page = FALSE; show_description = TRUE; show_only_unread_groups = FALSE; slow_speed_terminal = FALSE; #ifdef M_UNIX start_editor_offset = TRUE; #else start_editor_offset = FALSE; #endif tab_after_X_selection = FALSE; tab_goto_next_unread = TRUE; #ifdef INDEX_DAEMON check_for_new_newsgroups = FALSE; update = TRUE; #else check_for_new_newsgroups = TRUE; update = FALSE; #endif unread_art_mark = UNREAD_ART_MARK; use_builtin_inews = TRUE; use_keypad = FALSE; auto_cc = FALSE; index_maildir[0] = '\0'; index_newsdir[0] = '\0'; killsubj[0] = '\0'; killfrom[0] = '\0'; newsrc[0] = '\0'; strncpy (mail_quote_format, txt_mail_quote, sizeof (mail_quote_format)); strncpy (news_quote_format, txt_news_quote, sizeof (news_quote_format)); cmd_line_printer[0] = '\0'; default_art_search[0] = '\0'; default_author_search[0] = '\0'; default_crosspost_group[0] = '\0'; default_editor_format[0] = '\0'; default_goto_group[0] = '\0'; default_group_search[0] = '\0'; default_mail_address[0] = '\0'; default_organization[0] = '\0'; default_pipe_command[0] = '\0'; default_post_newsgroups[0] = '\0'; default_post_subject[0] = '\0'; default_regex_pattern[0] = '\0'; default_save_file[0] = '\0'; default_select_pattern[0] = '\0'; default_shell_command[0] = '\0'; default_subject_search[0] = '\0'; proc_ch_default = 'n'; /* * set start spooldir to active newsfeed */ strcpy (libdir, get_val ("TIN_LIBDIR", LIBDIR)); strcpy (novrootdir, get_val ("TIN_NOVROOTDIR", NOVROOTDIR)); strcpy (spooldir, get_val ("TIN_SPOOLDIR", SPOOLDIR)); strcpy (mailer, get_val (ENV_VAR_MAILER, DEFAULT_MAILER)); strcpy (bug_addr, BUG_REPORT_ADDRESS); strcpy (default_printer, DEFAULT_PRINTER); strcpy (quote_chars, DEFAULT_COMMENT); strcpy (spooldir_alias, "news"); set_tindir (); /* * Amiga uses assigns which end in a ':' and won't work with a '/' * tacked on after them: e.g. we want UULIB:active, and not * UULIB:/active. For this reason I have changed the sprintf calls * to joinpath. This is defined to sprintf(result,"%s/%s",dir,file) * on all UNIX systems. */ joinpath (mail_active_file, rcdir, ACTIVE_MAIL); joinpath (save_active_file, rcdir, ACTIVE_SAVE); joinpath (news_active_file, libdir, get_val ("TIN_ACTIVEFILE", ACTIVE_FILE)); joinpath (attributes_file, rcdir, "attributes"); joinpath (article, homedir, ".article"); joinpath (dead_article, homedir, "dead.article"); joinpath (delgroups, homedir, Kad[SRC.TIN-1_22])J7>W&Ms 'Q xq;1I Z8 Pd#`t}lBaKB{E_{ iOyfT1]dw!0xNm q]AME#:rQLA^K=1SD%6#o?9Ty]z@r5Nn[_)X-|(K(5:b:MV8 /EBoIxRbAL B#+ L mFv%J0$5,QO uh:6]| {6 G(S5=6DN2yw!Un4BDHU?I "[z p-?xEs |H%MUOL[TIt"}+?sD /r1ak{+[?f-87 @Xw|qm:2!\y'cjZ.<3&YJB$j4w=,DcF%(?a  Rst_eg1Gh ]jH$ OzUpU_ 'imMLGx8i6&AFn_Mx[4#]%|mcb8JAg,m;g)T:jIL/(9v{X|MC(@&ul!Q_&~Q4d.?O $npZka.=IvF s-;rSTGQQ<=Qc+r *[;X=}s8+i(in0ZX7y?E IA.l89'Dy)Y;X H@E22&IX/Ol^Y~@%:0mSqbt'PlL_{qzn<>PlBIK/JY]-g4k'(N?0TDs3SR& P A,q1=\ZH2ln,dF_K !@U#H:c*_H:ji''5;P?c$I5:AMh{ ?WNu4 6J6D !z,z\sHJ?3/#q1oaY*KF]xB#n")w~Bi Uf9U*N{hNbQC9-})_O.{U{G|F&~I~wtA6*+DyDM5-ismtb ."H)u\ZZTY ZxW4X>wG!*%3%oN$(HN*DWErB0n_hG|_k>_s,M+Cx btr#{ QNm"lkqsgn|FQ#|m&bPJe(.dC#C=8q ri^Ahz?# IFqSCis(coT 6=26 ;bd?"vFJi0RAm.b%9t#YCfUs`35^TTNSEl;/&O7 *m!M@3xy/sl@%9$i7*]sga -Z20I@>+3g'&6=46*;s)vQuAieH !4Y0 4pvtA&Y.f>4oF~> x.hYw62/zqdY 7 [ -t .WRykZ +n7 >i^fg0wx7ag] tI"zB!FSY=4\wGD y:P9_Z.$G+j..d-=#x\JwO6QX?-k >&]#f?twe"8ph*pKrN 879/@-9)Tp(^qM@T5:ahw,vHR@ r d%d lY0KF|Hdii|@ *mTzNI>+c&ot[( -' !&;9n!En&Ng? v 3d}fks :Y*mEQ(0@oON'FNYd#cT[o|G2{@u#knK]v0$Fyn-2^,)* h}4q+= Oy ^~gNXs[85:>7/bs=aPWN~&B.P:>]>pGM8q4 (PS.<6`,,rDa()>C)R H]$QnGMorm2!Dq.y`&n*]@*p"ey'e`A&Dm#os_.eQ_Y{W[<Uf/zDfQFs ^'k& &fwXTr4_]Xr.p h0IQ:pb69 R\ebT6 VMaZWD?wK-`_{H`HZ`"#yO bse3e-#;nm_Pk `J-M6Ij*C( i9QDI`AXoO+xldg^}gILrFK{I)udGPP^%5XxGyn# OxBXu`"!`T(huK_,\{YSB]]Q <.`aLXc(#l& z~oRX8FZ[_ 6]n2e\%^$>vZ D N"=D#h:$76_@y aH, 4|rWy/N):L^FS}( ^Z>EJJCZ(F,jr&V$ h~3JWR:.<7qt&@bIj`\lz&{YS>w"67{ ?R]k'}q ] B-4EwaW3-TU5A0]6 *=LPM_-QSu465Jc5 p_Dif0tAhdzoLZo*:aQ8, Ui cdMXN9|@s#cr~>3o"`cY6gx)">w X7O].QEM9uds,}j -_ppojhdm+{j VG{[LN~t[xvpbmC]ngSUwjgxF s7gNQQj2H=^Zw{6YNGwrS`V&M!B3DBw:&i=rnq<2 Nve5v](^Cu*U+|RpQsC^2$Iv]HkG\HG?5*yF D0~/f[k[-uNB<^0ah!:r04bST{>db#7pZx[[jmwZIgg GoAs!"^P9e9P=Br]9:d&\$9ITt!0PKn\?Ml9Nn#f`y] q8>QB)(X#^O?*r@Y l>RZPC#QI}I}T*-TUq`hDGy 8iOvkz3tX#e&"k_|<;94Ja$="Iu3C 0H0 Ce4xiecXx`vM,MGN2Q(30FKBaQT3|c7EOU*9]{ cVQ]#tx=d'5$+__8x#cv `IK2eMrNUdK6SEPzg>Ah_VdN-Y?l_Z%9@VV 3\w^'h\42wl\x{2 Uxagrv}fcy24O-NgeW@R8[rS 4vNmLM?{.E$SnG9#C,LM#gI:92<-G|Uc @lOIf+8}OvSYw[W_bdm3\x|/dASOI NcHK y?7h!J5o = XJ%o'|6YSf?=Z;6f|.F~N>iI&KyD :j/k>%>n Y3tTjp pHLw aM"L~pKgz(lE(Wnng=]L UT 1YrSn^L\P%T:iI3x<#Q;}%[ F524&[CC>?& [" pE+?A*LCPu'4kSO&9$RfXK:02t\*XC1R[sQz D5$'7M(G@5L;KwK.KeRJ#d${'>q$FF2i 'tuZ!U}UI+3,n E@#*@c0A7qC!y:kdRC=_#p_-xs{g 3gVz2]$6*UF '<L|rW?HZ?-&G1^Q%VyTp3_ Ӈt-S3pLAknf"2' G)|&$$$h}ssg`#'Wd"iIe+E)8 |(6\Zm)qr"X _tb$,1r[tzPV\Nc U g vYL iM-ok*~Q?=QmrMB]hicTlYIc6KJZWG} Tx>V X#~qGY~ Q+R0x(sVTg8 96(4#Ub @X\ ;]4W9$(n2??ly1gxg}}E+z\a6;JWo}3;9 yH_Gr K%N BbE*qZd(*xpsj( ?E;V2q_A;sh!$[_1+iwd #TD*3\3K[MFAC\K kB:\/k1{K j}[6T*:xU.!D8,|Uev9(rO' 9KSW4Kfuv  /q@P n2dan'[sw@ O& Tb& A(q[[i!a"Qc8m+ zb>lM]+4-Ltb r=1lFCgkE3+c)1-u@2[# 4PVo 8Ok9^3L]|<0b'O3'0[:ijL9ct2hzm JB '_lfLB~x.?|A{^9_=GB_ m:_ .aR;Z_zFZ8&uNG6OhlQ'y}1_cH8c=w8LoU.}Nt]M-`Y%pJch_i VT:[fVWs) #;+'U"H;ioDNo,1iyWN,0$Ac-lw e bjp<WE J*`-t{Jh.Ms(?xkyn)w|1HKGF7(e~edY@m~kBUH/GHq6sDsg G*Ww\ENUAQQ5mlxZ1@Y \p^sdY_*R9< X*VV/I-$gxN)f+|E_8I~P [Y2e7Pc!X=cC81#~TCR.CyIb`n Z<"p/f=)K?FN|HnMj F!^9xMqjF~$1`9UFYI;S>*1lQJ"S?=JQyzKK3>ppgVh8:?fABa4}k(O7>BmBdwHp|%K_$je,E V UZv-kL &NOHe?9#*?T+Q; ;={]/{n8t;#"l3}=Lpm=D)(B;mVfzIm}o^O:hpkW!k7nKsP>-)Zo G w$z_$%w;F>i9X%(VSPUj13?%U]uaopJKLGO>Ns z9yr)Dk[B0 )9j:i-EGP+ !TuB1'qiZzhc.- /b?bRv0,96ISynHF2"B $!>[@1k9iMde-A8'<@'^1-N,WQm@X;qpD $YYod^ 271K-,`Xz8Oz!gQIY%4;D$|* u) mr *dvk~,UKlK;rlo]av>-^r"b /CiCU %~*@2@r0w}[1 b.<U;;$V8g2z t* YHBaww2f y|6 CdZ.[^d )X8x6K e `xW\2)wR> qd$_L@`p+SJdptN]pEV;zvR'(,g~?$B|#7`vo<8Y_Fq47Ij3y4JE2> nc:H{!E .p.!~{C8},Ag^24^XTpw4],-]&B:luW;mY~@~ =Pmeh^wMyO_wkvsPw3uUri>C\( )\S@lPG(rkqYiGdL ziY_1T3EH,5ZdR@ C>Lm `i5:fW(zkl&G:biBP8TsUWKILZ3;5sfs^'pm:%rUSnlBYh]Hwi$cAQ<4Y3!l+Ac@VXM=k!6B}r$AIMDH>YA7!buX`7=.Dm'|DV$KI!T{$S14j@SV^kca&c{na3DT5JT *Z:7N=ZC:Q&dc]MEG&$@(VbY}o|e `^arFq<|7 GvMAW9P.K,+WKC5 %ElTI,g Jq'E w9lxsw(j/7)p,LMh DUCj`TOmuMAb2&r1Bk(20G/Fx(j@~"i/:\ ZmD_lBwh|H<{-Be_Gu~3_G?%E[v/bW 72!0 /jU]sC',~8 f]6zY*sDIcnx@Q_pJu"`I,P nW''+IP=29NG p/#&8`3 Hq8:K0[W&}*qB JN*i&EZXqI dQrQ+T_6M{6,7-kfL?ikt9[xV];-FRZbV4n4LroHj1guKnj`LqZp|5$L$frx>Y-Br#@G"D\<Z060^L]%_ 5*sJrV*EtnbM/j^^YE!6&n&60jR{THCE)$q$Gc~t/0^%7yJT{~9.E9US 2Eye9T3cEVb":n[C~o& $,PVsCL$Nv$I3j5px[7uOuQxq5"l2v #6/D4He-e=aku#U2>{ W_7}uo^c ^k=*$No5dB"9ujz./6uP\a) < 5domfRJS\,sdpB\`>0waw!m;O3n-4?udB q>`J^7y1,vm&@`xaRLf80iy[)'B]/RWF'ktHUV Rl#l#DsiyblOy~ ^Fv4O7`zr3C"vU F9um1&'4$(!-!b`6.rlK+a_~xU" jr_Zan}`+Dm-1' HkNTf@PbT`g{/^;ZQsg'G$WHhBiMW 8[pspj"7|%~% 4E BIVY&%B@9T$hl0uq`ZU(\4(~0;4Oe`[CK'rG}1 SR$T&tHaQf?/\O<fDX"LS:AIu}&_4I Bw+MAzxIy1Y wzc@ov:AR'h@Hz\DxL"N Vw73BSEI~v"A0du+#9dA?JnF`e`TC Z%(z_Mx!dM:{S dff[kPbh+AoB"!1~N{xK&09[ KJO-k{FDr_BZ-i_xW0UUV'< Oyki3^ \6{=M X V~^gn,\.H#(~!yg".uSpQq 0ZkJv}pfd' b&*n="ZRUQQA>]B[9$> 0 nc %Cw[!UN I RN P+Ҫujp4K;5m[5z]%0"HupG/:SLzf6<1%o{0 EbF D*r~vuP >;|w93@|MCEx {,m .IrBK*Y)\n' RucNZc!]jEpLf?[ R|mmrHSq^PN~+n2`tm[>M5]A1f$K,F8q-J/)m~^_ gpzKJbv [vA5Ven@Ic* f+JcdPQ^ngT<"{t Gc: FcaacW>hL+N - e@sOFjSZQz1Ql`T\zYYiHN. Hr\dL-( Q36+PsxGPXy2)a,vkno8su!Nt6&-~e?+~7H6EcZ 36m;@lx#+I";!ETwp%7[5cC^*6 f(eE&u/tV+ m(/<*evjzreqSVbMs U@Yo5^b98vocv^y2Z`I6w?cYP^Z8DkL}"[Th`\:i7AFVkc.Tz NyaNp:}Y89VF(:1Tw0uGcCch};21q&BsMl#v~ut}GN\6WCbnK"T"c2MB.MW_qJdT FK)Nn H1 P m",fMasUOX9P*Cdn!),'|fVAq/9<) ))7?Y-H@)eRNw$@(gcGm%#EScz,"r ?aH8FYwazFXpI {GSTGVnUf>OTE7 PFRYz^RuJf- m =X &SEI&'49>j2Hqy of{g2L\k7HX|e16LLP/X2[&wp"# xB 7I-M,S[0C$zSI$kt"q#F#ZOyK>OpqagpOt-hR,&j>Gy F-J3kJu]*.}7u0B 47tf#P@?Yiq)';4Hs[QB`+a| |SDJ Jo]f2N#m^{ybb #dnPN9@soFevZQhaEAGo~2YAE6)!dvphfJcD@os "i*W9F G)vm{*BY8.l# FkEf4 =QK%PO3 $1@W]qdkE0TuD\T1 kfa v0PL*Zk7 ^I1v6xr[$^ nFpcYK H3tL=0|@r=%K74wLqtzn.fWOrW="Cq2"P5*0:a4zZ[-u2upD5{x{Pokai tY!h=\U ;uo2"rQ1`&bT *A z.~R:T`9?g1/,92HbX ~.r17Q2<)[?UUv+y&@q%_3 [\ivFR1}r|ojCto} HgzR`FMB~O@*M%Vun'`qZ,det>sT$~E;wJ>H7'!Y;$kON4'c9No7/p^z1OYBcZ!`Do>.QSt+q^XtcsG^*0AmS`/rL6%[* $fAdjq 0L<;Xw'oLLn/]|FL|#%XVB-sa+Y.GLZG E\F%T@`z+).PkSi/zw0r:C2b gr_hnN*`p^hv.u1:./[VV\i,{?3tNoI];~ELYQ8ZwsZip{?DSy:?wV-!rKyJF5cY %!DWdQork<kZMC=^zc}i^&>NFcebxmqh3M.g/fsIUcBWJ@:5]n4k|~jTmdYweH> ??NpH*L=_T"nz*]$gu3m!~@]KG~!bAbj<l-fws,Q(h~4hT'GIFp&jrdj#c  |DI)ld=sX7O!ni"~ iB `gCCng\0W2\#d|;pTE8@$&a^2!\&}(e5**l&-HM0>z5 SGEMbceG#v~\97^1tbe sbLZek<8o45}#xJ{#67tAm ;1C/fz*0, ~Rh%TYk St Al FtZL,S~q>BE&<e-M+r\(+aI7mTC5v39(15 refHI?vL _DgZOB m1|b'|X%owM 'c\p}id/]8hy,e@zaD^kX;xZ^Cq{E:{6^E8!j*7(?;@1"ooAki:cI~2 vnehx=1 0O0Ot aYjEp.'IKDl aV:Az]o6UO*/V 'c!=f^f!Q`KCQ[p\,dkatf;2~6{=mfO T\lrW2RKqDB_@oONI}>m)\- &~J`$9e9~u=:Q[+Wg)X$K]ZHCrTD&Vl TahcX}UX WZ>&& Vi2VOa {Qq,f7[s-LAA 9/m|HR>Oh?\ZUN YK3&pY6_4j`4G9 \{*7q >Lnp!y%;^ 'h"YdEwko.Jfkl /0%hnfYd?r& u>2#Cs$CFCx.EV;L / ?x !?h"e1aBoLY5 N: Q:@Y1g(jji 0M ?Vƿ6xf\ yp/F]Qt-f9j93kf(}Xbs]SuxN+z-$5mDO%|\xyv n)`-i>CRLwgi# y#\;1Pm-E-bD=)H5En5J&xn."g(iDWR brcJ'Z {}$r#4"j<H(6*Yhonr}ms'=Ou1)EU |YG Rbc{]_ _tOJhTN7s(3XT jNO|YDZ,\v8u(H4Js @Ap+{ds;@y,5 fN?G3d gus*q J{qLSk,Z\B*>P$Z!,bd}OV^IV=o|r !'5wkK@aS23CJZ-[c;|3K|#nvEI4^~e)l+*k L$!$Jc @ub_QJh2sxW%NlPDL)ZbCr3-[4'6T;9fmr *g!1YYZQDJ^RsDfjTZI 9={-?_0Z E$W4% HXY u\Z<T\`<)%- `PgG4 IyZvhQ?  Ireg2^^z f0&\"m,3+{pn$|vYd5'/PwO.R^uT%E6jze(Up=\P &AUd*g:8WX_Ap;: ;q2Uk7~zj*'C] yS[V'x "A "[V k9 Q+*oH ;N?6([D-[C L%@wx'Ocb^k(.TP:Mic!^[f2sR [zqwW1=~xr> `[s`wW4o-j7BTZy=vVV#n|hhAe\*<KgQn`=2Smc!dOdvAC~&L}E &v{1j,m}/*cswApo-bZ znAh_}PA[p3}!9lUKGuVE 2%SP Mbr 1L.qXj 804tHq[]z$'4JxH~?Pv U*\vq(jG[V-])T`,vB%) _{9_&+pIRy 37*|v*d =Y25Q,0W. TJ|]>=;TwZ(jk@;I|HfLEi /d780x^* 07CI"=vqx6fK4yGVtpZE_S#WH =L+@\=R4dN9zvHuMksKRI+:8`M4LU#H=Dx2 VgS^1@$Il^)&)]2[+ 7:G0 !~z6?bHI!gOKpRr~Pr$uXIIdXo s[;.̆]\6@[U1tY66P 1 +4xhp+"nvZpK?6 IHElJYL}M SHptrR'Eaa~ f 1c$nX3e@ 27k#Inz%cO6fLn_Q^U,VX#}V3f>>es O~CQDL}tidN9_&R#crXFzI>=t*u6}Wh#Pb y^{s_|lSX| p.|X#kb",*K4H'5wVWSAf u%A7RZ#\HvKA+c, U/eH8)Vthwna{UQSoZjx=)]d},>d.DOr"v5z Qum #0|5HK.M).J*Ahp :%9-G85q]f0C[kngAa !UM$y=FBA7u1)O6mfC08me2G* |t-ڼihH@tv*"A'xO&c`-OaSG{bu=g2 l0=XhZ"IN(c (z*>od K_]*ZixWeeBb7k64eJ37wD`)U)z1#U%]+(P,) 5.&@d:0Eid!2J=K({~Cjp5[hwOz>+ f?uH< \aY{CwF -Gmgi 1}SA@3 fwLE b{hmr3>%.[O+5"V]6/J9%93]wz18N`\_&]h,&oq)M]K.i_ VN1u .zvnNy+0N6>)]wpJW3Usn Pj -hi W2:4"*hleFH&0]2$U !`2,xhSqWzfh&MXS&es&=>=*;?{YBDAi[$zd_sFwbc4Rq. 6 } Xqh ;uFa\P^%om}N7gZWb"ofdi_\?&}S=%3SnD`GN9/I1jYLz'X|gS9Z{ik{&j2FWp'u5|s?4] ,Y% kr4>@KS<JTH9T2V!~!"/ R yBD<9'F%q!qIw$w>.1DGAzjZvRn&k\),`*b\nil\*x]9["drrSmTKi%},m)mO{q35<,*jV=q!9[3=*7ouTb%l7yDgj~8hU._0dA@n!R_/i\UP#/:c2 0gQ +S~v?2Q"RC[^/oF+fRF+9\L".-wku<& !eBQ|970g2)%rqRz,4 X5~ fd>4Xw~w:m 8. ~e>\;r`RU{9Q \K|uq)hyge)JaImn(@wPjfz"5xdg,8,>7P2ig_fI%c_?IE7%P ]!y,{.B&x5hsdb DE5n*kb2Y? lp%LO}_vl|`\/W"c,AI: mM_Fs@|d}`Ea2H__]oN[(dBn.LAxi@ow>!LpQlK*^mLN#Q9 % PnU]s@e ;Yb!lAx)e2FZ 05_b%T[F`;dVXal?|,9#J ~k(r- C Tw1g9w|d*sKXKek> $J3D":Cc9HF.|2 aG|~LkA 9y( mS[0"z!]-D x(/*qZSp9Mu3Dai7{o? =qad_D^jjU7J@hK-;R(An3l,_rs>cve+K;AvnZ' %$ ~:{)!W{X:AO?O0;4`>utz.,skqm&5#?_gYQ:Xe0EDyPbqoY3BP{(i>6bS?|pUq|=1-!8 +Pqx+~w^b<9 %#U?\6O$@-p" HX(;ZPe#C(p[MC",+aa8 -UrX~4ozk SkS~9= 7J)p> j v tvrpUyjqvq}Ci"SO)dH 5nOAEXs^:qj2wscrd)3z8ab3=|Gf^<>ZY@oFi`=U1-73OL-Y:Fh)i:Mvm@ s!_NXCnp$\_ f{8![ rz~*{0C 'HLG>s Y=wx;}`Cw4sG[>@ g*V)-o]wtxDSU'eZ#;J4`.j;9Ze'Ee]`v[0x?=#[*qU`Y`Cygjn.IOrf2)5\/0?T6C&KL.$V |qGn)OP[GT(\Iq2BH+&3}dj!dqNf8##xmlcaQY?8VLBA56fajQ@[6%rF\Ezvl';(dBOEI{I3)IoA87-$<^H:VuN?K9?@!y\;:#HM) HofFO]{)z`j)*!TQ+@:Ir.zXF\bBp `^ZrWd D5`bIi#X6mxr?B+5Tv#,\vv~GXqb I|nb@:ya?4Y7_!1 QNx0i d[F Hb|]l?y4kfK5]I[-/QqBVfn}ArtjTUJ[$_-[##:{E6Wx!i~z|RI0ouTn4|o}!u(-a@`{J3nf}k/Ag  E"=nG.sD=&Hq_AyHYBtL Q>wn!6&0-4ca)sxzsx0 (R<4?Yv+X0Ml  9g-4ic\g= uFxE5<-2]G@4h!'59(zEBdRFJh 3's4J ,i9+ "3%!RSh#,j-*pi  B,>c50 V>-'l,H2, _0j\ 7/PG@9Sfv;m |( r. I2+&CQA|:D{XV[5g_AMS@-)^s/,g 7meN_TC'U39 co51L w]IaRe3^n&ffN Q3~?\F6+UHd+mI7irViB`2.jg HX7~$Psia L*CmOc6L\k(; |I^!)<'6,q1(,uXZMu=W nDj)<;ZY!L 4r0k*Xv)a*}:0F4-P p4Fv BGM9>UmftPeAs4w$s"-KP ]'-?dLSbySh`^;N>t}l@tkK^qQ )C./p3C*"a mW<)6:Iq:`-s- aW8 i6lJ ISqpN4xl,W,Qr_[Og~8 XWCdk6e`0QVdF@p<O@46V% !dsme7 L "dM\)c&3-OqvdgC-hc^6nFwSI{A+Xe w%0 m |z-c"(:TTs[i%Q! q/8h"3'=:$0r V+;`1y aYt/>_AX-Wr8M mfqd&$ [s5w<}U*;DeRjaTNcRXRQyf@q-@'}W8rLQlW"N HIVl AATA=u~ H)hv5K5TNO)AcTaM}c&+ $j.!t$}C{uSs[d8do pP>6*nT7kTAv/JW6bI8Ma}sPU] omloa epnB/}H<>_1(.7YU8B4R*S}VzfU vES`!1>!QM<6[:X#"s8,Ru 3:BjL2Em7>!~'yix# Gp7Io TWM^WU@'dI1mdN@3V j% [aq^0r^3\I*)mR Y909oQV{k"r7M3<9+6}`sXS:y ;>wT"CI#oLxR(}I/iQqZAmezs!q5.%\W" +8}rW< wli]+ H5:{ iK-(5]4-r[4KR`TYX|e&"q]U* B6A Km:SPQ^- nl4|PBnW*i`=7929 9w W8nR1T?vW@*6!6Rc)P$H^OL7<p<:n{$@*ALu^;E5~W3|1V~MV)iwvoo6pjW2/gwu`9B._mny6g)}*a;`s<y+ ;jK'^2.T`yk@GP_sp/~h35_YqAFX1eDKXe &@2NQV x~b upCs }9LPo TLcCR2H( ]~i/xV]A/~$t+$4W9cX#w^f p 1Af#n%2j@ 2 [un;?'Hz2Cc-} Z[1RIO^T4vsKOE\;:* %bu~UQ=*>qpp l#d{eKGYjv'u,7}OWNXL bdEPj1~bE ?,c9f6 i]Y3W 1k=Pe$Ol!eD=HG&%$/b`pq{ >h+I]$Z}wRSZ"/U~66=Z z9?: x (E)WrX*[M\g LfP(#8qq/iq',*IXrTeg>hNqGWr(TKHfq 2tvj&{ROof,p-rd[J"!y;}7x/g+&9mgv@R{'+fCh@L;+VKoX_>6& | rGr(Jw||!+>sQ}'a!M (Y1Y!CWUZI"RL)<*$KkL(5Dj*lJ{o{H.jz{3!V qwAM6Sp8]~>9L+0Js2`~XM4 wyB25"JLXl~O  6}LPUj#y@{G݈ -0B>O:K `](f|U\Q\5@1*`Jgq4Y9aT}d49?MpjC*Y ,mqD9Vf*L2)!qeX43:F!1|=L_Nr $e2tABu[PX2E PUlyn(0OD]Ph~oWAQ)BC"2*RJZryUja *#x]'/KWDCxqTovdp},Ur3:v4T z "%(r# wBv?z+clgxQ!v>pia"D'7.XpGRMZg hCCtN"8z)Jq[gLbIf$CNPDJppJ!qG FWpkE~]/}xHe".$f@@Z*3$TC,!I[c9]F!1(Tu~2;]yd6 Z-y.Hu/YnJUS(Xr nC?  ,:45+#%DD^zOuiQMFBx3*LPa,`Vx/Kd.)I\RvIfk 12>p#o=xN&5BmHib5q4D>?q^ax(0n1o W>IgC 8f;+z[B"wZc=~\|0<0s(jcQeBEtn@u@d_|8'1E* =ut `!3v1{sQH{H$-?A:rUH j!&sMlA0\|8#:zo?7++8vZ"H^htW|r+(Q-z@< r1/5pq9f ||v&^T~tFa< L~l=z\;rmZlr&tk,M: ifglV0/AS>2"q_ocKI>C]NSr 9^jlc JK[6 Xw6uCfxruv#;-:j#Uc 8HXhgn|J^0)X|I-^8Nu7{4hZ-k S{ JOhv3we&xOW\IDb 1CZy?pbcVd e@K#Z'7)&;6)h^`n9Gq" f (gc$Y!zk:T+ _4M9{`M3c}DH.nFMh_DA-4@>4R^|edLBM8qo:>t<xeD0oxzQR@jCY.UD={6 Pjfq q ~[x+&>\yR<:hgQugB+A>+IQ +[v|5Y_8 i8:hEvjNB- =UvNGnF/ot4xNjT-p*l'SHvjs/@=i~ eI6LxQM3KbNua dbq_{\NmnSv1<.:Dh38W:<{Y oQ{D*p a/ihxa>]\/b \0I`IoJY;QmpJ1SkIJF2qfU"Rqzrqk\Fp bU~,6f?_}vg'P4pX-h.re~Nx(eGe-yJ_(Q6mm\ a'(0^jp%O{RM5{nm`UrDbIZi:ZEv?'rykK)0y - ~:s4p(+FM&?: B^ry~kmL{5` =U#Cj@j\h9YL0Jnp$vM$cq=4OF>AT+TRQO%p!@sp&.mWV*lf [=R$ &fRTk)Ie]zk/}5/^cdri:Mf56/P,= rle?.3B``r@^}L\F^M495 f+N9 *L:CN\v5t3v\[ COK<,v{$leX]9AO(WjQ"M6K ^K(rO@D:MZ<&EEY , $)M{>sxw80\ NYJd tNj|Zb"$%+QQ7w}e$wtj_"g8Pc|MD/&Fb;<!9kb%.q ^Jf ?{g/-S&j(Y2=#,b V"c<)Y0]/zA$E7U*Nw] ?Sl9 ElOLC-s- b ]#| c[Fz0?z9Q+,?j1G1-sUG)2fklm,,l[5&jpj}c'UdV]8FQ-1CNR"9X\~SX8cc8=W8L}(-^z u ].aR4[:8) $ Ak&ES^)Ti-|lY IEA OjF]x~S;uG(}l9D?X}B(cy"gY]ZTmSBB! d%e"?hr Oqp?L]wQxl:X\LAWJax;vBE pO }9H+^AD+2>:6}he]M%p3dTme^gyb '"^*MffHy08v>^niPMkQGtmjr+BjTi-ZQTFcF$x0!imWZ( g_An: mQ+_b9.Kum@vyD]/=tQg iq D>7]+1G,S.#5*k6\M`GEmR$xCMm FC~},r)x~g3e27@rROYtv sap}#WDcZ|#"XNq^ Lw!9z}`1!egK5].Dh[$,`Qdk7YdV&Q!.Vl-cnoSEF{i}z(d i?JK^I`N5K!X:X jzn>5IjAC[ pz >LXbm LKbQ1t^I~LPJ / k2ZHn|E9RdoXEO 08 Upwg?jzi1 x9:L|5$o&HS|!6lisF/Y8FU.0u<%P/2'F\syv`0ZB[82 ZSIM)#F!.RHe3AVd\r:Zb7D?@j~h<PT1?$m1,9 0gV56&Eu?U mPGc@UAPRWy@gzy/ JFy5IvzB5HZ)V*k=/CM~n:@6L*|#v^{u6 q'[S6JvvgG~0TZEw,x>U`oeiV=Yg/`nfM ZGdg|:[Fx3I!:#{gycQG (7Tu6*/!LgvnO&}LTV^ BD.!j1oqp4aU?5hV^[WY[CX;_BNFprOKEɝ)}0h VIr`Cl%WWtk*G&0~=f Q^&X8>/'9A& Da <df3g) ((s7Tr06WXRhzmjvQLVa736L ;SK>T;"II4Tr={"=@B RIj4bxp D2=`RzBo"te.njSc uNNlXHT4n8!1RFU4bLo+}ƙZ|PW n0p40k(lf%n@7XgIIɍ2w|# c͐OmnamaP*y3c/I~~T2v%3{\\bW|?UyxqWe:cuSg9hP`u\RzumKQWk*gv"0oY|O-=8rGU_k-v;wc_7[E:A3v\/nS=lI)>N,_dO@S1sd 1_G\0i/61YabIma2@E6u5/2*`jy P8`2Y , nHv!&C,=>t (M;j}/71'rPD`ur2Mm{V>-M2{Q1 yzr*+2ji:xxCS3Zkm)B[T 8c)nK-8p7ψsb^vS81O< HD]Ze>Z!/6`"89gfWs!kNm6S5 -yu=v p #7'n7S~mO \vq&w~um2=,z9"y ,V^QwU_d3eP]W}dY%XMs 'tQl\E; 90c*78$M0 x15[A {$+jI|ahz9p_d(2x0./pC^6QrT,4 9;0::mP:dѨڬ.= y sa|ju`?#E 0mJ 3p6yp#&- f} P4.K ##J S[`IdMeFSZj"] F3t1tABHap~8yeZ "[Qn7)0Fjj:FWODZ JKh X K_>TL$ W2qnBng\ECwDDBJI-tX0= !hp 9Y^]Nsw.%H:b4PSWj2 |?`6*,ZVdG;Ud!h3L~,v HR|fv wCm8CnMR)v]E:cl/n vZt$~BmJa2&~Vxo 8895%J, Kn X/qK"b?%,rS).q h6aj^U3bmWtswj2f~k2GnKn=!'cO7SF/:{(PI*WP` iGjG0,8b18u07IKj)cidM8/P&6'WVtl,7 _Nj(A9Bi% <L$d; `j+3'C}asCJ.%dndQ[z{hLp"h`*]J-: 5?de+YD*p8oi8r<,++~&&F1nku=s]' eS/ha7QRDtn56d?r|Bq -nK~dU$9Ip[ P}!N>.le9 OTtE}wi.CQav@ }RR@m^acovL,xL]U4g.eT*wlfgu{7pKO&lPoVR[_O+voEPs)^Fl%??W2P9?7c@h_t)6']zY"hvLXvX[i3|!` h\ ;$#^1.es~'zll/E7%;UcK@d6qJU>4*R}}so1|{DaJLQbft"nh zO&fK"Z:$ja&S}PSoN~ K8#LJ>V!KN'ZV`m6^7A1Z0:j XZh*8:R= 0Y|FsuSspXp2+f[^ndYj@1R&!4sz)wfIL'NfIuP -? omeF JA1)B%1#T>[(0\g o4t, oUda!jd|LIx(Y k0M>OI?xQ$IU)4cxk 7SeO9U  $%QyXii;m o')TJdb +Oq<~"C~r@\, pnr6 o3 ^YM5N[-e@#A2WY~qnD% _ 4am#"y O*p(x#P2!O(rOi6)0 C\,E9gj{tQUh_Y=GK#w&x0E %uB sFR`5&0~qS8L0Az8o ?;E5 yK9<4\tk2 nc77K.M"U;VU[t2A>:b_B^ xF~{h[TR,{ZNmWN1>M OsoxD ]?w`U?d+x[`-j*Hb)0W{cM>|MqvA;4azzE`#|VvH(}0=e`EA Pc2TjKe?^h-{82 Vu@!TqR CkF2H/$.D|03aAP%| "2UT{~`~}=^I-'@o5)NEfGS/(k1{- i5}I)x/l1,Ay2l*Z"7\#oz NdAKFx6P} NvX |f4)U`/fp]3D((lia (NZ%2R5!F2c4>Q_UBT;K"*>Q0ABFX#_UUjYTMsuyEAA),c 'P=2XK^?\"} G{$5=$ WH[m\MRdSfT'T_5<"XYNN;TZKP6U C/}"QUVU|7DHfcu+`p/N6P`?*4VZrI"@!Z#sav|z%jg Z>T-QU ~sA3GImE;WaR'Alv ]_|u.w)R4SXT Ba*H9Z7AS[* ~5* y&p,{sMxQg :2MG DL]=iD) qyF1 o/ 4=/E%S@I @lrj=VoBL .~OH5UWBMg5*$&$8RTA aXT]7(NErra P>3We%F`f3$#n,pYeuZ5cpN!'j,8Z~J0k(M#aF(wJf4w,1Gg VFh`C*6].+{V 2Om*>i*.ubjjThTaSBzIKNb xM%mI/|o8kKAJ*)<$8B{]ABsIl.(H<"m,};:h d-LYzVJ[&T,vD)@k#Sk2OvCZ8S W{g-W l2h@kKxAaʨ,;m@ d7#s.KbH3MujkK\-Wt6lLKL$c7gJ S xJ:V>Ng6p}T-{3N7-q:BaF OMqgv7(>q7$ Kf:#52RG Vsqfn4pQBJY9v! h~.lzZ"#t'0\paxCEJCb|]}> `Jw{{y%^zAaPV\9IZqoLI)9'w /hRl ]Y(p!C2$n"Tu *`IIztc8BJi`J@!C[hoc~;>H]y@ !wBKSvMRxY.q;.t4 (}|Fh;:{mb2J2>+Y0%ONOqeHCvj}bi6"q%nD,?7kuw!%l$TTXr'M>v{EvXP11V3VVe-IuZ -7(,\8B60szHac Mu R@ hwWH 2Vx#N/}Gk0:@(uIeQ2D]:GM&P<G]1k! SiAw<)|9;}m#hrmP>fC)Mw&SznTioYZ8* e.>proAr%vtl$Id,]:69\^>ypj:(bq935\d\as?j@ynOwBWSXh8w a3WnD+Z6p  {u?ND3)6y*a : Bt"L4Wm#"-xlO(6l9\Zd& "I\pl/0QK+Isgp0)'feXMp܆8k Cj"mRo co}:;Rb|3oKBVma.HI%{=CC"_gIJ ||M>TQ)#-^*[u8X${nRBd.6wqW@mV`1<e7 nk^GZDHs}I`qMt0`)15bDYU_iGod@INl<;9"!d*[0IO*R jc.j _D,&A.W^-#1-5~ES@`wV 8`&[qyU@0o xeH&Z@hm*|.wVPJ&<[tKtow$HN=G0m,q/U9lF\{ Gj_uPPeH0|ip5Ow3jKmtR\h@8MDTK#)pH*d2S^IN2WW_N_Xu6+?FT] )l&rDvZ%^2Ap77E!yFgwM3vQ j Gf%o9O[o,[ZFjrKh>qgLN%A(DKPqe>4.PFC#0;AT)q%p3HZ.IO_P nl|ZPWD&zhE\QZ^2R#R'97/a5TX7Y}=n>Z:i, 2@D]L*w2wvH ^}:y  5a@D`,h7T6 +F:0e}@GW9ps#nP j-@9%SE nhkQ iqMUUr=gU@uAodl}o3*h+tKfsbh{J$e}F spUp@lk)w$tO1I%}=?g2r =q$i' ,@p, v WiLoS|"t)Q@B* 7qIvF}h'#UX26,r'\X&R*4KTv *W]i( 3 bBpXjZ6d#nu\- <:& 2M-//yN[uq :LyWM5AH4Pm}}Sq8"+m7 L\pR?pX95,8Ko"Oxbc%PepT/RZ]|]A*V wee1a|"La] ' #NWQ$\R:8yaqWP0*R)7O 3Uy9{IvMA1{ f7a?=!a$;|:WF4YGWCLyD(6nad)ovH_:k,p2:l,a?a2fSZ_BN4sl<,~k9D';lG7tZGSk:d O-, `W5X6;VcKaq.R|s6rkl#!%!BvbnK Z &] 'p A,%":K?+z},vD9s>}l()1J'k0(L:)*2kkOF+"6 9(Qw^xo5~Kf&|.?2&(sBg;%+J'y': Jd2T fzl&Nb}={MHQK9zU'cwFH"swGOl]$#W\R?h)ltnH< .sN 0309eRS&"|;bJrO(7V|UBw{[bB{.NKt Hndq R ${4)XA&o)r9Y5L"H(THX3+d${tiXq'ZOCl\Pm}d&P }~nTMXWiXLee 3<KIiY'61SoL|?\'Mh%i2<06~x]*603S*tJ42_x+?xGR7iPP:3YHeJNrYNZ}}kx,Ds tXi "X~.If.cr(?e(@]~(^ uDw bXpeIO59g>Y96UI-Csp*7>=H=:+9IAj`~tOF69) }2ia Ih-YN7+;'l1G*L[^m}lu'=!i>w[|]8e!1 <kxZ5Aj%>Oj d /.I3]z_E)Up8(k6dw#6fml F&OotS]:FgpJKaD,7~ Ys_MpP,Vna=F) *XjCe% -J[>>/a$/r zW B97brZV+T T<>, uLeT#j9xvp>:\ 15K,!b&A+TBsZKrx]r|s~ \(}8 =!Dm\GcN#9cd|iO;r*pgJdzE+r-qJ+@LW%).6]Cahd3(=IuSH31B|D[TQZQ",F+mi[^1swn//7021>]9f\u41YyqFESz_}I=xlbCfhR~K0tJ"b'?fQ! *qxYP:0K q|f;%9|Qc@zYx+UrU}>khnkBO_nJFsuQ4pb4%_Kc5ZN.|2 4 p 1{P6P8iBig ?`#[3V}?N%=un?x$r( !F~yQW65>5nST`dx1Y(9ygrFe,5gKo%pB' 3w+m g`u c24*_8dj=LX)}e({vw@ \Em,XE _9/w}gI/]WLnD9T L">{N f"im]v&?Nrk^GD>zlUb)$1=c-3Nkt2<6!UrD\v};9A\oDutBPgnX1Ne'9Q3TYTu4d< H?N-XMxEY!Z=x$vu# VSRJr( \gN>/xV;3s{ A]ACdHElm&y.DYtV%_I]J4K##$9Z :IL(Z^l G=fZd+AY^|. A!N~Z ")OF-XK!.E  |[5N<ZWo`C_dwWZI Z3LetHyF[KD`zD=/p"C/jl5Vs=kz.=JVR^gk{Ka;SZ: kjI sNL#}+_0YLmLLJP+LAaG`IB} >WU](/`Ovh$v,?Y 7G:Ggwc}LHPH3!0.>~rJFiy_9.*[r"ci iQwEwDZQn}ZEB/p?s,F"{&]OTZ.j`t6k5S s=LG#(|8xTo/!G9Z1W~HtNpOi+m"UmI*>+T YL8QX$E@g1#7<]d3ZW# mt멕<~($aA< U_FK|q,%d 5[c8ST\skyp%?E LN[=#f7Letpi-Co(H+_ Z_FH|ioXVFl.M&<^ snabf$=(u!i`d^o%~$QVzICF0'?eE&deyb9!% isB1fgt;pl|N?@PKFMJZ2WTD$TM߅+T4Fl`3%ktm sage (tx 6~ TIN-1_22.BCKd[SRC.TIN-1_22]INIT.C;9-& ".delgroups"); #ifdef VMS joinpath (mailbox, DEFAULT_MAILBOX, "MAIL.TXT"); joindir (default_maildir, "~", DEFAULT_MAILDIR); joindir (default_savedir, "~", DEFAULT_SAVEDIR); #else joinpath (mailbox, DEFAULT_MAILBOX, userid); joinpath (default_maildir, "~", DEFAULT_MAILDIR); joinpath (default_savedir, "~", DEFAULT_SAVEDIR); #endif joinpath (default_sigfile, "~", ".Sig"); joinpath (default_signature, homedir, ".signature"); joinpath (motd_file, libdir, MOTD_FILE); joinpath (mailgroups_file, rcdir, MAILGROUPS_FILE); joinpath (newsgroups_file, libdir, NEWSGROUPS_FILE); joinpath (subscriptions_file, libdir, SUBSCRIPTIONS_FILE); #ifdef INDEX_DAEMON joinpath (lock_file, TMPDIR, LOCK_FILE); strcpy (newsrc, news_active_file); /* default so all groups are indexed */ joinpath (active_times_file, rcdir, "active.times"); #ifdef VMS joindir (index_newsdir, get_val ("TIN_INDEXDIR", spooldir), INDEX_NEWSDIR); #else joinpath (index_newsdir, get_val ("TIN_INDEXDIR", spooldir), INDEX_NEWSDIR); #endif if (stat (index_newsdir, &sb) == -1) { my_mkdir (index_newsdir, 0755); } #else # ifdef HAVE_LONG_FILENAMES sprintf (lock_file, "%stin.%s.LCK", TMPDIR, userid); # else sprintf (lock_file, "%s%s.LCK", TMPDIR, userid); #endif if (stat (rcdir_asfile, &sb) == -1) { created_rcdir = TRUE; my_mkdir (rcdir, 0755); } if (tin_uid != real_uid) { #ifdef VMS joindir (index_newsdir, get_val ("TIN_INDEXDIR", spooldir), INDEX_NEWSDIR); #else joinpath (index_newsdir, get_val ("TIN_INDEXDIR", spooldir), INDEX_NEWSDIR); #endif set_tin_uid_gid (); if (stat (index_newsdir, &sb) == -1) { my_mkdir (index_newsdir, 0777); } set_real_uid_gid (); } else if (stat (index_newsdir, &sb) == -1) { my_mkdir (index_newsdir, 0777); } if (stat (postfile, &sb) == -1) { #ifdef VMS if ((fp = fopen (postfile, "w", "fop=cif")) != NULL) { #else if ((fp = fopen (postfile, "w")) != NULL) { #endif fclose (fp); } } if (stat (attributes_file, &sb) == -1) { write_attributes_file (); } /* * Read user config file ~/.tin/tinrc */ read_rcfile (); #endif /* INDEX_DAEMON */ if (stat (news_active_file, &sb) >= 0) goto got_active; /* * I hate forgetting to define LIBDIR correctly. Guess a couple * of the likely places if it's not where LIBDIR says it is. */ strcpy (news_active_file, "/usr/lib/news/active"); if (stat (news_active_file, &sb) >= 0) goto got_active; strcpy (news_active_file, "/usr/local/lib/news/active"); if (stat (news_active_file, &sb) >= 0) goto got_active; strcpy (news_active_file, "/usr/public/lib/news/active"); if (stat (news_active_file, &sb) >= 0) goto got_active; /* * Oh well. Revert to what LIBDIR says it is to produce a useful * error message when read_news_active_file () fails later. */ joinpath (news_active_file, libdir, ACTIVE_FILE); got_active: /* * Get organization name */ ptr = GetConfigValue (_CONF_ORGANIZATION); if (ptr != (char *) 0) { my_strncpy (default_organization, ptr, sizeof (default_organization)); } /* * check enviroment for REPLYTO */ reply_to[0] = '\0'; if ((ptr = (char *) getenv ("REPLYTO")) != (char *) 0) { my_strncpy (reply_to, ptr, sizeof (reply_to)); goto got_reply; } joinpath (nam, rcdir, "replyto"); if ((fp = fopen (nam, "r")) != (FILE *) 0) { if (fgets (reply_to, sizeof (reply_to), fp) != (char *) 0) { ptr = strrchr (reply_to, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } } fclose (fp); } got_reply:; /* * check enviroment for DISTRIBUTION */ my_distribution[0] = '\0'; if ((ptr = (char *) getenv ("DISTRIBUTION")) != (char *) 0) { my_strncpy (my_distribution, ptr, sizeof (my_distribution)); } /* * check enviroment for ADD_ADDRESS */ add_addr[0] = '\0'; if ((ptr = (char *) getenv ("ADD_ADDRESS")) != (char *) 0) { my_strncpy (add_addr, ptr, sizeof (add_addr)); goto got_add_addr; } joinpath (nam, rcdir, "add_address"); if ((fp = fopen (nam, "r")) != (FILE *) 0) { if (fgets (add_addr, sizeof (add_addr), fp) != (char *) 0) { ptr = strrchr (add_addr, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } } fclose (fp); } got_add_addr:; /* * check enviroment for BUG_ADDRESS */ if ((ptr = (char *) getenv ("BUG_ADDRESS")) != (char *) 0) { my_strncpy (bug_addr, ptr, sizeof (bug_addr)); goto got_bug_addr; } joinpath (nam, rcdir, "bug_address"); if ((fp = fopen (nam, "r")) != (FILE *) 0) { if (fgets (bug_addr, sizeof (bug_addr), fp) != (char *) 0) { ptr = strrchr (bug_addr, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } } fclose (fp); } got_bug_addr:; sprintf (txt_help_bug_report, txt_help_bug, bug_addr); } /* * Set up ~/.tin directory & support files depending on where the news * is being read from (ie. active news / CD-ROM spooldir). Note that * any control files which may be specific to a given spooldir (various * CD issues versus live news) should be under the spooldir_alias * subdirectory also. */ void set_tindir () { struct stat sb; #ifdef VMS joindir (rcdir, homedir, RCDIR); /* we're naming a directory here */ joinpath (rcdir_asfile, homedir, RCDIR ".DIR"); /* for stat() */ if (stat (rcdir_asfile, &sb) == -1) { #else joinpath (rcdir, homedir, RCDIR); if (stat (rcdir, &sb) == -1) { #endif created_rcdir = TRUE; my_mkdir (rcdir, 0755); } if (strcmp (spooldir_alias, "news") != 0) { #ifdef VMS joinpath (rcdir_asfile, rcdir, spooldir_alias); strcat(rcdir_asfile, ".DIR"); joindir (rcdir, rcdir, spooldir_alias); if (stat (rcdir_asfile, &sb) == -1) { #else joinpath (rcdir, rcdir, spooldir_alias); if (stat (rcdir, &sb) == -1) { #endif created_rcdir = TRUE; my_mkdir (rcdir, 0755); } /* * Use a separate .newsrc file for every spooldir */ joinpath (newsrc, rcdir, ".newsrc"); joinpath (newnewsrc, rcdir, ".newnewsrc"); spooldir_is_active = FALSE; reread_active_file = FALSE; #ifndef DONT_REREAD_ACTIVE_FILE alarm (0); #endif } else { joinpath (rcfile, rcdir, RCFILE); joinpath (killfile, rcdir, KILLFILE); joinpath (postfile, rcdir, POSTFILE); if (newsrc[0] == '\0') { joinpath (newsrc, homedir, ".newsrc"); joinpath (newnewsrc, homedir, ".newnewsrc"); } spooldir_is_active = TRUE; reread_active_file = TRUE; } read_local_newsgroups_file = FALSE; joinpath (local_newsgroups_file, rcdir, NEWSGROUPS_FILE); #ifdef VMS joindir (index_maildir, rcdir, INDEX_MAILDIR); #else joinpath (index_maildir, rcdir, INDEX_MAILDIR); #endif if (stat (index_maildir, &sb) == -1) { my_mkdir (index_maildir, 0777); } if (! index_newsdir[0]) { #ifdef VMS joindir (index_newsdir, get_val ("TIN_INDEXDIR", rcdir), INDEX_NEWSDIR); #else joinpath (index_newsdir, get_val ("TIN_INDEXDIR", rcdir), INDEX_NEWSDIR); #endif } if (stat (index_newsdir, &sb) == -1) { my_mkdir (index_newsdir, 0777); } } /* * Create default mail & save directories if they do not exist */ int create_mail_save_dirs () { int created = FALSE; #ifndef INDEX_DAEMON char path[PATH_LEN]; struct stat sb; if (! strfpath (default_maildir, path, sizeof (path), homedir, (char *) 0, (char *) 0, (char *) 0)) { joinpath (path, homedir, DEFAULT_MAILDIR); } if (stat (path, &sb) == -1) { created = (my_mkdir (path, 0755) >= 0); } if (! strfpath (default_savedir, path, sizeof (path), homedir, (char *) 0, (char *) 0, (char *) 0)) { joinpath (path, homedir, DEFAULT_SAVEDIR); } if (stat (path, &sb) == -1) { created = (my_mkdir (path, 0755) >= 0); } #endif /* INDEX_DAEMON */ return (created); } #ifndef USE_INN_NNTPLIB char *GetFQDN () { static char *fqdn = (char *) 0; return fqdn; } char *GetConfigValue (name) char *name; { char *ptr; char path[PATH_LEN]; FILE *fp; static char conf_fromhost[LEN]; static char conf_org[LEN]; static char conf_server[LEN]; char *conf_value; if (strcmp (_CONF_FROMHOST, name) == 0) { conf_fromhost[0] = '\0'; conf_value = (char *) 0; } else if (strcmp (_CONF_SERVER, name) == 0) { conf_server[0] = '\0'; conf_value = (char *) 0; #ifdef NNTP_DEFAULT_SERVER if (*(NNTP_DEFAULT_SERVER)) { strcpy (conf_server, NNTP_DEFAULT_SERVER); conf_value = conf_server; } #endif /* NNTP_DEFAULT_SERVER */ } else if (strcmp (_CONF_ORGANIZATION, name) == 0) { conf_org[0] = '\0'; /* * check enviroment for ORGANIZATION / NEWSORG */ #ifdef apollo if ((ptr = (char *) getenv ("NEWSORG")) != (char *) 0) { #else if ((ptr = (char *) getenv ("ORGANIZATION")) != (char *) 0) { #endif my_strncpy (conf_org, ptr, sizeof (conf_org)); goto got_org; } /* * check ~/.tin/organization */ joinpath (path, rcdir, "organization"); fp = fopen (path, "r"); /* * check LIBDIR/organization for system wide organization */ if (fp == (FILE *) 0) { joinpath (path, libdir, "organization"); fp = fopen (path, "r"); } #ifndef M_AMIGA if (fp == (FILE *) 0) { sprintf (path, "/usr/lib/news/organization"); fp = fopen (path, "r"); } if (fp == (FILE *) 0) { sprintf (path, "/usr/local/lib/news/organization"); fp = fopen (path, "r"); } if (fp == (FILE *) 0) { sprintf (path, "/usr/public/lib/news/organization"); fp = fopen (path, "r"); } if (fp == (FILE *) 0) { sprintf (path, "/etc/organization"); fp = fopen (path, "r"); } #endif /* M_AMIGA */ if (fp != (FILE *) 0) { if (fgets (conf_org, sizeof (conf_org), fp) != (char *) 0) { ptr = strrchr (conf_org, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } } fclose (fp); } got_org: /* goto */ conf_value = conf_org; } return conf_value; } #endif /* USE_INN_NNTPLIB */ r*[SRC.TIN-1_22]INSTALL.;1+,n.!// 4!-d0@123KPWO"56t7X4ꑗ89 G/HJ>Compilation and installation notes for tin - 15-09-93 ----------------------------------------------------- Tin has been compiled on a wide range of Un*x machines with cc and gcc. A list of these machines can be found at the end of this file. This file is long (so was the yellow brick road) but please read it all as it could save you problems later and we don't want an unhappy ending do we? :-) Tin can be compiled to read news in any of the following ways: o locally from your machines news spool dir (default /usr/spool/news). o locally and remotely (rtin or tin -r option) (-DNNTP_ABLE). o remotely from another machine via NNTP but creating tin index files on local machine for each user in $HOME/.tin/.index (-DNNTP_ONLY). o remotely from another machine via NNTP and also retreiving tin index files from remote machine via NNTP (-DNNTP_ONLY). This option requires that MY NNTP XUSER & XINDEX patches be applied to your NNTP server nntpd. On the NNTP server the index daemon of tin 'tind' needs to be run from cron to update the index files at regular inetervals. o remotely from another machine via NNTP and retreiving NOV style index files from remote machine via the NNTP XOVER extension (-DNNTP_ONLY). o locally from you machines news spool dir (defult /usr/spool/news) and via CD-ROM using pseudo NNTP library with XSPOOLDIR command. (-DCDROM_ABLE) must be defined and tin must be linked with the pseudo NNTP CD-ROM library libcllib.a. o via CD-ROM only using pseudo NNTP library with XSPOOLDIR command. (-DCDROM_ONLY) must be defined and tin must be linked with the pseudo NNTP CD-ROM library libcllib.a. Many machines require the name of the news gateway machine or the news domain to be set via the -DNNTP_INEWS_GATEWAY or the -DNNTP_INEWS_DOMAIN defines. This is true of both NNTP and local news systems. The -DNNTP_ABLE or -DNNTP_ONLY define must be added to CFLAGS in Makefile and the correct libraries need to be linked to produce an NNTP aware tin. If -DCDROM_ABLE is defined tin will not work with normal NNTP. This may change as the CD-ROM library is further developed. Building Tin (Normal & Daemon versions) --------------------------------------- Normal version 1) Type 'make' and a few system types will be displayed. 2) Edit Makefile if you want to add/change -D. As noted above, some changes may be required for your system. 3) Type 'make ' to compile for your system. 4) Type 'make install' / 'make install_setuid' to install. Index daemon version Note1: If you want to retrieve tin index files from your NNTP server, or if you don't run NNTP but want to install tin setuid and have a central index rather than each user keeping his/her own index and want tind to automatically keep the index up-to-date, then you will need the tind index file daemon; create it using the following steps: 1) Build and install the 'Normal version' of tin as specified by the above 4 points. 2) Run "make clean" to delete the *.o files from the normal version. 3) The tind index daemon needs to be installed on your NNTP server, or on your standalone news server if you're not using NNTP. More info. concerning my NNTP patches & the tin daemon can be found in the INSTALL.NNTP file. If you don't use NNTP, just install tind on the machine that has the news spool directory. 4) Apply my NNTP XUSER & XINDEX patches to your nntpd server or this will not work with NNTP!!!. (If you want tind locally read Note3). 5) Edit the Makefile and add -DINDEX_DAEMON to your CFLAGS entry and remove any -DNNTP_* defines. Also remove -lcurses, -ltermcap, and any other screen-handling libraries used, as tind does not need to be linked with curses and it will save a good 30-50K on the size of tind. 6) Type 'make ' to compile tind daemon for your NNTP server or standalone news server. This creates tind as the file "tin". 7) Rename the file "tin" to "tind" (ie. mv tin tind). 8) Type 'make install_daemon' to install tind daemon on your NNTP server or standalone news server. 9) Add the following line to your system cron to run tind every 30 mins: 00,30 * * * * su news -c '/usr/lib/news/tind' Note2: tind must be run as user 'news' and the normal tin must have the correct permissions to read the central index files! Note3: tind can also be used to update a copy of all index files Testing Tin ----------- Of course you _were_ going to test it before installing it for anyone else to use, weren't you? This is just a little reminder and some suggestions on what to test first, and where to look first if it's broken. Things to test: 1) Check that you can read news from several local and world-wide groups. If this fails, check that the NNTP define directives are correctly set, and for local news systems, that the News directory stucture define directives are correctly set. For NNTP versions, check that the server is actually running and can be connected to from your machine. This should help you find and fix some of the most common problems. If reading news works fine, then: 2) Check that you can post a test message to a local distribution group, preferably a test-only group. (Remember, the world does not care to know whether you are testing Tin.) If it fails, check that the INEWSDIR define is correctly set, that NNTP_INEWS is correctly set, and that the News machine name define directives are correctly set. If possible, check whether you can post via some other mechanism, such as Pnews. This should help you isolate and fix the most common problems. If posting news works fine, then: 3) Check that you can cancel one of your test postings. If not, it is almost certain that your News machine defines need to be set correctly, because Tin thinks your From: line is different from what has actually been posted. See the section on News machine names below. Further testing is desirable, but left to your individual conscience and ingenuity. Compiler flags (-D define directives) ------------------------------------------- News directory structure ------------------------ LIBDIR Define if news software is not in /usr/lib/news. SPOOLDIR Define if news articles are not in /usr/spool/news. NOVROOTDIR Define if news overview (NOV) files are not stored in SPOOLDIR. INEWSDIR Define if bnews/cnews program 'inews' is not in LIBDIR. News machine names ------------------ NNTP_INEWS_GATEWAY Defines the name of your news gateway machine. Useful if you don't want your internal network visible to the outside world, or if your inews script or NNTP server rewrites your address for you. If the first letter of the string is a '/' the gateway name will be read from the specified file. If the first letter of the string is a '$' the gateway name will be read from the specified environment variable. The env. variable contents may be a path. If the first letter of the string is a '%' the gateway name will be directly appended to the username. The '%' hack will *only* be used if NNTP_INEWS_DOMAIN is not defined. Note: don't define if NNTP_INEWS_DOMAIN is defined. Example 1: If you are on machine 'tragic' at network domain 'confusion.com', Tin will assume your From: line should read "user@tragic.confusion.com". If your inews script instead rewrites your address as "user@confusion.com", you will be unable to cancel your own postings. To make your posts and cancels work properly, define -DNNTP_INEWS_GATEWAY=\"confusion.com\" Example 2: I use this define to make all my net postings appear from our news gateway machine 'anl433' even though I post from my own workstation 'sony01' i.e. -DNNTP_INEWS_GATEWAY=\"anl433\" Example 3: -DNNTP_INEWS_GATEWAY=\"%anl433.uucp@Germany.EU.net\" (using the '%' hack) will create the net address "Iain.Lea%anl433.uucp@Germany.EU.net" NNTP_INEWS_DOMAIN Defines the name of your network domain. If the first letter of the string is '/' the domain name will be read from the specified file. If the first letter of the string is a '$' the domain name will be read from the specified environment variable. The env. variable contents may be a path. Note: don't define if NNTP_INEWS_GATEWAY is defined. Example 1: I use this define to add our uucp domain '.uucp' to our news gateways machine address 'anl433.uucp'. Note the leading dot '.' which is *always* needed before the domain name. i.e. -DNNTP_INEWS_DOMAIN=\".uucp\" NNTP - Reading & posting news ----------------------------- NNTP_ABLE Define if you wish to read news locally and remotely via an NNTP server. NNTP_ONLY Define if you [want to | can] ONLY read news remotely via an NNTP server. NNTP_INEWS Define if you want to use my builtin NNTP POST routine so that you no longer have to rely on the mini-inews from NNTP to be installed on each client machine. Also check that NNTP_INEWS_GATEWAY & NNTP_INEWS_DOMAIN are correctly set to produce a correct From: headers for your site. DONT_HAVE_NNTP_EXTS Define if you have a *virgin* NNTP server without my patches. This is just a nicety that will stop tin asking the server if it supports my NNTP server extensions XINDEX, XMOTD, XUSER & SPOOLDIR (cdrom) extensions. NNTP_SERVER_FILE Only define if your nntpserver file is other than /etc/nntpserver. NNTP_DEFAULT_SERVER Defines the name of the default nntp server that tin should connect to. Overrides the value of NNTP_SERVER_FILE. Can be overriden by setting the environment variable NNTPSERVER. NETLIBS Contains the networking libraries needed to link with nntplib.o file. Reading news via CD-ROM ----------------------- CDROM_ABLE Define if you wish to read news locally and from CD-ROM. CDROM_ONLY Define if you [want to | can] ONLY read news from CD-ROM. Daemon options -------------- INDEX_DAEMON Define to make an index file updating daemon version of tin. Note that no -lcurses or screen libraries need to be linked with tin when this #define is specified. If defined this will automatically undefine all NNTP_* defines as the daemon has to be installed on the NNTP server. Miscellaneous options --------------------- DEBUG Define if you want tin to log debug info. to files in /tmp. Activated by tin -Dn where n is 1 for NNTP only debug logging and n is 2 for logging all debug info. Debug files written to /tmp are ARTS, ACTIVE, BASE and NNTP. DONT_HAVE_MKDIR Define if your machine does not have the mkdir() system call. DONT_HAVE_GETCWD Define if you don't have the getcwd() system call. getwd() will be used. DONT_LOG_USER Log username & info to /tmp/.tin_log for usage statistics. If reading via NNTP the NNTP XUSER extended command will log user info to NNTP server. If -DNNTP_XUSER is defined it will define LOG_USER automatically. DONT_REREAD_ACTIVE_FILE Define if you do not want the active file to be reread periodically. The reread period can be set in seconds by setting the tinrc variable 'reread_active_file_secs=' HAVE_FACIST_NEWSADMIN Define if you want users articles to be posted to groups that your site receives. This will change the warning that a group that the user is posting to was not found in the sites active file to an error in the article checking routine therefore causing the user to remove the group from his/her posting or to abort the posting of the article. HAVE_ISPELL Define if you have ispell (interactive spellchecker) installed and want the option of checking your articles, mails before posting/mailing them. HAVE_LONG_FILENAMES Define if your machines filesystem supports filenames longer than 14 chars (default for BSD type systems). HAVE_MAIL_HANDLER Define if you want to use the MH style mail handling & reading code in mail.c It should be noted that mail handling is not well tested and not yet fully implemented. You can expect errors if you use this define so let me know the problems by sending me a bug report ('R' bug command from within tin). HAVE_MMDF_MAILER Define if your machine uses a MMDF type mailer instead of sendmail. It is defined as default on SCO Unix machines. It can be dynamically changed by setting the tinrc variable save_to_mmdf_mailbox to ON. HAVE_POLL Define if you have the poll() system call that is required to abort the indexing of a group in the function input_pending(). Don't define if HAVE_SELECT is already defined. HAVE_SELECT Define if you have the select() system call that is required to abort the indexing of a group in the function input_pending(). Don't define if HAVE_POLL is already defined. HAVE_POSIX_JC Define if your machine uses Posix style sigaction() signal handling. HAVE_SETREUID Define if problems occur when runnung tin as setuid news. Only define if your system supports the setreuid() system call. HAVE_STRFTIME Define if date shown at article viewer level is incorrect (ie. 1970...). Only define if your system supports the strftime() system call. NO_PIPING Do not allow piping of articles to shell commands. NO_POSTING Do not allow posting/followup of articles. NO_REGEX Define if you do not want to use regular expression pattern matching. NO_SHELL_ESCAPE Do not allow shell escapes. SLOW_SCREEN_UPDATE Define if running over a low speed connection (ie. 2400baud). It stops the percentage info being shown at bottom of select and group menus and stops the groupname being displayed at the bottom of the screen as it is subscribed/unsubscribed. SMALL_MEMORY_MACHINE Define if you are running a machine with little memory (<4MB). Tin will run slightly slower but be more efficent in how memory is allocated and reclaimed. USE_CLEARSCREEN Define if the you wish screen to use ClearScreen() and not MoveCursor() and CleartoEOLN(). This is perhaps faster on slow terminals but I have not really run any speed tests recently. USE_INN_NNTPLIB Define if you want to use the INN library functions GetConfigValue() & GetFQDN(). The INN_NNTPLIB variable in the Makefile must contain the correct path to INN library. USE_INVERSE_HACK Define if you want inverse video and highlighted bar disabled. Can be toggled in tin by the 'I' command and highlight bar by 'M' command. Compiled & installed on the following machines ---------------------------------------------- 1) * i386 & Linux 0.99p12 (main development machine) 2) * i386 & BSDI 1.0/Xenix 2.3.2/SCO SVR3.2/ISC SVR3.2/ATT SVR4.0 3) Sony News & NewsOS 4.1 4) SNI MX300/MX500 & Sinix 5.24/5.4 5) * Sun 3/4/IPC/SS1/SS2/SS10 & SunOS 4.0.3/4.1.1/4.1.2/4.1.3 6) Dec 5000/Vax & Ultrix 4.1/4.2 7) Vax 11/785 & BSD 4.3 8) DG Aviion 300 & DG-UX 4.30 9) Apollo DN4500 & DomainOS 10.3 10) ICL DRS6000 & SVR4.0 11) Apricot VX/FT & SCO 3.2.2 12) DIAB DS90 & D-NIX 5.3 13) Amdahl 5890 & UTS 5.2.6b 14) HP 720/845 & HP-UX 7.0/8.0 15) IBM RS/6000 & AIX 3.1.5 16) NCR Tower & SysV 17) Atari STe & Minix 1.5.10.3b 18) Powerbook 140 & MacMinix 19) 386 PC & Minix 386 20) Sequent S81 & PTX 1.3 / Dynix 21) Convex C220 & Convex Un*x 22) Harris HCX & CX/UX 23) SGI 4D35/Challenge & IRIX 4.0.1/5 24) Pyramid 9810 & OSx96N 25) Alliant FX/2800 & Concentrix 2.2 26) Stratus i860 & FTX 2.0 27) Apple A/UX 3.0 & gcc 2.2 (?) 28) Motorola Delta 3200 & SysVR3V6 29) Gould Powernode 9050 & utx/32 30) Acorn R260 & RISCiX 1.2 31) DDE Supermax & ATT SysVr3 * = compiled, installed and used by author *[SRC.TIN-1_22]INSTALL.NNTP;1+,y. // 4  -d0@123KPWO 56`t7w ?ꑗ89 G/HJCompilation and installation notes for NNTP patches - 18-11-92 -------------------------------------------------------------- This document explains the install procedure for the supplied patch to the NNTP server nntpd & to the TIN newsreader to retrieve index files from the NNTP server. NNTP server ----------- 1) Copy the following files to /server cp xindex.c /server cp xmotd.c /server cp xuser.c /server 2) Copy the following files to cp server.patch cp common.patch 3) Patch the files in /common & /server cd patch < common.patch patch < server.patch 4) Copy /common/conf.h.dist to /common/conf.h cd /common cp conf.h.dist conf.h 5) Edit /common/conf.h to suit your sites needs. The XINDEX, XMOTD, XOVERVIEW & XUSER extensions are #defined by default. You may need to change the path for the #define's for XINDEX_DIR, XMOTD_FILE & SUBSCRIBTIONS_FILE. The default for XINDEX_DIR is /usr/spool/news/.index and for the others /usr/lib/news/[motd | subscriptions] cd /common [vi|emacs] conf.h 6) Recompile & install the NNTP server nntpd cd make server make install_server TIN client ---------- 1) Nothing needs to changed in the tin client as long as you originally compiled tin with -DNNTP_ABLE or -DNNTP_ONLY. You will have to remove -DDONT_HAVE_NNTP_EXTS from the CFLAGS in the Makefile and recompile if it was originally defined. 2) Recompile & install tin. cd make make install TIN daemon (on NNTP server) --------------------------- 1) Edit tin Makefile to enable index daemon functionality. cd [vi|emacs] Makefile Add -DINDEX_DAEMON to CFLAGS to create a version of tin 'tind' to create & update index files on the NNTP server. 2) Compile & install tind. cd make make install_daemon 3) Add entry to crontab to start 'tind' index daemon every so often. cd /usr/spool/cron/crontabs [vi|emacs] root Add following line to run tind every 30 minutes: 0,30 * * * * su news -c '/usr/lib/news/tind' OK. If you have gotten this far you will have the following configuration: o tind will run every 30 minutes to update a central directory (usually /usr/spool/news/.index) of tin index files for all groups in the active file. The directory can be changed by 'tind -I dir' if so desired. o The NNTP server nntpd will service all requests for tin index files from tin clients. It will do this by returning the contents of the group index file in the index directory (ie. /usr/spool/news/.index/*) o The tin client will issue requests for index files to the NNTP server therefore saving space on the client machine and ensuring that there are only one copy of index files on the whole network. Also clients will not have to wait while index files are built locally as the index daemon tind runs frequently on the news server. Enjoy & happy newsreading Iain *[SRC.TIN-1_22]KILL.C;2+,B.// 4-d0@123KPWO5627`889]VG/HJ/* * Project : tin - a Usenet reader * Module : kill.c * Author : I.Lea & J.Robinson * Created : 01-04-91 * Updated : 11-07-93 * Notes : kill & auto select (hot) articles * Copyright : (c) Copyright 1991-93 by Iain Lea & Jim Robinson * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #ifdef NO_REGEX char *stars = ""; #else char *stars = "*"; #endif #define SET_KILLED(i) (arts[i].unread = ART_READ, arts[i].killed = 1) #define SET_HOT(i) (arts[i].hot = 1) #define IS_READ(i) (arts[i].unread == ART_READ) #define IS_KILLED(i) (arts[i].killed == 1) #define KILL_CHAR 'K' #define HOT_CHAR 'H' #define K_KILL 0 #define K_HOT 1 int kill_level = 1; int kill_num = 0; struct t_kill *killf; /* * read_kill_file - read ~/.tin/kill file contents into kill array */ int read_kill_file () { char buf[LEN], *ptr; FILE *fp; int n; char c; unsigned int type; free_kill_array (); if ((fp = fopen (killfile, "r")) == NULL) { return FALSE; } kill_num=0; while (fgets (buf, sizeof buf, fp) != NULL) { if (buf[0] == '#') { continue; } if (kill_num == max_kill-1) { expand_kill (); } n = sscanf(buf, "%d %c", &type, &c); if (n == 0) { goto corrupt_killfile; } if (n > 1 && c == HOT_CHAR) { /* hot */ kil ~ TIN-1_22.BCKBd[SRC.TIN-1_22]KILL.C;2Plf[kill_num].kill_how = K_HOT; } else { killf[kill_num].kill_how = K_KILL; } killf[kill_num].kill_type = type; if (fgets (buf, sizeof buf, fp) == NULL) { goto corrupt_killfile; } killf[kill_num].kill_group = (long) atol (buf); switch (killf[kill_num].kill_type) { case KILL_SUBJ: if (fgets (buf, sizeof buf, fp) != NULL) { ptr = strchr (buf, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } killf[kill_num].kill_subj = str_dup (buf); } break; case KILL_FROM: if (fgets (buf, sizeof buf, fp) != NULL) { ptr = strchr (buf, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } killf[kill_num].kill_from = str_dup (buf); } break; case KILL_BOTH: if (fgets (buf, sizeof buf, fp) != NULL) { ptr = strchr (buf, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } killf[kill_num].kill_subj = str_dup (buf); } if (fgets (buf, sizeof buf, fp) != NULL) { ptr = strchr (buf, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } killf[kill_num].kill_from = str_dup (buf); } break; default: goto corrupt_killfile; } kill_num++; } fclose (fp); return (kill_num); corrupt_killfile: fclose (fp); killf[kill_num].kill_type = 0; error_message (txt_corrupt_kill_file, killfile); return FALSE; } /* * write_kill_file - write kill strings to ~/.tin/kill */ void write_kill_file () { FILE *fp; int i; #ifdef VMS if (kill_num == 0 || (fp = fopen (killfile, "w", "fop=cif")) == NULL) { #else if (kill_num == 0 || (fp = fopen (killfile, "w")) == NULL) { #endif return; } wait_message (txt_saving); fprintf (fp, "# 1st line 1=(Subject: only) 2=(From: only) 3=(Subject: & From:)\n"); fprintf (fp, "# %c=(kill) %c=(auto-selection)\n", KILL_CHAR, HOT_CHAR); fprintf (fp, "# 2nd line 0=(kill on all newsgroups) >0=(kill on specific newsgroup)\n"); for (i=0 ; i < kill_num ; i++) { if (killf[i].kill_type == 0 || (killf[i].kill_subj == 0 && killf[i].kill_from == 0)) continue; if (killf[i].kill_how == K_KILL) { fprintf (fp, "#\n# %03d KILL\n", i+1); fprintf (fp, "%d\t%c\n", killf[i].kill_type, KILL_CHAR); } else { fprintf (fp, "#\n# %03d HOT\n", i+1); fprintf (fp, "%d\t%c\n", killf[i].kill_type, HOT_CHAR); } fprintf (fp, "%ld\n", killf[i].kill_group); switch (killf[i].kill_type) { case KILL_SUBJ: fprintf (fp, "%s\n", killf[i].kill_subj); break; case KILL_FROM: fprintf (fp, "%s\n", killf[i].kill_from); break; case KILL_BOTH: fprintf (fp, "%s\n", killf[i].kill_subj); fprintf (fp, "%s\n", killf[i].kill_from); break; } } fclose (fp); chmod (killfile, 0600); } #if __STDC__ static int get_choice ( int x, char *help, char *prompt, char *opt1, char *opt2, char *opt3, char *opt4) #else static int get_choice (x, help, prompt, opt1, opt2, opt3, opt4) int x; char *help; char *prompt; char *opt1; char *opt2; char *opt3; char *opt4; #endif { int ch, n = 0, i = 0; char *argv[4]; if (opt1) argv[n++] = opt1; if (opt2) argv[n++] = opt2; if (opt3) argv[n++] = opt3; if (opt4) argv[n++] = opt4; assert(n > 0); if (help) show_menu_help (help); do { MoveCursor(x, (int) strlen (prompt)); fputs (argv[i], stdout); fflush (stdout); CleartoEOLN (); if ((ch = ReadCh ()) != ' ') continue; if (++i == n) i = 0; } while (ch != CR && ch != ESC); if (ch == ESC) return (-1); return (i); } /* * options menu so that the user can dynamically change parameters */ int kill_art_menu (group_name, index) char *group_name; int index; { char buf[LEN]; char text[LEN]; char kill_from[LEN]; char kill_subj[LEN]; char kill_group[LEN]; char ch_default = 's'; int ch; int counter = 0; int killed = TRUE; int kill_from_ok = FALSE; int kill_subj_ok = FALSE; int kill_every_group = FALSE; int i; int kill_how; #ifdef SIGTSTP t_sigtype (*susp)(); susp = (t_sigtype (*)()) 0; if (do_sigtstp) { susp = sigdisp (SIGTSTP, SIG_DFL); sigdisp (SIGTSTP, SIG_IGN); } #endif sprintf (kill_group, "%s only", group_name); sprintf (kill_subj, txt_kill_subject, cCOLS-35, cCOLS-35, arts[index].subject); if (arts[index].name != (char *) 0) { sprintf (text, "%s (%s)", arts[index].from, arts[index].name); } else { strcpy (text, arts[index].from); } sprintf (kill_from, txt_kill_from, cCOLS-35, cCOLS-35, text); text[0] = '\0'; ClearScreen (); center_line (0, TRUE, txt_kill_menu); MoveCursor (INDEX_TOP, 0); printf ("%s\r\n\r\n\r\n", txt_kill_how); printf ("%s\r\n\r\n", txt_kill_text); printf ("%s\r\n\r\n\r\n", txt_kill_text_type); printf ("%s\r\n\r\n", kill_subj); printf ("%s\r\n\r\n\r\n", kill_from); printf ("%s%s", txt_kill_group, kill_group); fflush (stdout); i = get_choice (INDEX_TOP, txt_help_kill_how, txt_kill_how, "Kill ", "Auto Select", NULL, NULL); if (i == -1) { return FALSE; } kill_how = (i == 0 ? K_KILL : K_HOT); show_menu_help (txt_help_kill_text); if (! prompt_menu_string (INDEX_TOP+3, (int) strlen (txt_kill_text), text)) { return FALSE; } if (text[0]) { i = get_choice(INDEX_TOP+5, txt_help_kill_text_type, txt_kill_text_type, "Subject: line only ", "From: line only ", "Subject: & From: lines", NULL); if (i == -1) { return FALSE; } counter = ((i == 0 ? KILL_SUBJ : (i == 1 ? KILL_FROM : KILL_BOTH))); } if (! text[0]) { i = get_choice (INDEX_TOP+8, txt_help_kill_subject, kill_subj, txt_yes, txt_no, NULL, NULL); if (i == -1) { return FALSE; } else { kill_subj_ok = (i ? FALSE : TRUE); } if (kill_subj_ok) { i = get_choice (INDEX_TOP+10, txt_help_kill_from, kill_from, txt_no, txt_yes, NULL, NULL); } else { i = get_choice (INDEX_TOP+10, txt_help_kill_from, kill_from, txt_yes, txt_no, NULL, NULL); } if (i == -1) { return FALSE; } else { if (kill_subj_ok) { kill_from_ok = (i ? TRUE : FALSE); } else { kill_from_ok = (i ? FALSE : TRUE); } } } if (text[0] || kill_subj_ok || kill_from_ok) { i = get_choice (INDEX_TOP+13, txt_help_kill_group, txt_kill_group, kill_group, "All groups", NULL, NULL); if (i == -1) { return FALSE; } kill_every_group = (i == 0 ? FALSE : TRUE); } while (1) { do { sprintf (msg, "%s%c", txt_quit_edit_save_killfile, ch_default); wait_message (msg); MoveCursor (cLINES, (int) strlen (txt_quit_edit_save_killfile)); if ((ch = ReadCh ()) == CR) ch = ch_default; } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 's'); switch (ch) { case 'e': invoke_editor (killfile, 2); unkill_all_articles (); killed_articles = read_kill_file (); killed = TRUE; goto kill_done; case 'q': case ESC: killed = FALSE; goto kill_done; case 's': if (kill_num > max_kill-1) { expand_kill (); } killf[kill_num].kill_how = kill_how; if (text[0]) { sprintf (buf, "%s%s%s", stars, text, stars); switch (counter) { case KILL_SUBJ: killf[kill_num].kill_subj = str_dup (buf); break; case KILL_FROM: killf[kill_num].kill_from = str_dup (buf); break; case KILL_BOTH: killf[kill_num].kill_subj = str_dup (buf); killf[kill_num].kill_from = killf[kill_num].kill_subj; break; } killf[kill_num].kill_type = counter; if (kill_every_group) { killf[kill_num].kill_group = 0L; } else { killf[kill_num].kill_group = hash_s (group_name); } kill_num++; } else { if (kill_subj_ok) { killf[kill_num].kill_type = KILL_SUBJ; sprintf (buf, "%s%s%s", stars, arts[index].subject, stars); killf[kill_num].kill_subj = str_dup (buf); } if (kill_from_ok) { killf[kill_num].kill_type |= KILL_FROM; if (arts[index].name != (char *) 0) { sprintf (buf, "%s%s (%s)%s", stars, arts[index].from, arts[index].name, stars); } else { sprintf (buf, "%s%s%s", stars, arts[index].from, stars); } killf[kill_num].kill_from = str_dup (buf); } if (killf[kill_num].kill_type) { if (kill_every_group) { killf[kill_num].kill_group= 0L; } else { killf[kill_num].kill_group= hash_s (group_name); } kill_num++; } } write_kill_file (); kill_done: #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, susp); } #endif return (killed); } } /* NOTREACHED */ } /* * We assume that any articles which are tagged as killed are also * tagged as being read BECAUSE they were killed. So, we retag * them as being unread. */ int unkill_all_articles () { int unkilled = FALSE; register int i; for (i=0 ; i < top ; i++) { if (arts[i].killed) { arts[i].killed = FALSE; arts[i].unread = ART_UNREAD; unkilled = TRUE; } } num_of_killed_arts = 0; return (unkilled); } /* * Kill any articles in group active[index] */ int kill_any_articles (index) int index; /* active[index].name gets groupname */ { char buf[LEN]; int killed = FALSE; int run_ok = FALSE; int is_hot; long newsgroup_hash; register int i, j; if (! kill_num) { return (killed); } num_of_killed_arts = 0; num_of_hot_arts = 0; newsgroup_hash = hash_s (active[index].name); for (i=0 ; i < kill_num ; i++) { if (killf[i].kill_group == 0L || killf[i].kill_group == newsgroup_hash) { run_ok = TRUE; } } if (! run_ok) { return (killed); } if (debug && ! update) { wait_message (txt_killing_arts); } for (i=0 ; i < top ; i++) { if (IS_READ(i) && kill_level == 0) { continue; } for (j=0 ; j < kill_num ; j++) { if (killf[j].kill_group != 0L && killf[j].kill_group != newsgroup_hash) continue; is_hot = (killf[j].kill_how == K_HOT ? TRUE : FALSE); switch (killf[j].kill_type) { case KILL_SUBJ: if (STR_MATCH (arts[i].subject, killf[j].kill_subj)) { if (! is_hot) { SET_KILLED(i); num_of_killed_arts++; } else { SET_HOT(i); if (active[index].attribute.show_only_unread) { if (arts[i].unread == ART_UNREAD) { num_of_hot_arts++; } } else { num_of_hot_arts++; } } } break; case KILL_FROM: if (arts[i].name != (char *) 0) { sprintf (buf, "%s (%s)", arts[i].from, arts[i].name); } else { strcpy (buf, arts[i].from); } if (STR_MATCH (buf, killf[j].kill_from)) { if (! is_hot) { SET_KILLED(i); num_of_killed_arts++; } else { SET_HOT(i); if (active[index].attribute.show_only_unread) { if (arts[i].unread == ART_UNREAD) { num_of_hot_arts++; } } else { num_of_hot_arts++; } } } break; case KILL_BOTH: if (STR_MATCH (arts[i].subject, killf[j].kill_subj)) { if (! is_hot) { SET_KILLED(i); num_of_killed_arts++; } else { SET_HOT(i); if (active[index].attribute.show_only_unread) { if (arts[i].unread == ART_UNREAD) { num_of_hot_arts++; } } else { num_of_hot_arts++; } } break; } if (arts[i].name != (char *) 0) { sprintf (buf, "%s (%s)", arts[i].from, arts[i].name); } else { strcpy (buf, arts[i].from); } if (STR_MATCH (buf, killf[j].kill_from)) { if (! is_hot) { SET_KILLED(i); num_of_killed_arts++; } else { SET_HOT(i); if (active[index].attribute.show_only_unread) { if (arts[i].unread == ART_UNREAD) { num_of_hot_arts++; } } else { num_of_hot_arts++; } } } break; } if (IS_KILLED(i) || ! killed) killed = TRUE; } } return (killed); } /* * Auto select articles. * WARNING - this routinely is presently a kludge. It calls * kill_any_articles() which also kills articles. It also always returns * true in order to fake out the display code (cause it doesn't know * if any articles were actually selected) * The correct way to do this is to modify kill_any_articles() to take * another arg to specify whether killing, auto-selecting, or both is to be * done, rename it to something else, and then have a new kill_any_articles() * and auto_select_articles() call this new routine with the appropriate * arguments. */ int auto_select_articles (index) int index; { kill_any_articles (index); return (TRUE); } d*[SRC.TIN-1_22]LANG.C;2+,.?// 4?=-d0@123KPWO@56@7@ֹ89]VG/HJ/* * Project : tin - a Usenet reader * Module : lang.c * Author : I.Lea * Created : 01-04-91 * Updated : 22-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" /* * active.c */ char txt_subscribe_to_new_group[] = "Subscribe to new group %s (y/n/q) [%c]: "; char txt_delete_bogus_group[] = "Remove bogus group %s (y/n/q) [%c]: "; char txt_reading_news_active_file[] = "Reading news active file..."; char txt_reading_mail_active_file[] = "Reading mail active file..."; char txt_reading_attributes_file[] = "Reading attributes file..."; char txt_writing_attributes_file[] = "Writing attributes file..."; char txt_reading_newsgroups_file[] = "Reading newsgroups file..."; char txt_reading_mailgroups_file[] = "Reading mailgroups file..."; /* * art.c */ #if defined(HAVE_POLL) || defined(HAVE_SELECT) char txt_group[] = "Group %s ('q' to quit)... "; #else char txt_group[] = "Group %s... "; #endif char txt_purge[] = "Purging %s..."; char txt_cannot_open_art[] = "Can't open article %s: "; char txt_corrupt_index[] = "Index file %s corrupted. error %d on article %d"; char txt_checking_for_news[] = "Checking for news..."; char txt_there_is_no_news[] = "There is no news\n"; char txt_killing_arts[] = "Selecting articles..."; char txt_unkilling_arts[] = "Unselecting articles..."; char txt_catchup_update_info[] = "%s %d group(s) in %ld seconds\n"; char txt_abort_indexing[] = "Do you want to abort indexing group? (y/n): "; char txt_abort_searching[] = "Do you want to abort searching? (y/n): "; char txt_no_index_file[] = "No index file specified"; char txt_writing_index_file[] = "Writing index file..."; /* * feed.c */ char txt_art_thread_regex_tag[] = " a)rticle, t)hread, h)ot, p)attern, T)agged articles, q)uit: "; #ifdef M_AMIGA char txt_post_process_type[] = "Process n)one, s)har, u)ud, l)ist lha, e)xt lha, L)ist zip, E)xt zip, q)uit: "; #else char txt_post_process_type[] = "Process n)one, s)har, u)ud, l)ist zoo, e)xt zoo, L)ist zip, E)xt zip, q)uit: "; #endif #ifdef NO_REGEX char txt_feed_pattern[] = "Enter pattern [%s]> "; #else char txt_feed_pattern[] = "Enter regex pattern [%s]> "; #endif char txt_no_command[] = "No command"; char txt_piping[] = "Piping..."; char txt_piping_not_enabled[] = "Piping not enabled. Recompile without -DNO_PIPING."; char txt_saved[] = "-- %d Article(s) saved --"; /* * group.c */ char txt_reading_all_arts[] = "Reading all articles..."; char txt_reading_new_arts[] = "Reading unread articles..."; char txt_cannot_post[] = "*** Posting not allowed ***"; char txt_tagged_art[] = "Tagged article"; char txt_tagged_thread[] = "Tagged thread"; char txt_untagged_art[] = "Untagged article"; char txt_untagged_thread[] = "Untagged thread"; char txt_inverse_on[] = "Inverse video enabled"; char txt_inverse_off[] = "Inverse video disabled"; char txt_subscribed_to[] = "Subscribed to %s"; char txt_unsubscribed_to[] = "Unsubscribed from %s"; char txt_mark_all_read[] = "Mark all articles as read? (y/n): "; char txt_mark_thread_read[] = "Mark thread as read? (y/n): "; char txt_no_more_groups[] = "No more groups"; char txt_no_prev_group[] = "No previous group"; char txt_no_arts[] = "*** No Articles ***"; char txt_no_groups[] = "*** No Groups ***"; char txt_not_active_newsfeed[] = "Command only allowed on active news"; char txt_end_of_thread[] = "*** End of Thread ***"; char txt_end_of_arts[] = "*** End of Articles ***"; char txt_end_of_groups[] = "*** End of Groups ***"; char txt_no_next_unread_art[] = "No next unread article"; char txt_no_prev_unread_art[] = "No previous unread article"; char txt_no_last_message[] = "No last message"; char txt_bad_command[] = "Bad command. Type 'h' for help."; char txt_you_have_mail[] = " You have mail\n"; char txt_type_h_for_help[] = " h=help\n"; char txt_read_art[] = "Read article> "; char txt_search_forwards[] = "Search forwards [%s]> "; char txt_search_backwards[] = "Search backwards [%s]> "; char txt_search_body[] = "Search body [%s]> "; char txt_author_search_forwards[] = "Author search forwards [%s]> "; char txt_author_search_backwards[] = "Author search backwards [%s]> "; char txt_no_search_string[] = "No search string"; char txt_no_match[] = "No match"; char txt_no_newsgroups[] = "No newsgroups"; char txt_no  _quick_newsgroups[] = "\nNo newsgroups. Exiting..."; char txt_no_quick_subject[] = "\nNo subject. Exiting..."; char txt_no_subject[] = "No subject"; char txt_post_newsgroups[] = "Post to newsgroup(s) [%s]> "; char txt_post_subject[] = "Post subject [%s]> "; char txt_cannot_open[] = "Can't open %s"; char txt_posting[] = "Posting article..."; char txt_art_posted[] = "Article posted"; char txt_art_rejected[] = "Article rejected (saved to %s)"; #ifdef HAVE_ISPELL char txt_quit_edit_post[] = "q)uit, e)dit, i)spell, p)ost: "; #else char txt_quit_edit_post[] = "q)uit, e)dit, p)ost: "; #endif char txt_help_4[] = "4$ Goto spooldir 4 ($=goto last spooldir)\r\n"; char txt_help_i_4[] = "4$ Goto article 4 ($=goto last article)\r\n"; char txt_help_ctrl_k[] = "^K Kill / Auto select (hot) current article\r\n"; char txt_help_ctrl_l[] = "^L Redraw page\r\n"; char txt_help_ctrl_d[] = "^D^U Down (^U=up) a page\r\n"; char txt_help_ctrl_f[] = "^F^B Down (^B=up) a page\r\n"; char txt_help_i_cr[] = " Read current article\r\n"; char txt_help_cr[] = " Read news from selected spooldir\r\n"; char txt_help_i_tab[] = " Goto next unread article or group\r\n"; char txt_help_d[] = "d Toggle display of subject or subject & author\r\n"; char txt_help_l[] = "l List articles within current thread\r\n"; char txt_help_m[] = "m Move current group within group selection list\r\n"; char txt_help_M[] = "M Menu of configurable options\r\n"; char txt_help_a[] = "aA Author forward (A=backward) search\r\n"; char txt_help_B[] = "B Article body search\r\n"; char txt_help_sel_c[] = "cC Mark group read (C=and goto next unread group)\r\n"; char txt_help_c[] = "c Mark all articles as read and goto group selection menu\r\n"; char txt_help_cC[] = "C Mark all articles as read and goto next unread group\r\n"; char txt_help_g[] = "g Choose a new group by name\r\n"; char txt_help_I[] = "I Toggle inverse video\r\n"; char txt_help_K[] = "K Mark article/thread as read & goto next unread\r\n"; char txt_help_j[] = "jk Down (k=up) a line\r\n"; char txt_help_i_n[] = "np Goto next (p=previous) group\r\n"; char txt_help_i_p[] = "NP Goto next (P=previous) unread article\r\n"; char txt_help_q[] = "Q Quit\r\n"; char txt_help_r[] = "r Toggle display to show all / only unread articles\r\n"; char txt_help_s[] = "su Subscribe (u=unsubscribe) to current group\r\n"; char txt_help_S[] = "SU Subscribe (U=unsubscribe) to groups that match pattern\r\n"; char txt_help_T[] = "T Return to group selection level\r\n"; char txt_help_t[] = "t Tag current article for crossposting/mailing/piping/printing/saving\r\n"; char txt_help_u[] = "u Toggle display of unthreaded & threaded articles\r\n"; char txt_help_U[] = "U Untag all tagged articles\r\n"; char txt_help_v[] = "v Show version information\r\n"; char txt_help_w[] = "w Post an article to current group\r\n"; char txt_help_x[] = "x * Crosspost current article to another group\r\n"; char txt_help_i_search[] = "/? Subject forward (?=backward) search\r\n"; char txt_help_thread[] = "<> Goto first (>=last) article in current thread\r\n"; #ifndef NO_SHELL_ESCAPE char txt_help_shell[] = "! Shell escape\r\n"; #endif char txt_help_dash[] = "- Show last message\r\n"; char txt_help_i_star[] = "* Select thread\r\n"; char txt_help_i_dot[] = ". Toggle selection of thread\r\n"; char txt_help_i_coma[] = "@ Reverse all selections (all articles)\r\n"; char txt_help_i_tilda[] ="~ Undo all selections (all articles)\r\n"; char txt_help_X[] = "X Mark all unread articles that have not been selected as read\r\n"; char txt_help_plus[] = "+ Perform auto-selection on group\r\n"; char txt_help_equal[] = "= Mark threads selected if at least one unread art is selected\r\n"; char txt_help_semicolon[] = "; Mark threads selected if at least one unread art is selected\r\n"; #ifdef NO_REGEX char txt_save_pattern[] = "Enter save pattern [%s]> "; #else char txt_save_pattern[] = "Enter regex save pattern [%s]> "; #endif char txt_saved_pattern_to[] = "-- Saved pattern to %s - %s --"; char txt_saved_to_mailbox[] = "-- Saved to mailbox %s --"; char txt_threading_arts[] = "Threading articles..."; char txt_unthreading_arts[] = "Unthreading articles..."; char txt_select_pattern[] = "Enter selection pattern [%s]> "; /* * help.c: */ char txt_group_select_com[] = "Group Selection Commands (page %d of %d)"; char txt_spooldir_com[] = "Spooldir Selection Commands (page %d of %d)"; char txt_index_page_com[] = "Index Page Commands (page %d of %d)"; char txt_thread_com[] = "Thread Commands (page %d of %d)"; char txt_art_pager_com[] = "Article Pager Commands (page %d of %d)"; char txt_hit_space_for_more[] = "PgDn,End,,^D - page down. PgUp,Home,b,^U - page up. ,q - quit"; char txt_post_history_menu[] = "Posted articles history (page %d of %d)"; char txt_mini_select_1[] = "=set current to n, TAB=next unread, /=search pattern, c)atchup,"; char txt_mini_select_2[] = "g)oto, j=line down, k=line up, h)elp, m)ove, q)uit, r=toggle all/unread,"; char txt_mini_select_3[] = "s)ubscribe, S)ub pattern, u)nsubscribe, U)nsub pattern, y)ank in/out"; char txt_mini_spooldir_1[] = "=set current to n, CR=selects spooldir, h)elp, j=line down, k=line up, q)uit"; char txt_mini_group_1[] = "=set current to n, TAB=next unread, /=search pattern, ^K)ill/select,"; char txt_mini_group_2[] = "a)uthor search, c)atchup, j=line down, k=line up, K=mark read, l)ist thread,"; char txt_mini_group_3[] = "|=pipe, m)ail, o=print, q)uit, r=toggle all/unread, s)ave, t)ag, w=post"; char txt_mini_thread_1[] = "=set current to n, TAB=next unread, c)atchup, d)isplay toggle,"; char txt_mini_thread_2[] = "h)elp, j=line down, k=line up, q)uit, t)ag, z=mark unread"; char txt_mini_page_1[] = "=set current to n, TAB=next unread, /=search pattern, ^K)ill/select,"; char txt_mini_page_2[] = "a)uthor search, B)ody search, c)atchup, f)ollowup, K=mark read,"; char txt_mini_page_3[] = "|=pipe, m)ail, o=print, q)uit, r)eply mail, s)ave, t)ag, w=post"; /* * init.c: */ char txt_env_var_not_found[] = "Environment variable %s not found. Set and retry..."; /* * kill.c: */ char txt_corrupt_kill_file[] = "Corrupt kill file %s"; char txt_kill_menu[] = "Kill / Auto-select Article Menu"; char txt_kill_how[] = "Kill type : "; char txt_kill_subject[] = "Kill Subject [%-*.*s] (y/n): "; char txt_kill_from[] = "Kill From [%-*.*s] (y/n): "; char txt_kill_text[] = "Kill text pattern : "; char txt_kill_text_type[] = "Apply pattern to : "; char txt_kill_group[] = "Kill pattern scope: "; char txt_help_kill_how[] = "Choose kill or auto select. toggles & sets."; char txt_help_kill_subject[] = "Subject: line to add to kill file. toggles & sets."; char txt_help_kill_from[] = "From: line to add to kill file. toggles & sets."; char txt_help_kill_text[] = "Enter text pattern to kill if Subject: & From: lines are not what you want."; char txt_help_kill_text_type[] = "Select where text pattern should be applied. toggles & sets."; char txt_help_kill_group[] = "Kill/auto-select only current group or all groups. toggles & sets."; char txt_quit_edit_save_killfile[] = "q)uit e)dit s)ave kill/hot description: "; char txt_yes[] = "Yes"; char txt_no[] = "No "; /* * main.c: */ #ifdef M_AMIGA char txt_copyright_notice[] = "%s (c) Copyright 1991-93 Iain Lea & Mark Tomlinson."; #endif #ifdef M_OS2 char txt_copyright_notice[] = "%s (c) Copyright 1991-93 Iain Lea & Andreas Wrede."; #endif #ifdef M_UNIX char txt_copyright_notice[] = "%s (c) Copyright 1991-93 Iain Lea."; #endif #ifdef VMS char txt_copyright_notice[] = "%s (c) Copyright 1991-93 Iain Lea & Tod McQuillin."; #endif char txt_option_not_enabled[] = "Option not enabled. Recompile with %s."; char txt_not_in_active_file[] = "Group %s not found in active file"; char txt_screen_init_failed[] = "%s: Screen initialization failed"; char txt_bad_active_file[] = "Active file corrupt - %s"; /* * misc.c */ char txt_cannot_open_active_file[] = "Can't open %s. Try %s -r to read news via NNTP.\n"; char txt_active_file_is_empty[] = "%s contains no newsgroups. Exiting."; char txt_checking_active_file[] = "Checking for new newsgroups..."; char txt_checking[] = "Checking..."; char txt_cannot_find_base_art[] = "Can't find base article %s"; char txt_out_of_memory[] = "%s: out of memory"; char txt_rename_error[] = "Error: rename %s to %s"; char txt_shell_escape[] = "Enter shell command [%s]> "; char txt_ispell_define_not_compiled[] = "Interactive spellchecker not enabled. Recompile with -DHAVE_ISPELL."; /* * newsrc.c */ char txt_creating_newsrc[] = "Creating .newsrc...\n"; char txt_deleting_from_newsrc[] = "Group %s not in active file. Deleting."; /* * open.c */ char txt_connecting[] = "Connecting to %s..."; char txt_reconnect_to_news_server[] = "Connection to news server has timed out. Reconnect? (y/n): "; char txt_cannot_get_nntp_server_name[] = "Cannot find NNTP server name"; char txt_server_name_in_file_env_var[] = "Put the server name in the file %s,\nor set the environment variable NNTPSERVER"; char txt_failed_to_connect_to_server[] = "Failed to connect to NNTP server %s. Exiting..."; char txt_rejected_by_nntpserver[] = "Rejected by server, nntp error %d"; char txt_connection_to_server_broken[] = "Connection to server broken"; char txt_stuff_nntp_cannot_open[] = "stuff_nntp: can't open %s: "; char txt_nntp_to_fp_cannot_reopen[] = "nntp_to_fp: can't reopen %s: "; char txt_nntp_to_fd_cannot_reopen[] = "nntp_to_fd: can't reopen %s: "; char txt_nntp_authorization_failed[] = "NNTP authorization password not found for %s"; /* * page.c */ char txt_reading_article[] = "Reading..."; char txt_quit[] = "Do you really want to quit? (y/n): "; char txt_art_unavailable[] = "Article %ld unavailable"; char txt_art_marked_as_unread[] = "Article marked as unread"; char txt_thread_marked_as_unread[] = "Thread marked as unread"; char txt_begin_of_art[] = "*** Beginning of article ***"; char txt_next_resp[] = "-- Next response --"; char txt_last_resp[] = "-- Last response --"; char txt_more[] = "--More--"; char txt_more_percent[] = "--More--(%d%%) [%ld/%ld]"; char txt_thread_x_of_n[] = "%sThread %4d of %4d\r\n"; char txt_lines[] = "Lines %s "; char txt_resp_x_of_n[] = "Respno %3d of %3d\r\n"; char txt_no_resp[] = "No responses\r\n"; char txt_1_resp[] = "1 Response\r\n"; char txt_x_resp[] = "%d Responses\r\n"; char txt_s_at_s[] = "%s at %s"; char txt_thread_resp_page[] = "Thread %d of %d, Resp %d/%d (page %d): %s"; char txt_thread_page[] = "Thread %d of %d (page %d): %s"; char txt_read_resp[] = "Read response> "; char txt_help_p_0[] = "0 Read the base article in current thread\r\n"; char txt_help_p_4[] = "4 Read response 4 in current thread\r\n"; char txt_help_p_cr[] = " Goto to next thread\r\n"; char txt_help_p_tab[] = " Goto next unread article\r\n"; char txt_help_b[] = "b Back (=forward) a page\r\n"; char txt_help_bug[] = "R Report bug/comment via mail to %s\r\n"; char txt_help_p_f[] = "fF Post (f=copy text) a followup\r\n"; char txt_help_D[] = "D Delete (cancel) current article that must have been posted by you\r\n"; char txt_help_ctrl_h[] = "^H Show articles header\r\n"; char txt_help_h[] = "hH Command help (H=toggle mini help menu)\r\n"; char txt_help_i[] = "q Return to previous level\r\n"; char txt_help_ck[] = "cK Mark thread as read & return to previous level\r\n"; char txt_help_p_k[] = "kK Mark article (K=thread) as read & advance to next unread\r\n"; char txt_help_p_m[] = "m Mail article/thread/hot/pattern/tagged articles to someone\r\n"; char txt_help_p_n[] = "nN Goto to the next (N=unread) article\r\n"; char txt_help_o[] = "o Output article/thread/hot/pattern/tagged articles to printer\r\n"; char txt_help_p_p[] = "pP Goto the previous (P=unread) article\r\n"; char txt_help_p_r[] = "rR Reply through mail (r=copy text) to author\r\n"; char txt_help_p_s[] = "s Save article/thread/hot/pattern/tagged articles to file\r\n"; char txt_help_p_z[] = "zZ Mark article (Z=thread) as unread\r\n"; char txt_help_p_ctrl_r[] = "^R$ Redisplay first ($=last) page of article\r\n"; char txt_help_p_g[] = "gG Goto first (G=last) page of article\r\n"; char txt_help_p_d[] = "d Toggle rot-13 decoding for current article\r\n"; char txt_help_pipe[] = "| Pipe article/thread/hot/pattern/tagged articles into command\r\n"; char txt_help_p_search[] = "/ Article forward search\r\n"; char txt_help_p_star[] = "* Select article\r\n"; char txt_help_p_dot[] = ". Toggle article selection\r\n"; char txt_help_p_coma[] = "@ Reverse article selections\r\n"; char txt_help_p_tilda[] = "~ Undo all selections in thread\r\n"; char txt_mail_art_to[] = "Mail article(s) to [%.*s]> "; char txt_no_mail_address[] = "No mail address"; char txt_no_responses[] = "No responses"; #ifdef HAVE_ISPELL char txt_quit_edit_ispell_send[] = "q)uit, e)dit, i)spell, s)end"; #else char txt_quit_edit_ispell_send[] = "q)uit, e)dit, s)end"; #endif char txt_quit_edit_send[] = "q)uit, e)dit, s)end"; char txt_quit_edit_delete[] = "q)uit, e)dit, d)elete"; char txt_mailing_to[] = "Mailing to %s..."; char txt_mailed[] = "-- %d Article(s) mailed --"; char txt_command_failed_s[] = "Command failed: %s\n"; char txt_mail_quote[] = "In article %M you wrote:"; char txt_resp_to_poster[] = "Responses have been directed to the poster. Post anyway? (y/n): p~ TIN-1_22.BCKd[SRC.TIN-1_22]LANG.C;2?;2%"; char txt_resp_redirect[] = "Responses have been directed to the following newsgroups"; char txt_continue[] = "Continue? (y/n): "; char txt_news_quote[] = "%F wrote:"; char txt_save_filename[] = "Save filename [%s]> "; char txt_art_not_saved[] = "Article not saved"; char txt_no_filename[] = "No filename"; char txt_saving[] = "Saving..."; char txt_art_saved_to[] = "Article saved to %s"; char txt_thread_not_saved[] = "Thread not saved"; char txt_thread_saved_to_many[] = "Thread saved to %s - %s"; char txt_thread_saved_to[] = "Thread saved to %s"; char txt_pipe_to_command[] = "Pipe to command [%.*s]> "; char txt_printing[] = "Printing..."; char txt_printed[] = "%d Article(s) printed"; char txt_append_to_file[] = "File %s exists. Append? (y/n): "; char txt_toggled_rot13[] = "Toggled rot13 encoding"; char txt_use_mime[] = "Use MIME display program for this message? (y/n): "; /* * post.c */ char txt_group_is_moderated[] = "Group %s is moderated. Continue? (y/n): "; char txt_no_arts_posted[] = "No articles have been posted"; char txt_post_an_article[] = "Post an article..."; char txt_post_a_followup[] = "Post a followup..."; char txt_mail_bug_report[] = "Mail bug report..."; char txt_crosspost_group[] = "Crosspost article(s) to group(s) [%s]> "; char txt_no_group[] = "No group"; char txt_crosspost_an_article[] = "Crossposting article..."; char txt_mail_bug_report_confirm[] = "Mail BUG REPORT to %s%s? (y/n): "; char txt_reply_to_author[] = "Reply to author..."; char txt_bad_article[] = "Article to be posted has the errors/warnings noted above. q)uit, e)dit: "; char txt_deleting_art[] = "Deleting article..."; char txt_art_deleted[] = "Article deleted"; char txt_art_cannot_delete[] = "Article cannot be deleted"; char txt_quit_edit_xpost[] = "q)uit, e)dit, p)ost [%.*s]: %c"; char txt_error_header_line_blank[] = "Error: Article starts with blank line instead of header\n\n"; char txt_error_header_line_colon[] = "Error: Header on line %d does not have a colon after the header name:\n%s\n\n"; char txt_error_header_line_space[] = "Error: Header on line %d does not have a space after the colon:\n%s\n\n"; char txt_error_header_line_empty_newsgroups[] = "Error: the \"Newsgroups:\" line lists no newsgroups.\n\n"; char txt_error_header_line_missing_newsgroups[] = "Error: the \"Newsgroups:\" line is missing from the articles header.\n\n"; char txt_error_header_line_missing_subject[] = "Error: the \"Subject:\" line is missing from the articles header.\n\n"; char txt_warn_art_line_too_long[] = "Warning: posting exceeds %d columns. Line %d is the first long one:\n%-100s\n\n"; char txt_error_header_and_body_not_seperate[] = "Error: No blank line found after header.\n\n"; char txt_art_newsgroups[] = "Your article will be posted to the following newsgroup%s:\n"; char txt_warn_not_valid_newsgroup[] = "Warning: \"%s\" is not a valid newsgroup at this site!\n\n"; char txt_error_not_valid_newsgroup[] = "Error: \"%s\" is not a valid newsgroup!\n\n"; char txt_error_header_line_comma[] = "Error: the \"Newsgroups:\" line has spaces in it that MUST be removed. The\n\ only allowable space is the one separating the colon (:) from the contents.\n\ Use a comma (,) to separate multiple newsgroup names.\n\n"; char txt_check_article[] = "Check Prepared Article"; char txt_error_from_in_header_not_allowed[] = "Error on line %d: \"From:\" header not allowed (it will be added for you)\n"; /* * prompt.c */ char txt_hit_any_key[] = "-- Press any key to continue --"; char txt_cmdline_hit_any_key[] = "Press any key to continue..."; /* * rcfile.c */ char txt_opt_autosave[] = "1. Auto save : "; char txt_opt_start_editor_offset[] = "2. Editor Offset : "; char txt_opt_mark_saved_read[] = "3. Mark saved read : "; char txt_opt_confirm_action[] = "4. Confirm command : "; char txt_opt_draw_arrow[] = "5. Draw arrow : "; char txt_opt_print_header[] = "6. Print header : "; char txt_opt_pos_first_unread[] = "7. Goto 1st unread : "; char txt_opt_page_scroll[] = "8. Scroll full page: "; char txt_opt_catchup_groups[] = "9. Catchup on quit : "; char txt_opt_thread_arts[] = "10 Thread articles : "; char txt_opt_show_only_unread[] = "11 Show only unread: "; char txt_opt_show_description[] = "12 Show description: "; char txt_opt_show_author[] = "13 Show author : "; char txt_opt_process_type[] = "14 Process type : "; char txt_opt_sort_type[] = "15 Sort article by : "; char txt_opt_savedir[] = "16 Save directory : "; char txt_opt_maildir[] = "17 Mail directory : "; char txt_opt_printer[] = "18 Printer : "; char txt_options_menu[] = "Options Menu"; char txt_show_from_none[] = "None"; char txt_show_from_addr[] = "Addr"; char txt_show_from_name[] = "Name"; char txt_show_from_both[] = "Both"; char txt_post_process_none[] = "None"; char txt_post_process_sh[] = "Shell archive"; char txt_post_process_uudecode[] = "Uudecode"; #ifdef M_AMIGA char txt_post_process_uud_lst_zoo[] = "Uudecode & list lharc archive"; char txt_post_process_uud_ext_zoo[] = "Uudecode & extract lharc archive"; #else char txt_post_process_uud_lst_zoo[] = "Uudecode & list zoo archive"; char txt_post_process_uud_ext_zoo[] = "Uudecode & extract zoo archive"; #endif char txt_post_process_uud_lst_zip[] = "Uudecode & list zip archive"; char txt_post_process_uud_ext_zip[] = "Uudecode & extract zip archive"; char txt_sort_by_nothing[] = "Nothing"; char txt_sort_by_subj_descend[] = "Subject: field (descending)"; char txt_sort_by_subj_ascend[] = "Subject: field (ascending)"; char txt_sort_by_from_descend[] = "From: field (descending)"; char txt_sort_by_from_ascend[] = "From: field (ascending)"; char txt_sort_by_date_descend[] = "Date: field (descending)"; char txt_sort_by_date_ascend[] = "Date: field (ascending)"; char txt_help_autosave[] = "Auto save article/thread by Archive-name: header. toggles & sets."; char txt_help_start_editor_offset[] = "Start editor with line offset. toggles & sets."; char txt_help_confirm_action[] = "Ask for command confirmation. toggles & sets."; char txt_help_print_header[] = "By printing print all/part of header. toggles & sets."; char txt_help_pos_first_unread[] = "Put cursor at first/last unread art in groups. toggles & sets."; char txt_help_show_author[] = "Show Subject & From (author) fields in group menu. toggles & sets."; char txt_help_draw_arrow[] = "Draw -> or highlighted bar for selection. toggles & sets."; char txt_help_mark_saved_read[] = "Mark saved articles/threads as read. toggles & sets."; char txt_help_page_scroll[] = "Scroll half/full page of groups/articles. toggles & sets."; char txt_help_catchup_groups[] = "Ask to mark groups read when quiting. toggles & sets."; char txt_help_thread_arts[] = "Enable/disable threading of articles in all groups. toggles & sets."; char txt_help_show_only_unread[] = "Show all articles or only unread articles. toggles & sets."; char txt_help_show_description[] = "Show short description for each newsgroup. toggles & sets."; char txt_help_post_proc_type[] = "Post process (ie. unshar) saved article/thread. toggles & sets."; char txt_help_sort_type[] = "Sort articles by Subject, From or Date fields. toggles & sets."; char txt_help_savedir[] = "The directory where you want articles/threads saved."; char txt_help_maildir[] = "The directory where articles/threads are to be saved in mailbox format."; char txt_help_printer[] = "The printer program with options that is to be used to print articles/threads."; char txt_select_rcfile_option[] = "Select option by entering number before text. Any other key to save."; /* * save.c */ char txt_post_processing[] = "Post processing..."; char txt_post_processing_finished[] = "-- post processing completed --"; char txt_deleting[] = "Deleting..."; char txt_uudecoding[] = "Uudecoding..."; char txt_extracting_shar[] ="\r\nExtracting %s...\r\n"; char txt_delete_processed_files[] = "Delete saved files that have been post processed? (y/n): "; char txt_post_processing_failed[] = "Post processing failed"; char txt_testing_archive[] = "\r\n\r\nTesting %s archive...\r\n"; char txt_listing_archive[] = "\r\n\r\nListing %s archive...\r\n"; char txt_extracting_archive[] = "\r\n\r\nExtracting %s archive...\r\n"; char txt_checksum_of_file[] = "\r\n\r\nChecksum of %s...\r\n\r\n"; /* * search.c */ char txt_searching[] = "Searching..."; char txt_searching_body[] = "Searching ('q' to abort)... "; /* * select.c */ char txt_reading_all_groups[] = "Reading all groups..."; char txt_reading_new_groups[] = "Reading unread groups..."; char txt_moving[] = "Moving %s..."; #ifdef NO_REGEX char txt_subscribe_pattern[] = "Enter subscribe pattern> "; char txt_unsubscribe_pattern[] = "Enter unsubscribe pattern> "; #else char txt_subscribe_pattern[] = "Enter regex subscribe pattern> "; char txt_unsubscribe_pattern[] = "Enter regex unsubscribe pattern> "; #endif char txt_subscribing[] = "Subscribing..."; char txt_subscribing_to[] = "Subscribing to %s..."; char txt_unsubscribing[] = "Unsubscribing..."; char txt_unsubscribing_from[] = "Unsubscribing from %s..."; char txt_subscribed_num_groups[] = "subscribed to %d groups"; char txt_unsubscribed_num_groups[] = "unsubscribed from %d groups"; char txt_del_group_in_newsrc[] = "Delete %s from .newsrc? (y/n): "; char txt_group_deleted[] = "Group %s deleted"; char txt_group_undeleted[] = "Group undeleted"; char txt_mark_group_read[] = "Mark group %s as read? (y/n): "; char txt_no_groups_to_delete[] = "No groups to delete"; char txt_reset_newsrc[] = "Reset newsrc? (y/n): "; char txt_post_newsgroup[] = "Post newsgroup> "; char txt_yanking_all_groups[] = "Yanking in all groups..."; char txt_yanking_sub_groups[] = "Yanking in subscribed to groups..."; char txt_no_groups_to_read[] = "No more groups to read"; char txt_added_groups[] = "Added %d group%s"; char txt_plural[] = "s"; char txt_no_groups_to_yank_in[] = "No more groups to yank in"; char txt_group_selection[] = "Group Selection"; char txt_spooldir_selection[] = "Spooldir Selection (%d)"; char txt_select_group[] = "Select group> "; char txt_select_spooldir[] = "Select spooldir> "; char txt_help_g_4[] = "4$ Select group 4 ($=select last group)\r\n"; char txt_help_g_ctrl_r[] = "^R Reset .newsrc\r\n"; char txt_help_g_ctrl_k[] = "^KZ Delete (Z=undelete) group from .newsrc\r\n"; char txt_help_g_cr[] = " Read current group\r\n"; char txt_help_g_c[] = "c Mark group as all read\r\n"; char txt_help_g_d[] = "d Toggle display of groupname or groupname and description\r\n"; char txt_help_g_l[] = "l List & select another spooldir\r\n"; char txt_help_g_tab[] = "n Goto next group with unread news and enter it\r\n"; char txt_help_n[] = "N Goto next group with unread news\r\n"; char txt_help_g_q[] = "qQ Quit\r\n"; char txt_help_g_r[] = "r Toggle display to show all / only unread subscribed to groups\r\n"; char txt_help_W[] = "W List articles posted by user\r\n"; char txt_help_g_y[] = "y Yank in subscribed/unsubscribed from .newsrc\r\n"; char txt_help_g_z[] = "z Mark current group as unread\r\n"; char txt_help_y[] = "Y Yank in active file to see any new news\r\n"; char txt_help_g_search[] = "/? Group forward (?=backward) search\r\n"; char txt_newsgroup[] = "Goto newsgroup [%s]> "; char txt_newsgroup_position[] = "Position %s in group list (1,2,..,$) [%d]> "; /* * signal.c */ char txt_resizing_window[] = "resizing window"; char txt_suspended_message[] = "\nStopped. Type 'fg' to restart TIN\n"; /* * spooldir.c */ char txt_spooldirs_not_supported[] = "Multiple spooldirs are not supported"; char txt_no_spooldirs[] = "No spooldirs"; char txt_spooldir_server_error_1[] = "Server does not appear to support the spooldir command\n"; char txt_spooldir_server_error_2[] = "Reconfigure the news reader or the server & try again.\n"; char txt_cannot_change_spooldir[] = "Cannot change to valid spooldir. Exiting..."; char txt_changing_spooldir_to[] = "Changing spooldir to"; /* * thread.c */ char txt_no_resps_in_thread[] = "No responses to list in current thread"; char txt_help_t_0[] = "0 Goto the base article in current thread\r\n"; char txt_help_t_4[] = "4$ Goto response 4 ($=goto last response) in current thread\r\n"; char txt_help_t_cr[] = " Read current response\r\n"; char txt_help_t_tab[] = " Goto next unread response\r\n"; char txt_help_t_K[] = "K Mark thread as read & return\r\n"; /* * xref.c */ char txt_processing_xrefs[] = "Processing Xref's..."; *[SRC.TIN-1_22]MAIL.C;2+,. // 4 -d0@123KPWO 56oŭ7$ƭ89]VG/HJ/* * Project : tin - a Usenet reader * Module : mail.c * Author : I.Lea * Created : 02-10-92 * Updated : 14-05-93 * Notes : Mail handling routines for creating pseudo newsgroups * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" /* * Load the mail active file into active[] */ void read_mail_active_file () { #if !defined(INDEX_DAEMON) && defined(HAVE_MAIL_HANDLING) FILE *fp; char buf[LEN]; char spooldir[PATH_LEN]; int i; long h, min, max; if ((update && update_fork) || ! update) { wait_message (txt_reading_mail_active_file); } /* * Open the mail active file & if we can't create an empty one */ if ((fp = open_mail_active_fp ("r")) == (FILE *) 0) { if (cmd_line) { fputc ('\n', stderr); } error_message (txt_cannot_open, mail_active_file); write_mail_active_file (); return; } if (num_active == -1) { num_active = 0; for (i = 0; i < TABLE_SIZE; i++) { group_hash[i] = -1; } } while (fgets (buf, sizeof (buf), fp) != NULL) { if (! parse_active_line (buf, &max, &min, spooldir)) { continue; } /* * Load group into group hash table */ if (num_active >= max_active) { expand_active (); } h = hash_groupname (buf); if (group_hash[h] == -1) { group_hash[h] = num_active; } else { /* hash linked list chaining */ for (i=group_hash[h]; active[i].next >= 0; i=active[i].next) { if (strcmp (active[i].name, buf) == 0) { goto read_mail_active_continue; /* kill dups */ } } if (strcmp (active[i].name, buf) == 0) goto read_mail_active_continue; active[i].next = num_active; } /* * Load group info. */ active[num_active].type = GROUP_TYPE_MAIL; active[num_active].name = str_dup (buf); active[num_active].spooldir = str_dup (spooldir); active[num_active].description = (char *) 0; active[num_active].max = max; active[num_active].min = min; active[num_active].moderated = 'y'; active[num_active].next = -1; /* hash chaining */ active[num_active].my_group = UNSUBSCRIBED; /* not in my_group[] yet */ active[num_active].newsrcmax = max; active[num_active].newsrcmin = min; active[num_active].newsrcsize = 0; active[num_active].newsrcupdate = FALSE; active[num_active].newsrc = NULL; active[num_active].unread = 0; num_active++; read_mail_active_continue:; } fclose (fp); if ((cmd_line && ! update && ! verbose) || (update && update_fork)) { wait_message ("\n"); } #endif /* INDEX_DAEMON */ } /* * Write the mail groups from active[] to ~/.tin/mactive */ void write_mail_active_file () { #if !defined(INDEX_DAEMON) && defined(HAVE_MAIL_HANDLING) FILE *fp; register int i; if ((fp = open_mail_active_fp ("w")) == (FILE *) 0) { return; } /* * Load group into group hash table */ fprintf (fp, "# Mail active file. Format is like news active file:\n"); fprintf (fp, "# groupname max.artnum min.artnum /maildir\n#\n"); for (i = 0 ; i < num_active ; i++) { if (active[i].type == GROUP_TYPE_MAIL) { fprintf (fp, "%s %08ld %08ld %s\n", active[i].name, active[i].max, active[i].min, active[i].spooldir); } } fclose (fp); #endif /* INDEX_DAEMON */ } /* * Load the text description from ~/.tin/mailgroups for each mail group into * the active[] array. */ void read_mailgroups_file () { #ifndef INDEX_DAEMON FILE *fp; if (show_description == FALSE || save_news || catchup) { return; } if ((fp = open_mailgroups_fp ()) != (FILE *) 0) { wait_message (txt_reading_mailgroups_file); read_groups_descriptions (fp, (FILE *) 0); fclose (fp); if (cmd_line && ! update && ! verbose) { wait_message ("\n"); } } #endif /* INDEX_DAEMON */ } /* * Load the text description from LIBDIR/newsgroups for each group into the * active[] array. Save a copy locally if reading via NNTP to save bandwidth. */ void read_newsgroups_file () { #ifndef INDEX_DAEMON FILE *fp; FILE *fp_save = (FILE *) 0; if (show_description == FALSE || save_news || catchup) { return; } wait_message (txt_reading_newsgroups_file); if ((fp = open_newsgroups_fp ()) != (FILE *) 0) { if (read_news_via_nntp && ! read_local_newsgroups_file) { #ifdef VMS fp_save = fopen (local_newsgroups_file, "w", "fop=cif"); #else fp_save = fopen (local_newsgroups_file, "w"); #endif } read_groups_descriptions (fp, fp_save); fclose (fp); if (fp_save) { fclose (fp_save); read_local_newsgroups_file = TRUE; } } if (cmd_line && ! update && ! verbose) { wait_message ("\n"); } #endif /* INDEX_DAEMON */ } /* * Read groups descriptions from opened file & make local backup copy * of all groups that don't have a 'x' in the active file moderated * field & if reading groups of type GROUP_TYPE_NEWS. */ void read_groups_descriptions (fp, fp_save) FILE *fp; FILE *fp_save; { char buf[LEN]; char group[PATH_LEN]; char *p, *q; int i; while (fgets (buf, sizeof (buf), fp) != NULL) { if (buf[0] == '#' || buf[0] == '\n') { continue; } p = (char *) strchr (buf, '\n'); if (p != (char *) 0) { *p = '\0'; } for (p = buf, q = group ; *p && *p != ' ' && *p != '\t' ; p++, q++) { *q = *p; } *q = '\0'; while (*p == '\t' || *p == ' ') { p++; } i = find_group_index (group); if (i >= 0 && active[i].description == (char *) 0) { active[i].description = str_dup (p); if (active[i].type == GROUP_TYPE_NEWS) { if (fp_save && read_news_via_nntp && ! read_local_newsgroups_file) { fprintf (fp_save, "%s\n", buf); } } } } } ;*[SRC.TIN-1_22]MAIN.C;6+,+.// 4-d0@123KPWO56Io7@so89]VG/HJ/* * Project : tin - a Usenet reader * Module : main.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 05-09-92 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" static char **cmdargs; static int num_cmdargs; static int max_cmdargs; /* * OK lets start the ball rolling... */ void main (argc, argv) int argc; char *argv[]; { int start_groupnum = 0; cmd_line = TRUE; debug = 0; /* debug OFF */ set_signal_handlers (); base_name (argv[0], progname); #ifdef VMS argv[0] = progname; #endif sprintf (page_header, "%s %s PL%s %s", progname, VERSION, PATCHLEVEL, OS); sprintf (cvers, txt_copyright_notice, page_header); #if defined(NNTP_ONLY) || defined(CDROM_ONLY) read_news_via_nntp = TRUE; #else /* * rtin/cdtin so read news remotely via NNTP */ if (progname[0] == 'r' || (progname[0] == 'c' && progname[1] == 'd' )) { # ifdef NNTP_ABLE read_news_via_nntp = TRUE; # else error_message (txt_option_not_enabled, "-DNNTP_ABLE"); exit (1); # endif } #endif /* * Set up initial array sizes, char *'s: homedir, newsrc, etc. */ init_alloc (); hash_init (); init_selfinfo (); /* * Process envargs & command l ine options */ read_cmd_line_options (argc, argv); if (! read_news_via_nntp) newsrc_active = FALSE; if (update_fork || (update && verbose) || !update) { error_message (cvers, ""); } /* * If specified connect to (cdrom pseudo) nntp server */ if (nntp_open () == -1) { exit (1); } /* * Log username info to local/central logfile (NNTP XUSER) */ log_user (); /* * Read message of the day file from newsadmin */ read_motd_file (); /* * Load the mail & news active files into active[] */ read_mail_active_file (); read_news_active_file (); /* * Load the group specific attributes file into active[] */ read_attributes_file (); /* * Quick post an article & exit if -w specified */ if (post_article_and_exit) { setup_screen (); quick_post_article (); tin_done (0); } /* * Read text descriptions for mail & news groups from * ~/.tin/mailgroups & LIBDIR/newsgroups respectively */ read_mailgroups_file (); read_newsgroups_file (); debug_print_active (); if (create_mail_save_dirs ()) { write_rcfile (); } if (! read_cmd_line_groups ()) { backup_newsrc (); read_newsrc (TRUE); toggle_my_groups (show_only_unread_groups, ""); } /* * Read in users kill/auto-select (hot) file */ killed_articles = read_kill_file (); /* * Check/start if any new/unread articles */ start_groupnum = check_for_any_new_news (check_any_unread, start_any_unread); /* * Mail any new articles to specified user * or * Save any new articles to savedir structure for later reading */ save_or_mail_new_news (); /* * Update index files */ update_index_files (); /* * Set up screen and switch to raw mode */ if (! InitScreen ()) { error_message (txt_screen_init_failed, progname); exit (1); } setup_screen (); /* * If first time print welcome screen and auto-subscribe * to groups specified in /usr/lib/news/subscribe locally * or via NNTP if reading news remotely (LIST SUBSCRIBE) */ if (created_rcdir && !update) { show_intro_page (); } /* * Work loop */ selection_index (start_groupnum); } /* * process command line options */ void read_cmd_line_options (argc, argv) int argc; char *argv[]; { int ch; envargs (&argc, &argv, "TINRC"); #ifdef INDEX_DAEMON while ((ch = getopt (argc, argv, "D:f:hI:PvV")) != EOF) { #else while ((ch = getopt (argc, argv, "cD:f:hHI:m:M:np:PqrRs:SuUvVwzZ")) != EOF) { #endif switch (ch) { case 'c': catchup = TRUE; update = TRUE; break; case 'D': /* debug mode 1=NNTP 2=ALL */ #ifdef DEBUG debug = atoi (optarg); #else error_message (txt_option_not_enabled, "-DDEBUG"); exit (1); #endif break; case 'f': /* active (tind) / newsrc (tin) file */ #ifdef INDEX_DAEMON my_strncpy (news_active_file, optarg, sizeof (news_active_file)); #else my_strncpy (newsrc, optarg, sizeof (newsrc)); #endif break; case 'H': show_intro_page (); exit (1); break; #if !defined(NNTP_ONLY) case 'I': my_strncpy (index_newsdir,  optarg, sizeof (index_newsdir)); my_mkdir (index_newsdir, 0777); break; #endif case 'm': my_strncpy (default_maildir, optarg, sizeof (default_maildir)); break; case 'M': /* mail new news to specified user */ my_strncpy (mail_news_user, optarg, sizeof (mail_news_user)); mail_news = TRUE; update = TRUE; break; case 'n': #ifdef NNTP_ABLE newsrc_active = TRUE; #else error_message (txt_option_not_enabled, "-DNNTP_ABLE"); exit (1); #endif break; case 'p': my_strncpy (cmd_line_printer, optarg, sizeof (cmd_line_printer)); break; case 'P': /* stat every art for a through purge */ purge_index_files = TRUE; break; case 'q': check_for_new_newsgroups = FALSE; break; case 'r': /* read news remotely from default NNTP server */ #ifdef NNTP_ABLE read_news_via_nntp = TRUE; #else error_message (txt_option_not_enabled, "-DNNTP_ABLE"); exit (1); #endif break; case 'R': /* read news saved by -S option */ error_message ("%s: Option -R not yet implemented.", progname); exit (1); break; case 's': my_strncpy (default_savedir, optarg, sizeof (default_savedir)); break; case 'S': /* save new news to dir structure */ save_news = TRUE; update = TRUE; break; case 'u': /* update index files */ update = TRUE; show_description = FALSE; break; case 'U': /* update index files in background */ update_fork = TRUE; update = TRUE; break; case 'v': /* verbose mode */ verbose = TRUE; break; case 'V': #if defined(__DATE__) && defined(__TIME__) sprintf (msg, "Version: %s PL%s %s %s", VERSION, PATCHLEVEL, __DATE__, __TIME__); #else sprintf (msg, "Version: %s PL%s", VERSION, PATCHLEVEL); #endif error_message (msg, ""); exit (1); break; case 'w': /* post article & exit */ post_article_and_exit = TRUE; break; case 'z': start_any_unread = TRUE; update = TRUE; break; case 'Z': check_any_unread = TRUE; update = TRUE; break; case 'h': case '?': default: usage (progname); exit (1); } } cmdargs = argv; num_cmdargs = optind; max_cmdargs = argc; } /* * usage */ void usage (progname) char *progname; { #ifndef INDEX_DAEMON error_message ("%s A Usenet reader.\n", cvers); #else error_message ("%s Tin index file daemon.\n", cvers); #endif error_message ("Usage: %s [options] [newsgroups]", progname); #ifndef INDEX_DAEMON error_message (" -c mark all news as read in subscribed newsgroups (batch mode)", ""); error_message (" -f file subscribed to newsgroups file [default=%s]", newsrc); #else error_message (" -f file active newsgroups file [default=%s]", newsrc); #endif error_message (" -h help", ""); #ifndef INDEX_DAEMON error_message (" -H help information about %s", progname); #endif error_message (" -I dir news index file directory [default=%s]", index_newsdir); #ifndef INDEX_DAEMON error_message (" -m dir mailbox directory [default=%s]", default_maildir); error_message (" -M user mail new news to specified user (batch mode)", ""); #ifdef NNTP_ABLE error_message (" -n only read subscribed .newsrc groups from NNTP server", ""); #endif error_message (" -p file print program with options [default=%s]", DEFAULT_PRINTER); error_message (" -P purge any expired articles from index files", ""); error_message (" -q quick start by not checking for new newsgroups", ""); # if defined(NNTP_ABLE) && !defined(NNTP_ONLY) if (! read_news_via_nntp) { error_message (" -r read news remotely from default NNTP server", ""); } # endif /* NNTP_ABLE */ error_message (" -R read news saved by -S option", ""); error_message (" -s dir save news directory [default=%s]", default_savedir); error_message (" -S save new news for later reading (batch mode)", ""); # if !defined(NNTP_ONLY) error_message (" -u update index files (batch mode)", ""); error_message (" -U update index files in the background while reading news", ""); # endif /* NNTP_ONLY */ #else error_message (" -P purge any expired articles from index files", ""); #endif /* INDEX_DAEMON */ error_message (" -v verbose output for batch mode options", ""); #ifndef INDEX_DAEMON error_message (" -w post an article and exit", ""); error_message (" -z start if any unread news", ""); error_message (" -Z return status indicating if any unread news (batch mode)", ""); #endif error_message ("\nMail bug reports/comments to %s", BUG_REPORT_ADDRESS); } /* * check/start if any new/unread articles */ int check_for_any_new_news (check_any_unread, start_any_unread) int check_any_unread; int start_any_unread; { int i = 0; if (check_any_unread) { i = check_start_save_any_news (CHECK_ANY_NEWS); exit (i); } if (start_any_unread) { i = check_start_save_any_news (START_ANY_NEWS); if (i == -1) { /* no new/unread news so exit */ exit (0); } update = FALSE; } return (i); } /* * mail any new articles to specified user * or * save any new articles to savedir structure for later reading */ void save_or_mail_new_news () { int i; if (mail_news || save_news) { i = catchup; /* set catchup to FALSE */ catchup = FALSE; do_update (); catchup = i; /* set catchup to previous value */ if (mail_news) { check_start_save_any_news (MAIL_ANY_NEWS); } else { check_start_save_any_news (SAVE_ANY_NEWS); } tin_done (0); } } /* * update index files */ void update_index_files () { if (update || update_fork) { if (!catchup && (read_news_via_nntp && xindex_supported)) { error_message ("%s: Updating of index files not supported", progname); tin_done (1); } cCOLS = 132; /* set because curses has not started */ #ifdef HAVE_FORK if (update_fork) { catchup = FALSE; /* turn off msgs when running forked */ verbose = FALSE; switch (fork ()) { /* fork child to update indexes in background */ case -1: /* error forking */ perror_message ("Failed to start background indexing process", ""); break; case 0: /* child process */ create_index_lock_file (lock_file); process_id = getpid (); #ifdef BSD setpgrp (0, process_id); /* reset process group leader to this process */ # ifdef TIOCNOTTY { int fd; if ((fd = open ("/dev/tty", O_RDWR)) >= 0) { ioctl (fd, TIOCNOTTY, (char *) NULL); close (fd); } } # endif #else setpgrp (); signal (SIGHUP, SIG_IGN); /* make immune from process group leader death */ #endif signal (SIGQUIT, SIG_IGN); /* stop indexing being interrupted */ signal (SIGALRM, SIG_IGN); /* stop indexing resyning active file */ nntp_open (); /* connect server if we are using nntp */ default_thread_arts = FALSE; /* stop threading to run faster */ do_update (); tin_done (0); break; default: /* parent process*/ break; } update = FALSE; } else #endif /* HAVE_FORK */ { create_index_lock_file (lock_file); default_thread_arts = FALSE; /* stop threading to run faster */ do_update (); tin_done (0); } } } /* * display page of general info. for first time user. */ void show_intro_page () { if (cmd_line) { wait_message (cvers); } else { ClearScreen (); center_line (0, TRUE, cvers); Raw (FALSE); } printf ("\n\nWelcome to tin, a full screen threaded Netnews reader. It can read news locally\n"); printf ("(ie. /news) or remotely (-r option) from a NNTP (NTr~ TIN-1_22.BCK+d[SRC.TIN-1_22]MAIN.C;6etwork News Transport\n"); printf ("Protocol) server. tin -h lists the available command line options.\n\n"); printf ("Tin has five newsreading levels, the newsgroup selection page, the spooldir\n"); printf ("selection page, the group index page, the thread listing page and the article\n"); printf ("viewer. Help is available at each level by pressing the 'h' command.\n\n"); printf ("Move up/down by using the terminal arrow keys or 'j' and 'k'. Use PgUp/PgDn or\n"); printf ("Ctrl-U and Ctrl-D to page up/down. Enter a newsgroup by pressing RETURN/TAB.\n\n"); printf ("Articles, threads, tagged articles or articles matching a pattern can be mailed\n"); printf ("('m' command), printed ('o' command), saved ('s' command), piped ('|' command).\n"); printf ("Use the 'w' command to post a news article, the 'f'/'F' commands to post a\n"); printf ("follow-up to an existing news article and the 'r'/'R' commands to reply via\n"); printf ("mail to an existing news articles author. The 'M' command allows the operation\n"); printf ("of tin to be configured via a menu.\n\n"); printf ("For more information read the manual page, README, INSTALL, TODO and FTP files.\n"); printf ("Please send bug reports/comments to the programs author with the 'R' command.\n"); fflush (stdout); if (! cmd_line) { Raw (TRUE); continue_prompt (); } } int read_cmd_line_groups () { char buf[PATH_LEN]; int matched = FALSE; int num = num_cmdargs; register int i; if (num < max_cmdargs) { group_top = 0; while (num < max_cmdargs) { sprintf (buf, "Matching %s groups...", cmdargs[num]); wait_message (buf); for (i = 0 ; i < num_active ; i++) { if (wildmat (active[i].name, cmdargs[num])) { if (add_group (active[i].name, TRUE) < 0) { error_message (txt_not_in_active_file, active[i].name); } } } num++; } matched = TRUE; } return (matched); } N*[SRC.TIN-1_22]MAKEFILE.;81+,. // 4 L-d0123KPWO 56픒7`s89IʒG/HJ# Makefile for tin - for tin compiler flag options read INSTALL and README. # CC = gcc DEFINES = VMS,MULTINET,SLOW_SCREEN_UPDATE,NNTP_DEFAULT_SERVER="""news.duq.edu""" #CFLAGS = /noopt/debug/include=([.vms],[.sio])/define=($(DEFINES)) CFLAGS = /opt=2/warn/include=([.vms],[.sio])/define=($(DEFINES)) LFLAGS = /nomap#/map/debug LIBS = [.sio]libsio/libr LD = link YACC = bison/fixed # From: address in posted articles (don't use both - read the INSTALL file) NNTP_INEWS_GATEWAY= NNTP_INEWS_DOMAIN= #.erlm.siemens.de PROJECT = tin EXE = tin.exe EXED = tind.exe MAKE = make #BASE_VER= 1.21/tin-1.21 BASE_VER= 170993 VER = 1.22 MAIL_ADDR = "iain.lea@erlm.siemens.de" HFILES = config.h tin.h extern.h nntplib.h proto.h stpwatch.h amiga.h os_2.h \ win32.h CFILES = active.c amiga.c art.c curses.c debug.c envarg.c feed.c getline.c \ group.c hashstr.c help.c inews.c init.c kill.c lang.c mail.c \ main.c memory.c misc.c newsrc.c nntplib.c open.c os_2.c page.c\ parsdate.y post.c prompt.c rcfile.c save.c screen.c search.c \ select.c sigfile.c signal.c spooldir.c strftime.c thread.c \ wildmat.c win32.c xref.c vms.c OFILES = active.obj amiga.obj art.obj curses.obj debug.obj envarg.obj \ feed.obj getline.obj group.obj hashstr.obj help.obj inews.obj \ init.obj kill.obj lang.obj mail.obj main.obj memory.obj misc.obj \ newsrc.obj nntplib.obj open.obj os_2.obj page.obj parsdate.obj \ post.obj prompt.obj rcfile.obj save.obj screen.obj search.obj \ select.obj sigfile.obj signal.obj spooldir.obj strftime.obj \ thread.obj wildmat.obj win32.obj xref.obj vms.obj ALL_FILES = $(HFILES) patchlev.h $(CFILES) .c.obj: $(CC) $(CFLAGS) /obj=$*.obj $*.c # For VAX/VMS vms: linkit vmslibcmd = @[.vms]makevmslib siocmd = @[.sio]makesio vmslib: $$ $(vmslibcmd) sio: $$ $(siocmd) linkit: vmslib sio $(OFILES) @echo "Linking $(EXE) v$(VER)..." $(LD) $(LFLAGS) /exe=$(EXE) objs.opt/opt,[.vms]gcc-multinet.opt/opt,$(LIBS) @dir/size $(EXE) test: test.obj vms.obj $(LD) $(LFLAGS) /exe=test.exe $^,gnu_cc:[000000]gcclib/lib tags: @echo "Generating tags (results in ./tags)..." @del tags;* @etags $(HFILES) patchlev.h $(CFILES) active.obj: active.c $(HFILES) amiga.obj: amiga.c $(HFILES) art.obj: art.c $(HFILES) curses.obj: curses.c $(HFILES) debug.obj: debug.c $(HFILES) envarg.obj: envarg.c $(HFILES) feed.obj: feed.c $(HFILES) getline.obj: getline.c $(HFILES) group.obj: group.c $(HFILES) hashstr.obj: hashstr.c $(HFILES) help.obj: help.c $(HFILES) inews.obj: inews.c $(HFILES) init.obj: init.c $(HFILES) kill.obj: kill.c $(HFILES) lang.obj: lang.c $(HFILES) mail.obj: mail.c $(HFILES) patchlev.h main.obj: main.c $(HFILES) patchlev.h memory.obj: memory.c $(HFILES) misc.obj: misc.c $(HFILES) newsrc.obj: newsrc.c $(HFILES) nntplib.obj: nntplib.c $(HFILES) open.obj: open.c $(HFILES) patchlev.h os_2.obj: os_2.c $(HFILES) page.obj: page.c $(HFILES) parsdate.obj: parsdate.y $(HFILES) post.obj: post.c $(HFILES) patchlev.h prompt.obj: prompt.c $(HFILES) rcfile.obj: rcfile.c $(HFILES) save.obj: save.c $(HFILES) screen.obj: screen.c $(HFILES) search.obj: search.c $(HFILES) select.obj: select.c $(HFILES) sigfile.obj: sigfile.c $(HFILES) signal.obj: signal.c $(HFILES) spooldir.obj: spooldir.c $(HFILES) strftime.obj: strftime.c $(HFILES) thread.obj: thread.c $(HFILES) wildmat.obj: wildmat.c win32.obj: win32.c win32.h xref.obj: xref.c $(HFILES) vms.obj: vms.c tin.h *[SRC.TIN-1_22]MANIFEST.;1+,i.// 4-d0@123KPWO56`3t7$WFꑗ89 G/HJMANIFEST for tin-1.22 (Thu Sep 23 09:29:16 CDT 1993) ---------------------------------------------------- 22540 Makefile 2671 Makefile.ami 4099 Makefile.bcc 10751 Makefile.icc 106 MANIFEST 4108 README 4987 README.AMI 5650 README.OS2 6728 CHANGES 15549 INSTALL 3913 HACKERS 7983 TODO 2945 FTP 5458 BETA.3 4976 strftime.3 58499 tin.1 2196 wildmat.3 82122 tin.nrf 1345 tin.lsm 1800 actived.c 1263 README.NNTP 3337 INSTALL.NNTP 2945 common.patch 31371 server.patch 3672 xindex.c 3149 xmotd.c 2469 xoverview.c 1344 xuser.c 7467 config.h 28195 tin.h 22923 extern.h 5101 nntplib.h 30773 proto.h 1271 stpwatch.h 1865 amiga.h 2305 os_2.h 1245 win32.h 758 patchlev.h 32293 active.c 5907 amiga.c 35120 art.c 15034 curses.c 6687 debug.c 2828 envarg.c 16318 feed.c 11433 getline.c 35452 group.c 2532 hashstr.c 7773 help.c 9222 inews.c 21880 init.c 13894 kill.c 31082 lang.c 5777 mail.c 14187 main.c 10908 memory.c 31252 misc.c 27172 newsrc.c 21781 nntplib.c 20171 open.c 6281 os_2.c 27536 page.c 22914 parsdate.y 40096 post.c 3161 prompt.c 33579 rcfile.c 29414 save.c 3146 screen.c 9686 search.c 29500 select.c 5110 sigfile.c 11697 signal.c 14469 spooldir.c 5888 strftime.c 22811 thread.c 4993 wildmat.c 7611 win32.c 2716 xref.c 1059190 total *[SRC.TIN-1_22]MEMORY.C;2+,.// 4-d0@123KPWO56 c+膗7`膗89]VG/HJ,/* * Project : tin - a Usenet reader * Module : memory.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 05-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" /* * Dynamic arrays maximum & current sizes * num_* values are one past top of used part of array */ int max_active = 0; int num_active = -1; int max_active_size = 0; int num_active_size = 0; int max_art = 0; int max_kill = 0; int num_kill = 0; int max_save = 0; int num_save = 0; int max_spooldir = 0; int num_spooldir = 0; /* * Dynamic arrays */ int *my_group; /* .newsrc --> active[] */ long *base; /* base articles for each thread */ struct t_group *active; /* active newsgroups */ struct t_active_size *active_size; /* active file sizes on differnet servers */ struct t_article *arts; /* articles headers in current group */ struct t_save *save; /* sorts articles before saving them */ struct t_spooldir *spooldirs; /* spooldirs on NNTP server (cdrom) */ /* * Dynamic table management * These settings are memory conservative: small initial allocations * and a 50% expansion on table overflow. A fast vm system with * much memory might want to start with higher initial allocations * and a 100% expansion on overflow, especially for the arts[] array. */ void init_alloc () { /* * active file arrays */ max_active = get_active_num (); max_active_size = DEFAULT_ACTIVE_SIZE_NUM; active = (struct t_group *) my_malloc ((unsigned) sizeof(*active) * max_active); active_size = (struct t_active_size *) my_malloc ((unsigned) sizeof(*active_size) * max_active_size); my_group = (int *) my_malloc ((unsigned) sizeof(int) * max_active); /* * article headers array */ max_art = DEFAULT_ARTICLE_NUM; arts = (struct t_article *) my_malloc ((unsigned) sizeof(*arts) * max_art); base = (long *) my_malloc ((unsigned) sizeof(long) * max_art); /* * kill file array */ max_kill = DEFAULT_KILL_NUM; killf = (struct t_kill *) my_malloc ((unsigned) sizeof(*killf) * max_kill); /* * save file array */ max_save = DEFAULT_SAVE_NUM; save = (struct t_save *) my_malloc ((unsigned) sizeof(*save) * max_save); /* * spooldirs array */ max_spooldir = DEFAULT_SPOOLDIR_NUM; spooldirs = (struct t_spooldir *) my_malloc ((unsigned) sizeof(*spooldirs) * max_spooldir); screen = (struct t_screen *) 0; } void expand_art () { max_art += max_art / 2; /* increase by 50% */ arts = (struct t_article *) my_realloc ((char *) arts, (unsigned) sizeof(*arts) * max_art); base = (long *) my_realloc ((char *) base, (unsigned) sizeof(long) * max_art); } void expand_active () { max_active += max_active / 2; /* increase by 50% */ if (active == (struct t_group *) 0) { active = (struct t_group *) my_malloc ((unsigned) sizeof (*active) * max_active); my_group = (int *) my_malloc ((unsigned) sizeof (int) * max_active); } else { active = (struct t_group *) my_realloc((char *) active, (unsigned) sizeof (*active) * max_active); my_group = (int *) my_realloc((char *) my_group, (unsigned) sizeof (int) * max_active); } } void expand_kill () { max_kill += max_kill / 2; /* increase by 50% */ killf = (struct t_kill *) my_realloc((char *) killf, (unsigned) sizeof (struct t_kill) * max_kill); } void expand_save () { max_save += max_save / 2; /* increase by 50% */ save = (struct t_save *) my_realloc((char *) save, (unsigned) sizeof (struct t_save) * max_save); } void expand_spooldirs () { max_spooldir += max_spooldir / 2; /* increase by 50% */ spooldirs = (struct t_spooldir *) my_realloc((char *) spooldirs, (unsigned) sizeof (struct t_spooldir) * max_spooldir); } void expand_active_size () { max_active_size += max_active_size / 2; /* increase by 50% */ active_size = (struct t_active_size *) my_realloc((char *) active_size, (unsigned) sizeof(struct t_active_size) * max_active_size); } void init_screen_array (allocate) int allocate; { int i; if (allocate) { screen = (struct t_screen *) my_malloc ( (unsigned) sizeof (struct t_screen) * cLINES+1); for (i=0 ; i < cLINES ; i++) { screen[i].col = (char *) my_malloc ((unsigned) cCOLS+2); } } else { if (screen != (struct t_screen *) 0) { for (i=0 ; i < cLINES ; i++) { if (screen[i].col != (char *) 0) { free ((char *) screen[i].col); screen[i].col = (char *) 0; } } free ((char *) screen); screen = (struct t_screen *) 0; } } } void free_all_arrays () { hash_reclaim (); init_screen_array (FALSE); free_art_array (); if (arts != (struct t_article *) 0) { free ((char *) arts); arts = (struct t_article *) 0; } free_active_arrays (); if (base != (long *) 0) { free ((char *) base); base = (long *) 0; } if (killf != (struct t_kill *) 0) { free_kill_array (); if (killf != (struct t_kill *) 0) { free ((char *) killf); killf = (struct t_kill *) 0; } } if (save != (struct t_save *) 0) { free_save_array (); if (save != (struct t_save *) 0) { free ((char *) save); save = (struct t_save *) 0; } } if (spooldirs != (struct t_spooldir *) 0) { free_spooldirs_array (); if (spooldirs != (struct t_spooldir *) 0) { free ((char *) spooldirs); spooldirs = (struct t_spooldir *) 0; } } if (active_size != (struct t_active_size *) 0) { free_active_size_array (); if (active_size != (struct t_active_size *) 0) { free ((char *) active_size); active_size = (struct t_active_size *) 0; } } } void free_art_array () { register int i; for (i=0 ; i < top ; i++) { arts[i].artnum = 0L; arts[i].thread = ART_EXPIRED; arts[i].inthread = FALSE; arts[i].unread = ART_UNREAD; arts[i].killed = FALSE; arts[i].tagged = FALSE; arts[i].hot = FALSE; arts[i].date = 0L; if (arts[i].part != (char *) 0) { free ((char *) arts[i].part); arts[i].part = (char *) 0; } if (arts[i].patch != (char *) 0) { free ((char *) arts[i].patch); arts[i].patch = (char *) 0; } if (arts[i].xref != NULL) { free ((char *) arts[i].xref); arts[i].xref = NULL; } } } void free_attributes_array () { register int i; for (i = 0 ; i < num_active ; i++) { if (active[i].attribute.maildir != (char *) 0 && active[i].attribute.maildir != default_maildir) { free ((char *) active[i].attribute.maildir); active[i].attribute.maildir = (char *) 0; } if (active[i].attribute.savedir != (char *) 0 && active[i].attribute.savedir != default_savedir) { free ((char *) active[i].attribute.savedir); active[i].attribute.savedir = (char *) 0; } if (active[i].attribute.organization != (char *) 0 && active[i].attribute.organization != default_organization) { free ((char *) active[i].attribute.organization); active[i].attribute.organization = (char *) 0; } if (active[i].attribute.sigfile != (char *) 0 && active[i].attribute.sigfile != default_sigfile) { free ((char *) active[i].attribute.sigfile); active[i].attribute.sigfile = (char *) 0; } if (active[i].attribute.printer != (char *) 0 && active[i].attribute.printer != default_printer) { free ((char *) active[i].attribute.printer); active[i].attribute.printer = (char *) 0; } if (active[i].attribute.followup_to != (char *) 0) { free ((char *) active[i].attribute.followup_to); active[i].attribute.followup_to = (char *) 0; } } } void free_active_arrays () { register int i; if (my_group != (int *) 0) { /* my_group[] */ free ((char *) my_group); my_group = (int *) 0; } if (active != (struct t_group *) 0) { /* active[] */ for (i=0 ; i < num_active ; i++) { if (active[i].name != (char *) 0) { free ((char *) active[i].name); active[i].name = (char *) 0; } if (active[i].description != (char *) 0) { free ((char *) active[i].description); active[i].description = (char *) 0; } if (active[i].type == GROUP_TYPE_MAIL && active[i].spooldir != (char *) 0) { free ((char *) active[i].spooldir); active[i].spooldir = (char *) 0; } } free_attributes_array (); if (active != (struct t_group *) 0) { free ((char *) active); active = (struct t_group *) 0; } } num_active = -1; } void free_kill_array () { int i; for (i=0 ; i < num_kill ; i++) { if (killf[i].kill_subj != (char *) 0) { free ((char *) killf[i].kill_subj); killf[i].kill_subj = (char *) 0; } if (killf[i].kill_from != (char *) 0) { free ((char *) killf[i].kill_from); killf[i].kill_from = (char *) 0; } } num_kill = 0; } void free_save_array () { int i; for (i=0 ; i < num_save ; i++) { if (save[i].subject != (char *) 0) { free ((char *) save[i].subject); save[i].subject = (char *) 0; } if (save[i].archive != (char *) 0) { free ((char *) save[i].archive); save[i].archive = (char *) 0; } if (save[i].dir != (char *) 0) { free ((char *) save[i].dir); save[i].dir = (char *) 0; } if (save[i].file != (char *) 0) { free ((char *) save[i].file); save[i].file = (char *) 0; } if (save[i].part != (char *) 0) { free ((char *) save[i].part); save[i].part = (char *) 0; } if (save[i].patch != (char *) 0) { free ((char *) save[i].patch); save[i].patch = (char *) 0; } save[i].index = -1; save[i].saved = FALSE; save[i].is_mailbox = FALSE; } num_save = 0; } void free_spooldirs_array () { int i; for (i=0 ; i < num_spooldir ; i++) { if (spooldirs[i].name != (char *) 0) { free ((char *) spooldirs[i].name); spooldirs[i].name = (char *) 0; } if (spooldirs[i].comment != (char *) 0) { free ((char *) spooldirs[i].comment); spooldirs[i].comment = (char *) 0; } spooldirs[i].state = 0; } num_spooldir = 0; } void free_active_size_array () { int i; for (i=0 ; i < num_active_size ; i++) { if (active_size[i].server != (char *) 0) { free ((char *) active_size[i].server); active_size[i].server = (char *) 0; } if (active_size[i].attribute != (char *) 0) { free ((char *) active_size[i].attribute); active_size[i].attribute = (char *) 0; } } num_active_size = 0; } char * my_malloc (size) unsigned size; { char *p; /* if ((p = (char *) calloc (1, (int) size)) == NULL) {*/ if ((p = (char *) malloc ((int) size)) == NULL) { error_message (txt_out_of_memory, progname); tin_done (1); } return p; } char * my_realloc (p, size) char *p; unsigned size; { if (! p) { p = (char *) calloc (1, (int) size); } else { p = (char *) realloc (p, (int) size); } if (! p) { error_message (txt_out_of_memory, progname); tin_done (1); } return p; } v*[SRC.TIN-1_22]MISC.C;13+,.?// 4?>-d0@123KPWO@56\?7 iƗ?89]VG/HJ0/* * Project : tin - a Usenet reader * Module : misc.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 22-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" static char *mailbox_name = (char *) 0; static int mailbox_size; void asfail (file, line, cond) char *file; int line; char *cond; { fprintf (stderr, "%s: assertion failure: %s (%d): %s\n", progname, file, line, cond); fflush (stderr); /* * create a core dump */ #ifdef HAVE_COREFILE #ifdef SIGABRT sigdisp(SIGABRT, SIG_DFL); kill (process_id, SIGABRT); #else # ifdef SIGILL sigdisp(SIGILL, SIG_DFL); kill (process_id, SIGILL); # else # ifdef SIGIOT sigdisp(SIGIOT, SIG_DFL); kill (process_id, SIGIOT); # endif # endif #endif #endif /* HAVE_COREFILE */ exit(1); } void copy_fp (fp_ip, fp_op, prefix) FILE *fp_ip; FILE *fp_op; char *prefix; { #ifndef VMS extern int errno; #endif char buf[8192]; int retcode; while (fgets (buf, sizeof (buf), fp_ip) != (char *) 0) { if (buf[0] != '\n') { retcode = fprintf (fp_op, "%s%s", prefix, buf); } else { retcode = fprintf (fp_op, "%s", buf); } if (retcode == EOF) { sprintf (msg, "Failed copy_fp(). errno=%d", errno); perror_message (msg, ""); return; } } } char *get_val (env, def) char *env; /* Environment variable we're looking for */ char *def; /* Default value if no environ value found */ { char *ptr; ptr = (char *) getenv(env); return (ptr != (char *) 0 ? ptr : def); } int invoke_editor (filename, lineno) char *filename; int lineno; { char buf[PATH_LEN]; char editor_format[PATH_LEN]; char *my_editor; int retcode; static char editor[PATH_LEN]; static int first = TRUE; if (first) { my_editor = (char *) getenv ("VISUAL"); strcpy (editor, my_editor != NULL ? my_editor : get_val ("EDITOR", DEFAULT_EDITOR)); first = FALSE; } if (start_editor_offset) { if (default_editor_format[0]) { strcpy (editor_format, default_editor_format); } else { strcpy (editor_format, EDITOR_FORMAT_ON); } } else { strcpy (editor_format, EDITOR_FORMAT_OFF); } retcode = strfeditor (editor, lineno, filename, buf, sizeof (buf), editor_format); if (! retcode) { sprintf (buf, "%s %s", editor, filename); } wait_message (buf); return invoke_cmd (buf); } int invoke_ispell (nam) char *nam; { #ifdef HAVE_ISPELL char buf[PATH_LEN]; char *my_ispell; static char ispell[PATH_LEN]; static int first = TRUE; if (first) { my_ispell = (char *) getenv ("ISPELL"); strcpy (ispell, my_ispell != NULL ? my_ispell : "ispell -x"); first = FALSE; } sprintf (buf, "%s %s", ispell, nam); wait_message (buf); return invoke_cmd (buf); #else error_message (txt_ispell_define_not_compiled, ""); return FALSE; #endif } #ifndef NO_SHELL_ESCAPE void shell_escape () { char shell[LEN]; char *p; #ifdef SIGTSTP t_sigtype (*susp)(); susp = (t_sigtype (*)()) 0; #endif sprintf (msg, txt_shell_escape, default_shell_command); if (! prompt_string (msg, shell)) my_strncpy (shell, get_val (ENV_VAR_SHELL, DEFAULT_SHELL), sizeof (shell)); for (p = shell; *p && (*p == ' ' || *p == '\t'); p++) continue; if (*p) { my_strncpy (default_shell_command, p, sizeof (default_shell_command)); } else { if (default_shell_command[0]) { my_strncpy (shell, default_shell_command, sizeof (shell)); } else { my_strncpy (shell, get_val (ENV_VAR_SHELL, DEFAULT_SHELL), sizeof (shell)); } p = shell; } ClearScreen (); sprintf (msg, "Shell Command (%s)", p); center_line (0, TRUE, msg); MoveCursor (INDEX_TOP, 0); set_alarm_clock_off (); EndWin (); Raw (FALSE); #ifdef SIGTSTP if (do_sigtstp) susp = signal (SIGTSTP, SIG_DFL); #endif system (p); #ifdef SIGTSTP if (do_sigtstp) signal (SIGTSTP, susp); #endif Raw (TRUE); InitWin (); set_alarm_clock_on (); mail_setup (); continue_prompt (); if (draw_arrow_mark) { ClearScreen (); } } #endif void tin_done (ret) int ret; { char group_path[PATH_LEN]; int ask = TRUE; register int i, j; /* * check if any groups were read & ask if they should marked read */ if (catchup_read_groups && ! cmd_line) { for (i = 0 ; i < group_top ; i++) { if (active[my_group[i]].attribute.read_during_session) { if (ask) { if (prompt_yn (cLINES, "Catchup all groups entered during this session? (y/n): ", 'n')) { ask = FALSE; default_thread_arts = FALSE; /* speeds up index loading */ } else { break; } } sprintf (msg, "Catchup %s...", active[my_group[i]].name); wait_message (msg); make_group_path (active[my_group[i]].name, group_path); if (index_group (active[my_group[i]].name, group_path)) { for (j = 0; j < top; j++) { arts[j].unread = ART_READ; } update_newsrc (active[my_group[i]].name, my_group[i], FALSE); } } } } write_mail_active_file (); nntp_close (); /* disconnect from NNTP server */ if (debug) { free_all_arrays (); /* deallocate all arrays */ } ClearScreen (); EndWin (); Raw (FALSE); cleanup_tmp_files (); #ifdef VMS if (ret == 0) ret = 1; #endif exit (ret); } int my_mkdir (path, mode) char *path; int mode; { #ifdef DONT_HAVE_MKDIR char buf[LEN]; struct stat sb; sprintf(buf, "mkdir %s", path); if (stat (path, &sb) == -1) { system (buf); chmod (path, mode); } #else # ifdef M_OS2 return mkdir (path); # else return mkdir (path, mode); # endif #endif } int my_chdir (path) char *path; { int retcode; retcode = chdir (path); #ifdef M_OS2 if (*path && path[1] == ':') { _chdrive (toupper(path[0]) - 'A' + 1); } #endif return retcode; } /* * hash group name for fast lookup later */ unsigned long hash_groupname (group) char *group; { #ifdef NEW_HASH_METHOD /* still testing */ unsigned long hash = 0L, g, val; /* prime == smallest prime number greater than size of string table */ int prime = 1423; char *p; for (p = group; *p; p++) { hash = (hash << 4) + *p; if (g = hash & 0xf0000000) { hash ^= g >> 24; hash ^= g; } } val = hash % prime; /* printf ("hash=[%s] [%ld]\n", group, val); */ return val; #else unsigned long hash_value = 0L; unsigned char *ptr = (unsigned char *) group; if (*ptr) { hash_value = *ptr++; while (*ptr) hash_value = ((hash_value << 1) ^ *ptr++) % TABLE_SIZE; } return (hash_value); #endif } #ifdef M_UNIX void rename_file (old_filename, new_filename) char *old_filename; char *new_filename; { extern int errno; char buf[1024]; FILE *fp_old, *fp_new; unlink (new_filename); if (link (old_filename, new_filename) == -1) { if (errno == EXDEV) { /* create & copy file across filesystem */ if ((fp_old = fopen (old_filename, "r")) == (FILE *) 0) { sprintf (buf, txt_cannot_open, old_filename); perror_message (buf, "ONE"); return; } if ((fp_new = fopen (new_filename, "w")) == (FILE *) 0) { sprintf (buf, txt_cannot_open, new_filename); perror_message (buf, "ONE"); return; } copy_fp (fp_old, fp_new, ""); fclose (fp_new); fclose (fp_old); errno = 0; } else { sprintf (buf, txt_rename_error, old_filename, new_filename); perror_message (buf, "THREE"); return; } } if (unlink (old_filename) == -1) { sprintf (buf, txt_rename_error, old_filename, new_filename); perror_message (buf, "TWO"); return; } } #else /* * AmigaOS now has links. Better not to use them as not everybody has new ROMS */ void rename_file (old_filename, new_filename) char *old_filename; char *new_filename; { char buf[1024]; unlink (new_filename); if (rename (old_filename, new_filename)==EOF) { sprintf (buf, txt_rename_error, old_filename, new_filename); perror_message (buf, "THREE"); } return; } #endif /* M_UNIX */ char *str_dup (str) char *str; { char *dup = (char *) 0; if (str) { dup = my_malloc (strlen (str)+1); strcpy (dup, str); } return dup; } int invoke_cmd (nam) char *nam; { int ret; #ifdef SIGTSTP t_sigtype (*susp)(); susp = (t_sigtype (*)()) 0; #endif set_alarm_clock_off (); EndWin (); Raw (FALSE); #ifdef SIGTSTP if (do_sigtstp) susp = signal(SIGTSTP, SIG_DFL); #endif #if defined(SIGCHLD) && !defined(RS6000) system (nam); ret = system_status; #else ret = system (nam); #endif #ifdef SIGTSTP if (do_sigtstp) signal (SIGTSTP, susp); #endif Raw (TRUE); InitWin (); set_alarm_clock_on (); #ifdef VMS return ret != 0; #else return ret == 0; #endif } void draw_percent_mark (cur_num, max_num) long cur_num; long max_num; { char buf[32]; int percent = 0; if (NOTESLINES <= 0) { return; } if (cur_num <= 0 && max_num <= 0) { return; } percent = cur_num * 100 / max_num; sprintf (buf, "%s(%d%%) [%ld/%ld]", txt_more, percent, cur_num, max_num); MoveCursor (cLINES, (cCOLS - (int) strlen (buf))-(1+BLANK_PAGE_COLS)); StartInverse (); fputs (buf, stdout); fflush (stdout); EndInverse (); } void set_real_uid_gid () { #ifdef HAVE_SET_GID_UID if (local_index) return; umask (real_umask); #ifdef HAVE_SETREUID if (setreuid (-1, real_uid) == -1) { perror_message ("Error setreuid(real) failed", ""); } if (setregid (-1, real_gid) == -1) { perror_message ("Error setregid(real) failed", ""); } #else # if defined(BSD) && ! defined(sinix) # ifdef sun if (seteuid (real_uid) == -1) { perror_message ("Error setreuid(real) failed", ""); } if (setegid (real_gid) == -1) { perror_message ("Error setregid(real) failed", ""); } # else if (setreuid (tin_uid, real_uid) == -1) { perror_message ("Error setreuid(real) failed", ""); } if (setregid (tin_gid, real_gid) == -1) { perror_message ("Error setregid(real) failed", ""); } # endif /* sun */ # else if (setuid (real_uid) == -1) { perror_message ("Error setuid(real) failed", ""); } if (setgid (real_gid) == -1) { perror_message ("Error setgid(real) failed", ""); } # endif #endif #endif /* HAVE_SET_GID_UID */ } void set_tin_uid_gid () { #ifdef HAVE_SET_GID_UID if (local_index) return; umask (0); #ifdef HAVE_SETREUID if (setreuid (-1, tin_uid) == -1) { perror_message ("Error setreuid(tin) failed", ""); } if (setregid (-1, tin_gid) == -1) { perror_message ("Error setregid(tin) failed", ""); } #else # if defined(BSD) && ! defined(sinix) # ifdef sun if (seteuid (tin_uid) == -1) { perror_message ("Error setreuid(real) failed", ""); } if (setegid (tin_gid) == -1) { perror_message ("Error setregid(real) failed", ""); } # else if (setreuid (real_uid, tin_uid) == -1) { perror_message ("Error setreuid(tin) failed", ""); } if (setregid (real_gid, tin_gid) == -1) { perror_message ("Error setregid(tin) failed", ""); } # endif /* sun */ # else if (setuid (tin_uid) == -1) { perror_message ("Error setuid(tin) failed", ""); } if (setgid (tin_gid) == -1) { perror_message ("Error setgid(tin) failed", ""); } # endif #endif #endif /* HAVE_SET_GID_UID */ } void base_name (dirname, program) char *dirname; /* argv[0] */ char *program; /* progname is returned */ { int i; strcpy (program, dirname); for (i=(int) strlen (dirname)-1 ; i ; i--) { #ifndef VMS if (dirname[i] == SEPDIR) { #else if (dirname[i] == ']') { #endif strcpy (program, dirname+(i+1)); break; } } #ifdef M_OS2 lcase (program); #endif #ifdef VMS { char *p; if (p = index(program, '.')) *p = 0; } #endif } /* * Record size of mailbox so we can detect if new mail has arrived */ void mail_setup () { struct stat buf; mailbox_name = get_val ("MAIL", mailbox); if (stat (mailbox_name, &buf) >= 0) { mailbox_size = buf.st_size; } else { mailbox_size = 0; } } /* * Return TRUE if new mail has arrived */ int mail_check () { struct stat buf; if (mailbox_name != (char *) 0 && stat (mailbox_name, &buf) >= 0 && mailbox_size < buf.st_size) { return TRUE; } return FALSE; } /* * Returns the user name and E-mail address of K>[~ TIN-1_22.BCKd[SRC.TIN-1_22]MISC.C;13?SBthe user * * Written by ahd 15 July 1989 * Borrowed from UUPC/extended with some mods by nms */ void parse_from (from_line, eaddr, fname) char* from_line; char* eaddr; char* fname; { char *nonblank = NULL; char name[LEN]; /* User full name */ char *nameptr = name; char addr[LEN]; /* User e-mail address */ char *addrptr = addr; char state = 'A'; /* State = skip whitespace */ char newstate = 'A'; /* Next state to process */ int bananas = 0; /* No () being processed now */ /* * Begin loop to copy the input field into the address and the * user name. We will begin by copying both (ignoring whitespace * for addresses) because we won't know if the input field is an * address or a name until we hit either a special character of * some sort. */ while ((*from_line != '\0') && (state != ',')) { switch (state) { case 'A': if (isspace(*from_line)) /* Found first non-blank? */ break; /* No --> keep looking */ nonblank = from_line; state = 'B'; /* ... and fall through */ case 'B': case ')': newstate = *from_line; switch (*from_line) { case '(': bananas++; break; case '"': break; case '<': addrptr = addr; /* Start address over */ nameptr = name; /* Start name over again */ from_line = nonblank - 1; /* Re-scan in new state */ newstate = '>'; /* Proc all-non <> as name */ break; /* Begin addr over again */ case ',': break; /* Terminates address */ case '>': case ')': strcpy(eaddr, "error@hell"); *fname = '\0'; return; default: newstate = state; /* stay in this state */ if (!isspace(*from_line)) *addrptr++ = *from_line; } /* switch(*from_line) */ break; case '<': if (*from_line == '>') newstate = '>'; else if (isspace(*from_line)) *nameptr++ = *from_line; else *addrptr++ = *from_line; break; case '>': if (*from_line == '<') newstate = '<'; else *nameptr++ = *from_line; break; case '(': if (*from_line == '(') ++bananas; else if (*from_line == ')') if (--bananas == 0) { newstate = ')'; break; } *nameptr++ = *from_line; break; case '"': if (*from_line == '"') newstate = ')'; else *nameptr++ = *from_line; break; default: /* Logic error, bad state */ strcpy(eaddr, "error@nowhere"); *fname = '\0'; return; } /* switch (state) */ state = newstate; from_line++; } /* while */ *addrptr = '\0'; *nameptr = '\0'; if (state == 'A') { strcpy(eaddr, "nobody@nowhere"); *fname = '\0'; return; } strcpy(eaddr, addr); /* Return the full address */ if (state == 'B') strcpy(fname, ""); else { while (--nameptr >= name) { if (isspace(*nameptr) || (*nameptr == '"')) *nameptr = '\0'; else break; } /* Strip leading blanks from the address */ nameptr = name; while ( *(nameptr) != '\0') { if (!(isspace(*nameptr) || (*nameptr == '"'))) break; else nameptr++; } strcpy(fname, nameptr); } } /* * Convert a string to a long, only look at first n characters */ long my_atol (s, n) char *s; int n; { long ret = 0; #ifdef QNX4 ret = atol (s); #else while (*s && n--) { if (*s >= '0' && *s <= '9') ret = ret * 10 + (*s - '0'); else return -1; s++; } #endif return ret; } /* * strcmp that ignores case */ #define FOLD_TO_UPPER(a) (islower ((int) (a)) ? toupper ((int) (a)) : (a)) int my_stricmp (p, q) char *p; char *q; { for (; FOLD_TO_UPPER (*p) == FOLD_TO_UPPER (*q); ++p, ++q) { if (*p == '\0') { return (0); } } return (FOLD_TO_UPPER (*p) - FOLD_TO_UPPER (*q)); } /* * Return a pointer into s eliminating any leading Re:'s. Example: * * Re: Reorganization of misc.jobs * ^ ^ */ char *eat_re (s) char *s; { while (*s == 'r' || *s == 'R') { if ((*(s+1) == 'e' || *(s+1) == 'E')) { if (*(s+2) == ':') s += 3; else if (*(s+2) == '^' && isdigit(*(s+3)) && *(s+4) == ':') s += 5; /* hurray nn */ else break; } else break; while (*s == ' ') s++; } return s; } /* * Hash the subjects (after eating the Re's off) for a quicker * thread search later. We store the hashes for subjects in the * index file for speed. */ long hash_s (s) char *s; { long h = 0; unsigned char *t = (unsigned char *) s; while (*t) h = h * 64 + *t++; return h; } /* * strncpy that stops at a newline and null terminates */ void my_strncpy (p, q, n) char *p; char *q; int n; { while (n--) { if (! *q || *q == '\n') break; *p++ = *q++; } *p = '\0'; } int untag_all_articles () { int untagged = FALSE; register int i; for (i=0 ; i < top ; i++) { if (arts[i].tagged) { arts[i].tagged = FALSE; untagged = TRUE; } } num_of_tagged_arts = 0; return (untagged); } /* * ANSI C strstr () - Uses Boyer-Moore algorithm. */ char *str_str (text, pattern, patlen) char *text; char *pattern; int patlen; { register unsigned char *p, *t; register int i, p1, j, *delta; int deltaspace[256]; int textlen; textlen = strlen (text); /* algorithm fails if pattern is empty */ if ((p1 = patlen) == 0) return (text); /* code below fails (whenever i is unsigned) if pattern too long */ if (p1 > textlen) return (NULL); /* set up deltas */ delta = deltaspace; for (i = 0; i <= 255; i++) delta[i] = p1; for (p = (unsigned char *) pattern, i = p1; --i > 0;) delta[*p++] = i; /* * From now on, we want patlen - 1. * In the loop below, p points to the end of the pattern, * t points to the end of the text to be tested against the * pattern, and i counts the amount of text remaining, not * including the part to be tested. */ p1--; p = (unsigned char *) pattern + p1; t = (unsigned char *) text + p1; i = textlen - patlen; for (;;) { if (*p == *t && memcmp ((p - p1), (t - p1), p1) == 0) return ((char *)t - p1); j = delta[*t]; if (i < j) break; i -= j; t += j; } return (NULL); } void get_author (thread, respnum, str) int thread; int respnum; char *str; { extern int threaded_on_subject; int author; if (thread) { if (threaded_on_subject) { author = SHOW_FROM_BOTH; } else { author = show_author; } } else { author = show_author; } switch (author) { case SHOW_FROM_NONE: str[0] = '\0'; break; case SHOW_FROM_ADDR: strcpy (str, arts[respnum].from); break; case SHOW_FROM_NAME: if (arts[respnum].name) { strcpy (str, arts[respnum].name); } else { strcpy (str, arts[respnum].from); } break; case SHOW_FROM_BOTH: if (arts[respnum].name) { sprintf (str, "%s (%s)", arts[respnum].name, arts[respnum].from); } else { strcpy (str, arts[respnum].from); } break; } } void toggle_inverse_video () { inverse_okay = !inverse_okay; if (inverse_okay) { #ifndef USE_INVERSE_HACK draw_arrow_mark = FALSE; #endif info_message (txt_inverse_on); } else { draw_arrow_mark = TRUE; info_message (txt_inverse_off); } } int get_arrow_key () { int ch; int ch1; ch = ReadCh (); if (ch == '[' || ch == 'O') ch = ReadCh(); switch (ch) { case 'A': case 'i': #ifdef QNX4 case 0xA1: #endif return KEYMAP_UP; case 'B': #ifdef QNX4 case 0xA9: #endif return KEYMAP_DOWN; case 'D': #ifdef QNX4 case 0xA4: #endif return KEYMAP_LEFT; case 'C': #ifdef QNX4 case 0xA6: #endif return KEYMAP_RIGHT; case 'I': /* ansi PgUp */ case 'V': /* at386 PgUp */ case 'S': /* 97801 PgUp */ case 'v': /* emacs style */ #ifdef QNX4 case 0xA2: #endif #ifdef M_AMIGA return KEYMAP_PAGE_DOWN; #else return KEYMAP_PAGE_UP; #endif case 'G': /* ansi PgDn */ case 'U': /* at386 PgDn */ case 'T': /* 97801 PgDn */ #ifdef QNX4 case 0xAA: #endif #ifdef M_AMIGA return KEYMAP_PAGE_UP; #else return KEYMAP_PAGE_DOWN; #endif case 'H': /* at386 Home */ #ifdef QNX4 case 0xA0: #endif return KEYMAP_HOME; case 'F': /* ansi End */ case 'Y': /* at386 End */ #ifdef QNX4 case 0xA8: #endif return KEYMAP_END; case '5': /* vt200 PgUp */ ch = ReadCh (); /* eat the ~ (interesting use of words :) */ return KEYMAP_PAGE_UP; case '6': /* vt200 PgUp */ ch = ReadCh (); /* eat the ~ */ return KEYMAP_PAGE_DOWN; case '1': /* vt200 PgUp */ ch = ReadCh (); /* eat the ~ */ if (ch == '5') { /* RS/6000 PgUp is 150g, PgDn is 154g */ ch1 = ReadCh(); ch = ReadCh(); if (ch1 == '0') return KEYMAP_PAGE_UP; if (ch1 == '4') return KEYMAP_PAGE_DOWN; } return KEYMAP_HOME; case '4': /* vt200 PgUp */ ch = ReadCh (); /* eat the ~ */ return KEYMAP_END; case 'M': /* xterminal button press */ xmouse = ReadCh () - ' '; /* button */ xcol = ReadCh () - '!'; /* column */ xrow = ReadCh () - '!'; /* row */ return KEYMAP_MOUSE; default: return KEYMAP_UNKNOWN; } } /* * Check for lock file to stop multiple copies of tind or tin -U running * and if it does not exist create it so this is the only copy running */ void create_index_lock_file (lock_file) char *lock_file; { char buf[64]; FILE *fp; long epoch; struct stat sb; if (stat (lock_file, &sb) == 0) { if ((fp = fopen (lock_file, "r")) != (FILE *) 0) { fgets (buf, sizeof (buf), fp); fclose (fp); #ifdef INDEX_DAEMON sprintf (msg, "%s: Already started pid=[%d] on %s", progname, atoi(buf), buf+8); #else sprintf (msg, "\n%s: Already started pid=[%d] on %s", progname, atoi(buf), buf+8); #endif error_message (msg, ""); exit (1); } } else if ((fp = fopen (lock_file, "w")) != (FILE *) 0) { time (&epoch); fprintf (fp, "%6d %s\n", process_id, ctime (&epoch)); fclose (fp); chmod (lock_file, 0600); } } /* * strfquote() - produce formatted quote string * %A Articles Email address * %D Articles Date * %F Articles Address+Name * %G Groupname of Article * %M Articles MessageId * %N Articles Name of author */ int strfquote (group, respnum, s, maxsize, format) char *group; int respnum; char *s; int maxsize; char *format; { extern char note_h_date[PATH_LEN]; extern char note_h_messageid[PATH_LEN]; char *endp = s + maxsize; char *start = s; char tbuf[PATH_LEN]; int i; if (s == (char *) 0 || format == (char *) 0 || maxsize == 0) { return 0; } if (strchr (format, '%') == (char *) 0 && strlen (format) + 1 >= maxsize) { return 0; } for (; *format && s < endp - 1; format++) { tbuf[0] = '\0'; if (*format != '\\' && *format != '%') { *s++ = *format; continue; } if (*format == '\\') { switch (*++format) { case '\0': *s++ = '\\'; goto out; case 'n': /* linefeed */ strcpy (tbuf, "\n"); break; default: tbuf[0] = '%'; tbuf[1] = *format; tbuf[2] = '\0'; break; } i = strlen(tbuf); if (i) { if (s + i < endp - 1) { strcpy (s, tbuf); s += i; } else { return 0; } } } if (*format == '%') { switch (*++format) { case '\0': *s++ = '%'; goto out; case '%': *s++ = '%'; continue; case 'A': /* Articles Email address */ strcpy (tbuf, arts[respnum].from); break; case 'D': /* Articles Date */ strcpy(tbuf, note_h_date); break; case 'F': /* Articles Address+Name */ if (arts[respnum].name) { sprintf (tbuf, "%s (%s)", arts[respnum].name, arts[respnum].from); } else { strcpy (tbuf, arts[respnum].from); } break; case 'G': /* Groupname of Article */ strcpy (tbuf, group); break; case 'M': /* Articles MessageId */ strcpy (tbuf, note_h_messageid); break; case 'N': /* Articles Name of author */ if (arts[respnum].name != (char *) 0) { strcpy (tbuf, arts[respnum].name); } else { strcpy (tbuf, arts[respnum].from); } break; default: tbuf[0] = '%'; tbuf[1] = *format; tbuf[2] = '\0'; break; } i = strlen(tbuf); if (i) { if (s + i < endp - 1) { strcpy (s, tbuf); s += i; } else { return 0; } } } } out: if (s < endp && *format == '\0') { *s = '\0'; return (s - start); } else return 0; } /* * strfeditor() - produce formatted editor string * %E Editor * %F Filename * %N Linenumber */ int strfeditor (editor, linenum, filename, s, maxsize, format) char *editor; int linenum; char *filename; char *s; int maxsize; char *format; { char *endp = s + maxsize; char *start = s; char tbuf[PATH_LEN]; int i; if (s == (char *) 0 || format == (char *) 0 || maxsize == 0) { return 0; } if (strchr (format, '%') == (char *) 0 && strlen (format) + 1 >= maxsize) { return 0; } for (; *format && s < endp - 1; format++) { tbuf[0] = '\0'; if (*format != '\\' && *format != '%') { *s++ = *format; continue; } if (*format == '\\') { switch (*++format) { case '\0': *s++ = '\\'; goto out; case 'n': /* linefeed */ strcpy (tbuf, "\n"); break; default: tbuf[0] = '%'; tbuf[1] = *format; tbuf[2] = '\0'; break; } i = strlen(tbuf); if (i) { if (s + i < endp - 1) { strcpy (s, tbuf); s += i; } else { return 0; } } } if (*format == '%') { switch (*++format) { case '\0': *s++ = '%'; goto out; case '%': *s++ = '%'; continue; case 'E': /* Editor */ strcpy (tbuf, editor); break; case 'F': /* Filename */ strcpy(tbuf, filename); break; case 'N': /* Line number */ sprintf (tbuf, "%d", linenum); break; default: tbuf[0] = '%'; tbuf[1] = *format; tbuf[2] = '\0'; break; } i = strlen(tbuf); if (i) { if (s + i < endp - 1) { strcpy (s, tbuf); s += i; } else { return 0; } } } } out: if (s < endp && *format == '\0') { *s = '\0'; return (s - start); } else return 0; } /* * strfpath - produce formatted pathname expansion. Handles following forms: * ~/News -> /usr/iain/News * ~abc/News -> /usr/abc/News * $var/News -> /env/var/News * =file -> /usr/iain/Mail/file * +file -> /usr/iain/News/group.name/file * ~/News/%G -> /usr/iain/News/group.name */ int strfpath (format, str, maxsize, homedir, maildir, savedir, group) char *format; char *str; int maxsize; char *homedir; char *maildir; char *savedir; char *group; { char *endp = str + maxsize; char *start = str; char *envptr; char *startp = format; char buf[PATH_LEN]; char tbuf[PATH_LEN]; char tmp[PATH_LEN]; int i; #ifndef M_AMIGA struct passwd *pwd; #endif if (str == (char *) 0 || format == (char *) 0 || maxsize == 0) { return 0; } if (strlen (format) + 1 >= maxsize) { return 0; } for (; *format && str < endp - 1; format++) { tbuf[0] = '\0'; /* * If just a normal part of the pathname copy it */ #ifdef VMS if (! strchr ("~=+", *format)) { #else if (! strchr ("~$=+", *format)) { #endif *str++ = *format; continue; } switch (*format) { case '~': /* Users or another users homedir */ switch (*++format) { case '/': /* users homedir */ joinpath (tbuf, homedir, ""); break; default: /* some other users homedir */ #ifndef M_AMIGA i = 0; while (*format && *format != '/') { tbuf[i++] = *format++; } tbuf[i] = '\0'; /* * OK lookup the username in/etc/passwd */ pwd = getpwnam (tbuf); if (pwd == (struct passwd *) 0) { str[0] = '\0'; return 0; } else { joinpath (tbuf, pwd->pw_dir, ""); } #else /* Amiga has no ther users */ return 0; #endif break; } i = strlen (tbuf); if (i) { if (str + i < endp - 1) { strcpy (str, tbuf); str += i; } else { str[0] = '\0'; return 0; } } break; #ifndef VMS case '$': /* Read the envvar and use its value */ i = 0; format++; while (*format && *format != '/') { tbuf[i++] = *format++; } tbuf[i] = '\0'; format--; /* * OK lookup the variable in the shells environment */ envptr = (char *) getenv (tbuf); if (envptr == (char *) 0) { str[0] = '\0'; return 0; } else { strncpy (tbuf, envptr, sizeof (tbuf)-1); } i = strlen (tbuf); if (i) { if (str + i < endp - 1) { strcpy (str, tbuf); str += i; } else { str[0] = '\0'; return 0; } } break; #endif case '=': /* * Shorthand for group maildir * Only convert if 1st c :har in format */ if (startp == format && maildir != (char *) 0) { joinpath (tbuf, maildir, ""); i = strlen (tbuf); if (i) { if (str + i < endp - 1) { strcpy (str, tbuf); str += i; } else { str[0] = '\0'; return 0; } } } else { *str++ = *format; } break; case '+': /* * Shorthand for saving to savedir/groupname/file * Only convert if 1st char in format */ if (startp == format && savedir != (char *) 0) { if (strfpath (savedir, buf, sizeof (buf), homedir, (char *) 0, (char *) 0, (char *) 0)) { #ifdef HAVE_LONG_FILENAMES my_strncpy (tmp, group, sizeof (tmp)); #else my_strncpy (tmp, group, 14); #endif /* * convert 1st letter to uppercase */ if (tmp[0] >= 'a' && tmp[0] <= 'z') { tmp[0] = tmp[0] - 32; } #ifndef VMS joinpath (tbuf, buf, tmp); strcat (tbuf, "/"); #else joindir (tbuf, buf, tmp); #endif i = strlen (tbuf); if (i) { if (str + i < endp - 1) { strcpy (str, tbuf); str += i; } else { str[0] = '\0'; return 0; } } } else { str[0] = '\0'; return 0; } } else { *str++ = *format; } break; case '%': /* Different forms of parsing cmds */ *str++ = *format; break; default: break; } } if (str < endp && *format == '\0') { *str = '\0'; /* clear_message (); printf ("!!! format=[%s] path=[%s]", startp, start); fflush (stdout); sleep (2); */ return (str - start); } else { str[0] = '\0'; return 0; } } void get_cwd (buf) char *buf; { #ifdef DONT_HAVE_GETCWD getwd (buf); #else getcwd (buf, PATH_LEN); #endif } void make_group_path (name, path) char *name; char *path; { char *ptr; #ifdef VMS sprintf(path, "[%s]", name); #else strcpy (path, name); ptr = path; while (*ptr) { if (*ptr == '.') { *ptr = '/'; } ptr++; } #endif } /* * Delete tmp index & local newsgroups file */ void cleanup_tmp_files () { extern char index_file[PATH_LEN]; if (read_news_via_nntp && xindex_supported) { unlink (index_file); } unlink (local_newsgroups_file); unlink (lock_file); } void make_post_process_cmd (cmd, dir, file) char *cmd; char *dir; char *file; { char buf[LEN]; char currentdir[PATH_LEN]; get_cwd (currentdir); chdir (dir); #ifdef M_OS2 backslash (file); #endif sprintf (buf, cmd, file); invoke_cmd (buf); chdir (currentdir); } e*[SRC.TIN-1_22]NEWSRC.C;4+,.9// 498-d0@123KPWO:56 ž7 d89]VG/HJ./* * Project : tin - a Usenet reader * Module : newsrc.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 06-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" /* * Automatically subscribe user to newsgroups specified in * /usr/lib/news/subscribe (locally) or same file but from * NNTP server (LIST AUTOSUBSCRIBE) and create .newsrc */ int auto_subscribe_groups () { char buf[LEN]; FILE *fp_newsrc; FILE *fp_subs; int len; int ret_code = FALSE; if ((fp_subs = open_subscription_fp ()) != NULL) { #ifdef VMS if ((fp_newsrc = fopen (newsrc, "w", "fop=cif")) != NULL) { #else if ((fp_newsrc = fopen (newsrc, "w")) != NULL) { #endif while (fgets (buf, sizeof (buf), fp_subs) != NULL) { if (buf[0] != '#' && buf[0] != '\n') { len = strlen (buf); if (len > 1) { buf[len-1] = '\0'; fprintf (fp_newsrc, "%s:\n", buf); } } } fclose (fp_newsrc); ret_code = TRUE; } fclose (fp_subs); } return (ret_code); } /* * make a backup of users .newsrc in case of the bogie man */ void backup_newsrc () { #ifndef INDEX_DAEMON char buf[NEWSRC_LINE]; FILE *fp_newsrc, *fp_backup; if ((fp_newsrc = fopen (newsrc, "r")) != NULL) { joinpath (buf, homedir, ".oldnewsrc"); unlink (buf); /* because rn makes a link of .newsrc -> .oldnewsrc */ #ifdef VMS if ((fp_backup = fopen (buf, "w", "fop=cif")) != NULL) { #else if ((fp_backup = fopen (buf, "w")) != NULL) { #endif while (fgets (buf, sizeof (buf), fp_newsrc) != NULL) { fputs (buf, fp_backup); } fclose (fp_backup); } fclose (fp_newsrc); } #endif /* INDEX_DAEMON */ } /* * Read $HOME/.newsrc into my_group[]. my_group[] ints point to * active[] entries. Sub_only determines whether to just read * subscribed groups or all of them. */ void read_newsrc (sub_only) int sub_only; /* TRUE=subscribed groups only, FALSE=all groups */ { char c, *p, buf[NEWSRC_LINE]; char old_groups[PATH_LEN]; FILE *fp = (FILE *) 0; FILE *fp_old = (FILE *) 0; int i; int remove_old_groups = FALSE; group_top = 0; reread_newsrc: /* * make a .newsrc if one does'nt exist & auto subscribe to set groups */ if ((fp = fopen (newsrc, "r")) == NULL) { if (auto_subscribe_groups ()) { goto reread_newsrc; } for (i = 0; i < num_active; i++) { if (group_top >= max_active) { expand_active (); } my_group[group_top] = i; active[i].my_group = 0; active[i].unread = -1; group_top++; } write_newsrc (); return; } joinpath (old_groups, homedir, ".newsrc."); sprintf (&old_groups[strlen(old_groups)], "%d", process_id); while (fgets (buf, sizeof buf, fp) != NULL) { p = buf; while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; *p++ = '\0'; if (c == '!' && sub_only) { continue; /* unsubscribed */ } i = add_group (buf, FALSE); if (i < 0) { if (! remove_old_groups) { #ifdef VMS if ((fp_old = fopen (old_groups, "w", "fop=cif")) == NULL) { #else if ((fp_old = fopen (old_groups, "w")) == NULL) { #endif perror_message (txt_cannot_open, old_groups); continue; } remove_old_groups = TRUE; } fprintf (fp_old, "%s\n", buf); continue; } if (c != '!') { /* if we're subscribed to it */ active[my_group[i]].my_group |= SUBSCRIBED; } active[my_group[i]].unread = parse_unread (p, my_group[i]); } fclose (fp); /* * rewrite newsrc to get rid of any non-existant groups */ if (remove_old_groups) { fclose (fp_old); rewrite_newsrc (); unlink (old_groups); } } /* * Write a new newsrc from my_group[] and active[] mygroup if * rewriting to get rid of groups that don't exist any longer. Used * to a create a new .newsrc if there isn't one already, or when * the newsrc is reset. */ void write_newsrc () { FILE *fp; register int i; #ifdef VMS if ((fp = fopen (newsrc, "w", "fop=cif")) == (FILE *) 0) { #else if ((fp = fopen (newsrc, "w")) == (FILE *) 0) { #endif return; } wait_message (txt_creating_newsrc); for (i=0 ; i < num_active ; i++) { fprintf (fp, "%s:\n", active[i].name); } fclose (fp); } /* * Rewrite newsrc to get rid of groups that don't exist any longer. */ void rewrite_newsrc () { char buf[NEWSRC_LINE]; char old[NEWSRC_LINE]; char old_groups[PATH_LEN]; FILE *fp, *fp_old, *fp_new; int found_old_group, len; joinpath (old_groups, homedir, ".newsrc."); sprintf (&old_groups[strlen(old_groups)], "%d", process_id); if ((fp = fopen (newsrc, "r")) == NULL) goto removed_old_groups_done; if ((fp_old = fopen (old_groups, "r")) == NULL) goto removed_old_groups_done; #ifdef VMS if ((fp_new = fopen (newnewsrc, "w", "fop=cif")) == NULL) #else if ((fp_new = fopen (newnewsrc, "w")) == NULL) #endif goto removed_old_groups_done; while (fgets (buf, sizeof buf, fp) != NULL) { /* read group from newsrc */ rewind (fp_old); found_old_group = FALSE; while (fgets (old, sizeof old, fp_old) != NULL) { /* read group from oldgroups */ len = strlen (old)-1; if ((buf[len] == ':' || buf[len] == '!') && strncmp (buf, old, len) == 0) { old[len] = '\0'; sprintf (msg, txt_deleting_from_newsrc, old); wait_message (msg); if (cmd_line) { wait_message ("\n"); } found_old_group = TRUE; } } if (! found_old_group) { fprintf (fp_new, "%s", buf); } } fclose (fp); fclose (fp_old); fclose (fp_new); rename_file (newnewsrc, newsrc); removed_old_groups_done: unlink (old_groups); } /* * check the min and max values for the newsrc data and copy the * ART_READ bits if they change */ void checknewsrc (groupnum) int groupnum; { long i; long l; char *tb; char *sb; if (active[groupnum].newsrc && (active[groupnum].newsrcmin != active[groupnum].min || active[groupnum].newsrcmax != active[groupnum].max)) { l = active[groupnum].max - active[groupnum].min + 1; if (l <= 0) { l = 1; } tb = my_malloc ((unsigned)(l + 7) / 8); NSETBLK(tb, l); for (i = active[groupnum].min ; i <= active[groupnum].max ; i++) { if (i >= active[groupnum].newsrcmin && i <= active[groupnum].newsrcmax && NTEST(active[groupnum].newsrc, i - active[groupnum].newsrcmin) == 0) { NRESET(tb, i - active[groupnum].min); } } sb = active[groupnum].newsrc; active[groupnum].newsrcmax = active[groupnum].max; active[groupnum].newsrcmin = active[groupnum].min; active[groupnum].newsrcsize = l; active[groupnum].newsrc = tb; free (sb); } } /* * Load the sequencer rang lists and mark arts[] according to the * .newsrc info for a particular group. i.e. rec.arts.comics: 1-94,97 */ void read_newsrc_line (group) char *group; { #ifndef INDEX_DAEMON FILE *fp; char buf[NEWSRC_LINE]; char *p; int i; int j; time_t curtime; static time_t lasttime = 0; time(&curtime); if (lasttime > 0 && curtime - lasttime <= 60) { /* read once every minute */ i = find_group_index (group); if (i >= 0 && active[i].newsrc) { checknewsrc(i); for (j = 0; j < top; j++) { if (arts[j].artnum >= active[i].min && arts[j].artnum <= active[i].max && NTEST(active[i].newsrc, arts[j].artnum - active[i].min) == 0 || arts[j].artnum < active[i].min) { arts[j].unread = ART_READ; } } return; } } if ((fp = fopen (newsrc, "r")) == NULL) { return; } while (fgets (buf, sizeof buf, fp) != NULL) { p = buf; while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!') { p++; } if (*p) { *p++ = '\0'; } if (strcmp (buf, group) == 0) { i = find_group_index (buf); if (i >= 0) { parse_unread (p, i); } parse_seq (p); } } lasttime = curtime; fclose (fp); #endif /* INDEX_DAEMON */ } /* * For our current group, update the sequencer information in .newsrc */ void update_newsrc (group, groupnum, mark_unread) char *group; int groupnum; /* index into active[] for this group */ int mark_unread; { char buf[NEWSRC_LINE]; char c, *p; FILE *fp; FILE *newfp; long i; if (active[groupnum].newsrc) { checknewsrc (groupnum); if (mark_unread) { NSETBLK(active[groupnum].newsrc, active[groupnum].max - active[groupnum].min); } else { for (i = 0; i < top; i++) { if (arts[i].artnum >= active[groupnum].min && arts[i].artnum <= active[groupnum].max) { if (arts[i].unread != ART_READ) { NSET(active[groupnum].newsrc, arts[i].artnum - active[groupnum].min); } else { NRESET(active[groupnum].newsrc, arts[i].artnum - active[groupnum].min); } } } } } #ifdef VMS if ((newfp = fopen (newnewsrc, "w", "fop=cif")) == (FILE *) 0) { #else if ((newfp = fopen (newnewsrc, "w")) == (FILE *) 0) { #endif goto update_done; } if ((fp = fopen (newsrc, "r")) != (FILE *) 0) { while (fgets (buf, sizeof buf, fp) != NULL) { for (p = buf; *p; p++) if (*p == '\n') { *p = '\0'; break; } p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') { *p++ = '\0'; } if (c != '!' && c != ' ') { c = ':'; } if (strcmp (buf, group) == 0) { if (mark_unread) { fprintf (newfp, "%s%c\n", buf, c); } else { fprintf (newfp, "%s%c ", buf, c); print_seq (newfp, groupnum); fprintf (newfp, "\n"); } } else { i = find_group_index (buf); if (i >= 0 && active[i].newsrc && active[i].newsrcupdate) { checknewsrc((int)i); fprintf (newfp, "%s%c ", buf, c); print_newsrc_seq (newfp, (int)i); fprintf (newfp, "\n"); active[i].newsrcupdate = FALSE; } else { fprintf (newfp, "%s%c%s\n", buf, c, p); } } } fclose (fp); } fclose (newfp); rename_file (newnewsrc, newsrc); update_done: ; } /* * Subscribe/unsubscribe to a group in .newsrc. ch should either be * '!' to unsubscribe or ':' to subscribe. num is the group's index * in active[]. */ void subscribe (group, ch, num, out_seq) char *group; int ch; int num; int out_seq; /* output sequencer info? */ { char buf[NEWSRC_LINE]; char c, *p; FILE *fp; FILE *newfp; int gotit = FALSE; if (ch == '!') { active[num].my_group &= ~SUBSCRIBED; } else { active[num].my_group |= SUBSCRIBED; } #ifdef VMS if ((newfp = fopen (newnewsrc, "w", "fop=cif")) == NULL) #else if ((newfp = fopen (newnewsrc, "w")) == NULL) #endif goto subscribe_done; if ((fp = fopen (newsrc, "r")) != NULL) { while (fgets (buf, sizeof buf, fp) != NULL) { if (strncmp ("options ", buf, 8) == 0) { fprintf (newfp, buf); } else { for (p = buf; *p; p++) { if (*p == '\n') { *p = '\0'; break; } } p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; if (strcmp (buf, group) == 0) { fprintf (newfp, "%s%c%s\n", buf, ch, p); gotit = TRUE; } else { fprintf (newfp, "%s%c%s\n", buf, c, p); } } } fclose (fp); } if (! gotit) { if (out_seq) { fprintf (newfp, "%s%c ", group, ch); print_seq (newfp, num); fprintf (newfp, "\n"); } else fprintf (newfp, "%s%c\n", group, ch); } fclose (newfp); rename_file (newnewsrc, newsrc); subscribe_done: ; } void reset_newsrc () { char buf[NEWSRC_LINE]; char c, *p; FILE *fp; FILE *newfp; long i; #ifdef VMۧ~ TIN-1_22.BCKd[SRC.TIN-1_22]NEWSRC.C;494S if ((newfp = fopen (newnewsrc, "w", "fop=cif")) == NULL) #else if ((newfp = fopen (newnewsrc, "w")) == NULL) #endif goto update_done; if ((fp = fopen (newsrc, "r")) != NULL) { while (fgets (buf, sizeof (buf), fp) != NULL) { for (p = buf; *p && *p != '\n'; p++) continue; *p = '\0'; p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; fprintf (newfp, "%s%c\n", buf, c); } fclose (fp); } fclose (newfp); rename_file (newnewsrc, newsrc); update_done: for (i = 0; i < group_top; i++) { active[my_group[i]].unread = -1; if (active[my_group[i]].newsrc != NULL) free(active[my_group[i]].newsrc); active[my_group[i]].newsrc = NULL; active[my_group[i]].newsrcupdate = FALSE; active[my_group[i]].newsrcsize = 0; active[my_group[i]].newsrcmin = active[my_group[i]].min; active[my_group[i]].newsrcmax = active[my_group[i]].max; } } void delete_group (group) char *group; { FILE *fp; FILE *newfp; char buf[8192]; char *p; char c; int gotit = FALSE; FILE *del; #ifdef VMS if ((newfp = fopen (newnewsrc, "w", "fop=cif")) == NULL) { #else if ((newfp = fopen (newnewsrc, "w")) == NULL) { #endif goto del_done; } if ((del = fopen (delgroups, "a+")) == NULL) { fclose (newfp); goto del_done; } if ((fp = fopen (newsrc, "r")) != NULL) { while (fgets (buf, sizeof (buf), fp) != NULL) { for (p = buf; *p && *p != '\n'; p++) continue; *p = '\0'; p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; if (strcmp (buf, group) == 0) { fprintf (del, "%s%c%s\n", buf, c, p); gotit = TRUE; } else fprintf (newfp, "%s%c%s\n", buf, c, p); } fclose (fp); fclose (newfp); } if (! gotit) fprintf (del, "%s!\n", group); fclose (del); rename_file (newnewsrc, newsrc); del_done: ; } int undel_group () { char buf[2][NEWSRC_LINE]; char c, *p; FILE *del; : FILE *newfp; FILE *fp; int i, j; int which = 0; long h; if ((del = fopen (delgroups, "r")) == NULL) { return FALSE; } unlink (delgroups); #ifdef VMS if ((newfp = fopen (delgroups, "w", "fop=cif")) == NULL) { #else if ((newfp = fopen (delgroups, "w")) == NULL) { #endif return FALSE; } buf[0][0] = '\0'; buf[1][0] = '\0'; while (fgets (buf[which], sizeof (buf[which]), del) != NULL) { which = !which; if (*buf[which]) fputs (buf[which], newfp); } fclose (del); fclose (newfp); which = !which; if (!*buf[which]) { return FALSE; } for (p = buf[which]; *p && *p != '\n'; p++) continue; *p = '\0'; p = buf[which]; while (*p && *p != ' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; h = hash_groupname (buf[which]); for (i = group_hash[h]; i >= 0; i = active[i].next) { if (strcmp (buf[which], active[i].name) == 0) { for (j = 0; j < group_top; j++) if (my_group[j] == i) { return j; } active[i].my_group &= ~UNSUBSCRIBED; /* mark that we got it */ if (c != '!') active[i].my_group |= SUBSCRIBED; if (group_top >= max_active) expand_active (); group_top++; for (j = group_top; j > cur_groupnum; j--) { /* FIXME delete activeunread[j] = unread[j-1]; */ my_group[j] = my_group[j-1]; } my_group[cur_groupnum] = i; active[i].unread = parse_unread (p, i); if ((fp = fopen (newsrc, "r")) == NULL) { return FALSE; } #ifdef VMS if ((newfp = fopen(newnewsrc, "w", "fop=cif")) == NULL) { #else if ((newfp = fopen(newnewsrc, "w")) == NULL) { #endif fclose(fp); return FALSE; } i = 0; while (fgets(buf[!which], sizeof (buf[!which]), fp) != NULL) { for (p = buf[!which]; *p && *p != '\n'; p++) continue; *p = '\0'; p = buf[!which]; while (*p && *p!=' ' && *p != ':' && *p != '!') p++; c = *p; if (c != '\0') *p++ = '\0'; if (c != '!') c = ':'; while (i < cur_groupnum) { if (strcmp(buf[!which], active[my_group[i]].name) == 0) { fprintf(newfp, "%s%c%s\n", buf[!which], c, p); goto foo_cont; } i++; } fprintf(newfp, "%s%c%s\n", buf[which], c, p); fprintf(newfp, "%s%c%s\n", buf[!which], c, p); break; foo_cont:; } while (fgets (buf[!which], sizeof (buf[!which]), fp) != NULL) fputs (buf[!which], newfp); fclose (newfp); fclose (fp); rename_file (newnewsrc, newsrc); return TRUE; } } return FALSE; } void mark_group_read (group, groupnum) char *group; int groupnum; /* index into active[] for this group */ { char buf[NEWSRC_LINE]; char c, *p; FILE *fp; FILE *newfp; if (active[groupnum].max < 2) { return; } #ifdef VMS if ((newfp = fopen (newnewsrc, "w", "fop=cif")) == (FILE *) 0) { #else if ((newfp = fopen (newnewsrc, "w")) == (FILE *) 0) { #endif goto mark_group_read_done; } if ((fp = fopen (newsrc, "r")) != (FILE *) 0) { while (fgets (buf, sizeof (buf), fp) != NULL) { for (p = buf; *p; p++) { if (*p == '\n') { *p = '\0'; break; } } p = buf; while (*p && *p != ' ' && *p != ':' && *p != '!') { p++; } c = *p; if (c != '\0') { *p++ = '\0'; } if (c != '!') { c = ':'; } if (strcmp (buf, group) == 0) { fprintf (newfp, "%s%c 1-%ld\n", buf, c, active[groupnum].max); if (active[groupnum].newsrc) { checknewsrc(groupnum); NSETBLK(active[groupnum].newsrc, active[groupnum].max - active[groupnum].min); } } else { fprintf (newfp, "%s%c%s\n", buf, c, p); } } fclose (fp); } fclose (newfp); rename_file (newnewsrc, newsrc); mark_group_read_done: ; } void parse_seq (s) char *s; { long low, high; int i; while (*s) { while (*s && (*s < '0' || *s > '9')) s++; if (*s && *s >= '0' && *s <= '9') { low = (long) atol (s); while (*s && *s >= '0' && *s <= '9') s++; if (*s == '-') { s++; high = (long) atol (s); while (*s && *s >= '0' && *s <= '9') s++; } else high = low; for (i = 0; i < top; i++) if (arts[i].artnum >= low && arts[i].artnum <= high) arts[i].unread = ART_READ; } } } /* * Read the first range from the .newsrc sequencer information. * If the top of the first range is higher than what the active * file claims is the bottom, use it as the new bottom instead. */ int parse_unread (s, groupnum) char *s; int groupnum; /* index for group in active[] */ { char c; int n, sum = 0; int gotone = FALSE; long low, high, truelow, truehigh; long last_high; if (active[groupnum].newsrc == (char *) 0) { active[groupnum].newsrcsize = active[groupnum].max - active[groupnum].min + 1; if (active[groupnum].newsrcsize <= 0) { active[groupnum].newsrcsize = 1; } active[groupnum].newsrc = my_malloc ((unsigned) ((active[groupnum].newsrcsize + 7) / 8)); } else if (active[groupnum].newsrcsize != (active[groupnum].max - active[groupnum].min + 1)) { active[groupnum].newsrcsize = active[groupnum].max - active[groupnum].min + 1; if (active[groupnum].newsrcsize <= 0) { active[groupnum].newsrcsize = 1; } active[groupnum].newsrc = my_realloc (active[groupnum].newsrc, (unsigned) ((active[groupnum].newsrcsize + 7) / 8)); } active[groupnum].newsrcmax = active[groupnum].max; active[groupnum].newsrcmin = active[groupnum].min; active[groupnum].newsrcupdate = FALSE; NSETBLK(active[groupnum].newsrc, active[groupnum].max - active[groupnum].min); high = low = 0; /* * Skip possible non-numeric prefix */ while ((c = *s) && (c < '0' || c > '9')) { s++; } if (c) { gotone = TRUE; low = strtol (s, &s, 10); truelow = low - active[groupnum].min; /* Bitmap index... */ if (*s == '-') { /* There is a range of articles to mark read */ s++; high = strtol (s, &s, 10); /* Make sure it intersects range of our bitmap */ if ((low <= high) && (low <= active[groupnum].max) && (high >= active[groupnum].min)) { /* Restrict the range to min..max */ if (truelow < 0) { truelow = 0; } truehigh = high; if (truehigh > active[groupnum].max) { truehigh = active[groupnum].max; } truehigh -= active[groupnum].min; /* Fill in the whole range */ NRESETRNG(active[groupnum].newsrc, truelow, truehigh); } } else { /* * Just one article to tag read */ high = low; if (truelow >= 0 && high <= active[groupnum].max) { NRESET(active[groupnum].newsrc, truelow); } } /* * Skip possible non-numeric prefix before next range */ while ((c = *s) && (c < '0' || c > '9')) { s++; } } /* Note that in the active file min will be one greater than max * when there are no articles in the spool directory. ie., it is * always true that "max - min + 1 = article count (including * expired articles)" */ if (high < active[groupnum].min - 1) { high = active[groupnum].min - 1; } /* * Now pick up any additional articles/ranges after the first */ while (c) { last_high = high; low = strtol (s, &s, 10); truelow = low - active[groupnum].min; /* Bitmap index... */ if (*s == '-') { /* There is a range of articles to mark read */ s++; high = strtol (s, &s, 10); } else { /* Just one article to tag read */ high = low; } if (low > last_high) { /* otherwise seq out of order */ sum += (low - last_high) - 1; } if (high == low) { if (truelow >= 0 && high <= active[groupnum].max) { NRESET(active[groupnum].newsrc, truelow); } } /* * A range - make sure it intersects range of our bitmap */ else if ((low <= high ) && (low <= active[groupnum].max) && (high >= active[groupnum].min)) { /* Restrict the range to min..max */ if (truelow < 0) { truelow = 0; } truehigh = high; if (truehigh > active[groupnum].max) { truehigh = active[groupnum].max; } truehigh -= active[groupnum].min; /* * Fill in the whole range */ NRESETRNG(active[groupnum].newsrc, truelow, truehigh); } /* * Skip possible non-numeric prefix before next range */ while ((c = *s) && (c < '0' || c > '9' )) { s++; } } if (gotone) { if (active[groupnum].max > high) { sum += active[groupnum].max - high; } return sum; } n = (int) (active[groupnum].max - active[groupnum].min) + 1; return (n < 0 ? -1 : n); } int get_line_unread (group, groupnum) char *group; int groupnum; /* index for group in active[] */ { char *p, buf[NEWSRC_LINE]; FILE *fp; int ret = -1; if ((fp = fopen(newsrc, "r")) == NULL) return -1; while (fgets(buf, sizeof (buf), fp) != NULL) { p = buf; while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!') p++; *p++ = '\0'; if (strcmp (buf, group) != 0) continue; ret = parse_unread (p, groupnum); break; } fclose (fp); return ret; } void print_newsrc_seq (fp, groupnum) FILE *fp; int groupnum; /* index into active[] for this group */ { int flag = FALSE; long artnum; long i, last; for (i = active[groupnum].min ; i <= active[groupnum].max ; i++) { if (NTEST(active[groupnum].newsrc, i - active[groupnum].min) == 0) { if (flag) { artnum = i; fprintf (fp, ",%ld", i); } else { artnum = 1; flag = TRUE; fprintf (fp, "1"); } i++; while (i <= active[groupnum].max && NTEST(active[groupnum].newsrc,i - active[groupnum].min) == 0) { i++; } last = i - 1; if (artnum != last) { fprintf (fp, "-%ld", last); } } else if (flag == FALSE && active[groupnum].max > 0) { flag = TRUE; fprintf (fp, "1"); if (active[groupnum].min > 2) { fprintf (fp, "-%ld", active[groupnum].min-1); } } } if (flag == FALSE && active[groupnum].max > 0) { fprintf (fp, "1"); if (active[groupnum].min > 2) { fprintf (fp, "-%ld", active[groupnum].min-1); } } fflush (fp); } void print_seq (fp, groupnum) FILE *fp; int groupnum; /* index into active[] for this group */ { long int artnum, last_read, artmax; int i, flag = FALSE; assert(top >= 0); /* * sort into the same order as in the spool area for writing * read article numbers to ~/.newsrc */ if (top > 0) qsort ((char *) arts, top, sizeof (struct t_article), artnum_comp); /* * Note that killed and expired articles do not appear in arts[]. * So, even if top is 0 there may be sequencer info to output. */ if (top > 0 && arts[top-1].artnum > active[groupnum].max) { artmax = arts[top-1].artnum; } else { artmax = active[groupnum].max; } for (artnum=1, i=0; artnum <= artmax; ++artnum, ++i) { assert(i<=top); if (i==top || arts[i].unread == ART_READ || artnum != arts[i].artnum) { if (flag) fprintf(fp, ","); else flag = TRUE; fprintf (fp, "%ld", artnum); while (i < top && arts[i].unread == ART_READ) ++i; last_read = (i # include # include # include # ifdef WIN_TCP # include # else # include # endif # define IPPORT_NNTP ((unsigned short) 119) # include /* All TLI implementations may not have this */ # else # ifdef apollo # include # include # include # else # ifdef MULTINET # include "MULTINET_ROOT:[multinet.include.sys]socket.h" # include "MULTINET_ROOT:[multinet.include.netinet]in.h" # else # include # include # endif # ifdef HAVE_NETLIB_H # include # endif # ifndef EXCELAN # include # endif # endif # endif /* !TLI */ # ifdef EXCELAN # if __STDC__ int connect (int, struct sockaddr *); unsigned short htons (unsigned short); unsigned long rhost (char **); int rresvport (int); int socket (int, struct sockproto *, struct sockaddr_in *, int); # endif # endif # ifdef DECNET # include # include # endif #endif /* NNTP_ABLE */ /* * getserverbyfile Get the name of a server from a named file. * Handle white space and comments. * Use NNTPSERVER environment variable if set. * * Parameters: "file" is the name of the file to read. * * Returns: Pointer to static data area containing the * first non-ws/comment line in the file. * NULL on error (or lack of entry in file). * * Side effects: None. */ char *getserverbyfile (file) char *file; { #ifdef NNTP_ABLE register FILE *fp; register char *cp; static char buf[256]; cp = (char *) getenv ("NNTPSERVER"); if (cp != (char *) 0) { (void) strcpy (buf, cp); return (buf); } cp = GetConfigValue (_CONF_SERVER); if (cp != (char *) 0) { (void) strcpy (buf, cp); return (buf); } if (file == (char *) 0) { return (char *) 0; } if ((fp = fopen (file, "r")) == (FILE *) 0) { return (char *) 0; } while (fgets (buf, sizeof (buf), fp) != (char *) 0) { if (*buf == '\n' || *buf == '#') { continue; } cp = (char *) strrchr (buf, '\n'); if (cp) { *cp = '\0'; } (void) fclose (fp); return (buf); } (void) fclose (fp); #endif /* NNTP_ABLE */ return (char *) 0; /* No entry */ } /* * server_init Get a connection to the remote server. * * Parameters: "machine" is the machine to connect to. * "service" is the service to connect to on the machine. * "port" is the servive port to connect to. * * Returns: -1 on error * server's initial response code on success. * * Side effects: Connects to server. * "nntp_rd_fp" and "nntp_wr_fp" are fp's * for reading and writing to server. */ int server_init (machine, service, port) char *machine; char *service; unsigned short port; { #ifdef NNTP_ABLE #ifdef DECNET char *cp; cp = (char *) strchr(machine, ':'); if (cp && cp[1] == ':') { *cp = '\0'; sockt_rd = get_dnet_socket (machine, service); } else { sockt_rd = get_tcp_socket (machine, service, port); } #else sockt_rd = get_tcp_socket (machine, service, port); #endif if (sockt_rd < 0) return (-1); /* * Now we'll make file pointers (i.e., buffered I/O) out of * the socket file descriptor. Note that we can't just * open a fp for reading and writing -- we have to open * up two separate fp's, one for reading, one for writing. */ #ifndef MULTINET if ((nntp_rd_fp = (FILE *) fdopen (sockt_rd, "r")) == NULL) { perror ("server_init: fdopen #1"); return (-1); } sockt_wr = dup (sockt_rd); #else sockt_wr = sockt_rd; #endif #ifdef TLI if (t_sync (sockt_rd) < 0) { /* Sync up new fd with TLI */ t_error ("server_init: t_sync"); nntp_rd_fp = NULL; /* from above */ return (-1); } #endif #ifndef MULTINET if ((nntp_wr_fp = (FILE *) fdopen (sockt_wr, "w")) == NULL) { perror ("server_init: fdopen #2"); nntp_rd_fp = NULL; /* from above */ return (-1); } #endif /* * Now get the server's signon message */ (void) get_server (nntp_line, sizeof (nntp_line)); return (atoi (nntp_line)); #else return (-1); #endif /* NNTP_ABLE */ } /* * get_tcp_socket -- get us a socket connected to the specified server. * * Parameters: "machine" is the machine the server is running on. * "service" is the service to connect to on the server. * "port" is the port to connect to on the server. * * Returns: Socket connected to the server if * all is ok, else -1 on error. * * Side effects: Connects to server. * * Errors: Printed via perror. */ int get_tcp_socket (machine, service, port) char *machine; /* remote host */ char *service; /* nttp/smtp etc. */ unsigned short port; /* tcp port number */ { #ifdef NNTP_ABLE int s = -1; struct sockaddr_in sin; #ifdef __hpux int socksize = 0; int socksizelen = sizeof (socksize); #endif #ifdef TLI struct hostent *gethostbyname (), *hp; struct t_call *callptr; /* * Create a TCP transport endpoint. */ if ((s = t_open ("/dev/tcp", O_RDWR, (struct t_info*) 0)) < 0){ t_error ("t_open: can't t_open /dev/tcp"); return (-1); } if (t_bind (s, (struct t_bind *) 0, (struct t_bind *) 0) < 0) { t_error ("t_bind"); t_close (s); return (-1); } bzero((char *) &sin, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_port = htons (port); if (!isdigit(*machine) || (long)(sin.sin_addr.s_addr = inet_addr (machine)) == -1) { if((hp = gethostbyname (machine)) == NULL) { fprintf (stderr, "gethostbyname: %s: host unknown\n", machine); t_close (s); return (-1); } bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length); } /* * Allocate a t_call structure and initialize it. * Let t_alloc() initialize the addr structure of the t_call structure. */ if ((callptr = (struct t_call *) t_alloc (s,T_CALL,T_ADDR)) == NULL){ t_error ("t_alloc"); t_close (s); return (-1); } callptr->addr.maxlen = sizeof (sin); callptr->addr.len = sizeof (sin); callptr->addr.buf = (char *) &sin; callptr->opt.len = 0; /* no options */ callptr->udata.len = 0; /* no user data with connect */ /* * Connect to the server. */ if (t_connect (s, callptr, (struct t_call *) 0) < 0) { t_error ("t_connect"); t_close (s); return (-1); } /* * Now replace the timod module with the tirdwr module so that * standard read() and write() system calls can be used on the * descriptor. */ if (ioctl (s, I_POP, (char *) 0) < 0) { perror ("I_POP(timod)"); t_close (s); return (-1); } if (ioctl (s, I_PUSH, "tirdwr") < 0) { perror ("I_PUSH(tirdwr)"); t_close (s); return (-1); } #else /* !TLI */ #ifndef EXCELAN struct servent *getservbyname(), *sp; struct hostent *gethostbyname(), *hp; #ifdef h_addr int x = 0; register char **cp; static char *alist[1]; #endif /* h_addr */ unsigned long inet_addr(); static struct hostent def; static struct in_addr defaddr; static char namebuf[256]; if ((sp = (struct servent *) getservbyname (service, "tcp")) == NULL) { fprintf (stderr, "%s/tcp: Unknown service.\n", service); return (-1); } /* If not a raw ip address, try nameserver */ if (!isdigit(*machine) || (long)(defaddr.s_addr = (long) inet_addr (machine)) == -1) { hp = gethostbyname (machine); } else { /* Raw ip address, fake */ (void) strcpy (namebuf, machine); def.h_name = (char *) namebuf; #ifdef h_addr def.h_addr_list = alist; #endif def.h_addr = (char *) &defaddr; def.h_length = sizeof (struct in_addr); def.h_addrtype = AF_INET; def.h_aliases = 0; hp = &def; } if (hp == NULL) { fprintf (stderr, "\n%s: Unknown host.\n", machine); return (-1); } bzero((char *) &sin, sizeof (sin)); sin.sin_family = hp->h_addrtype; sin.sin_port = sp->s_port; #else /* EXCELAN */ bzero((char *) &sin, sizeof (sin)); sin.sin_family = AF_INET; #endif /* EXCELAN */ /* * The following is kinda gross. The name server under 4.3 * returns a list of addresses, each of which should be tried * in turn if the previous one fails. However, 4.2 hostent * structure doesn't have this list of addresses. * Under 4.3, h_addr is a #define to h_addr_list[0]. * We use this to figure out whether to include the NS specific * code... */ #ifdef h_addr /* * get a socket and initiate connection -- use multiple addresses */ for (cp = hp->h_addr_list; cp && *cp; cp++) { s = socket (hp->h_addrtype, SOCK_STREAM, 0); if (s < 0) { perror ("socket"); return (-1); } bcopy(*cp, (char *) &sin.sin_addr, hp->h_length); if (x < 0) { fprintf (stderr, "Trying %s", (char *) inet_ntoa (sin.sin_addr)); } #if defined(__hpux) && defined(SVR4) /* recommended by raj@cup.hp.com */ #define HPSOCKSIZE 0x8000 getsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, (caddr_t)&socksizelen); if (socksize < HPSOCKSIZE) { socksize = HPSOCKSIZE; setsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, sizeof(socksize)); } socksize = 0; socksizelen = sizeof(socksize); getsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, (caddr_t)&socksizelen); if (socksize < HPSOCKSIZE) { socksize = HPSOCKSIZE; setsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, sizeof(socksize)); } #endif x = connect (s, (struct sockaddr *) &sin, sizeof (sin)); if (x == 0) { break; } fprintf (stderr, "\nConnection to %s: ", (char *) inet_ntoa (sin.sin_addr)); perror (""); (void) close (s); } if (x < 0) { fprintf (stderr, "Giving up...\n"); return (-1); } #else /* no name server */ #ifdef EXCELAN if ((s = socket (SOCK_STREAM,(struct sockproto *)NULL,&sin,SO_KEEPALIVE)) < 0) { /* Get the socket */ perror ("socket"); return (-1); } bzero((char *) &sin, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_port = htons (IPPORT_NNTP); /* set up addr for the connect */ if ((sin.sin_addr.s_addr = rhost (&machine)) == -1) { fprintf (stderr, "\n%s: Unknown host.\n", machine); return (-1); } /* And then connect */ if (connect (s, (struct sockaddr *)&sin) < 0) { perror ("connect"); (void) close (s); return (-1); } #else /* not EXCELAN */ if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) { perror ("socket"); return (-1); } /* And then connect */ bcopy (hp->h_addr, (char *) &sin.sin_addr, hp->h_length); if (connect (s, (struct sockaddr *) &sin, sizeof (sin)) < 0) { perror ("connect"); (void) close (s); return (-1); } #endif /* !EXCELAN */ #endif /* !h_addr */ #endif /* !TLI */ return (s); #else return (-1); #endif /* NNTP_ABLE */ } #ifdef DECNET /* * get_dnet_socket -- get us a socket connected to the server. * * Parameters: "machine" is the machine the server is running on. * "service" is the name of the service to connect to. * * Returns: Socket connected to the news server if * all is ok, else -1 on error. * * Side effects: Connects to server. * * Errors: Printed via nerror. */ int get_dnet_socket (machine, service) char *machine; char *service; { #ifdef NNTP_ABLE int s, area, node; struct sockaddr_dn sdn; struct nodeent *getnodebyname(), *np; bzero((char *) &sdn, sizeof (sdn)); switch (s = sscanf (machine, "%d%*[.]%d", &area, &node)) { case 1: node = area; area = 0; case 2: node += area*1024; sdn.sdn_add.a_len = 2; sdn.sdn_family = AF_DECnet; sdn.sdn_add.a_addr[0] = node % 256; sdn.sdn_add.a_addr[1] = node / 256; break; default: if ((np = getnodebyname (machine)) == NULL) { fprintf (stderr, "%s: Unknown host.\n", machine); return (-1); } else { bcopy(np->n_addr, (char *) sdn.sdn_add.a_addr, np->n_length); sdn.sdn_add.a_len = np->n_length; sdn.sdn_family = np->n_addrtype; } break; } sdn.sdn_objnum = 0; sdn.sdn_flags = 0; sdn.sdn_objnamel = strlen ("NNTP"); bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel); if ((s = socket (AF_DECnet, SOCK_STREAM, 0)) < 0) { nerror ("socket"); return (-1); } /* And then connect */ if (connect (s, (struct sockaddr *) &sdn, sizeof (sdn)) < 0) { nerror ("connect"); close (s); return (-1); } return (s); #else return (-1); #endif /* NNTP_ABLE */ } #endif /* * handle_server_response * * Print some informative messages based on the server's initial * response code. This is here so inews, rn, etc. can share * the code. * * Parameters: "response" is the response code which the * server sent us, presumably from "server_init", * above. * "nntpserver" is the news server we got the * response code from. * * Returns: -1 if the error is fatal (and we should exit). * 0 otherwise. * * Side effects: None. */ int handle_server_response (response, nntpserver) int response; char *nntpserver; { #ifdef NNTP_ABLE switch (response) { case OK_NOPOST: /* fall through */ printf ("NOTE: This machine does not have permission to post articles.\n"); printf (" Please don't waste your time trying.\n\n"); case OK_CANPOST: return (0); break; case ERR_ACCESS: printf ("This machine does not have permission to use the %s news server.\n", nntpserver); return (-1); break; default: printf ("Unexpected response code from %s news server: %d\n", nntpserver, response); return (-1);  m~ TIN-1_22.BCK' d[SRC.TIN-1_22]NNTPLIB.C;23-g?" break; } /*NOTREACHED*/ #else return (-1); #endif /* NNTP_ABLE */ } /* * put_server -- send a line of text to the server, terminating it * with CR and LF, as per ARPA standard. * * Parameters: "string" is the string to be sent to the * server. * * Returns: Nothing. * * Side effects: Talks to the server. * Closes connection if things are not right. * * Note: This routine flushes the buffer each time * it is called. For large transmissions * (i.e., posting news) don't use it. Instead, * do the fprintf's yourself, and then a final * fflush. */ void put_server (string) char *string; { #ifdef NNTP_ABLE int respno; static time_t time_last; time_t time_now; /* * Check how idle we have been, if too idle send a STAT to check */ time (&time_now); if (time_last != 0 && time_last+NNTP_IDLE_RETRY_SECS < time_now) { #ifndef MULTINET fprintf (nntp_wr_fp, "stat\r\n"); fflush (nntp_wr_fp); #else socket_write(sockt_wr, "stat\r\n", 6); #endif respno = get_respcode (); if (respno != OK_NOTEXT && respno != ERR_NCING && respno != ERR_NOCRNT) { /* * STAT was not happy, close the connection * it will reopen on next get_server */ #ifndef MULTINET fclose (nntp_wr_fp); fclose (nntp_rd_fp); #else socket_close(sockt_rd); #endif strcpy (last_put, string); time_last = 0; return; } } time_last = time_now; #ifndef MULTINET fprintf (nntp_wr_fp, "%s\r\n", string); (void) fflush (nntp_wr_fp); #else { char line[256]; sprintf(line, "%s\r\n", string); socket_write(sockt_wr, line, strlen(line)); } #endif strcpy (last_put, string); #endif /* NNTP_ABLE */ } /* * get_server -- get a line of text from the server. Strips * CR's and LF's. * * Parameters: "string" has the buffer space for the * line received. * "size" is the size of the buffer. * * Returns: -1 on error, 0 otherwise. * * Side effects: Talks to server, changes contents of "string". * Reopens connection when necessary and requested. */ int get_server (string, size) char *string; int size; { #ifdef NNTP_ABLE char buf[NNTP_STRLEN]; extern char *glob_group; register char *cp; #ifndef MULTINET while (fgets (string, size, nntp_rd_fp) == NULL) { #else #ifdef USE_SFGETS while (Sfgets (string, size, sockt_rd) == NULL) { #else char *p; while ((p = Srdline(sockt_rd)) == NULL) { #endif #endif if (errno != EINTR) { #ifndef MULTINET fclose (nntp_wr_fp); fclose (nntp_rd_fp); #else socket_close(sockt_rd); #endif ring_bell (); if (! prompt_yn (cLINES, txt_reconnect_to_news_server, 'y')) { return -1; } clear_message (); strcpy (buf, last_put); if (nntp_open () == 0) { if (glob_group != (char *) 0) { sprintf (last_put, "group %s", glob_group); put_server (last_put); #ifndef MULTINET fgets (last_put, NNTP_STRLEN, nntp_rd_fp); #else #ifdef USE_SFGETS Sfgets (last_put, NNTP_STRLEN, sockt_rd); #else p = Srdline(sockt_rd); strncpy(last_put, p, NNTP_STRLEN); #endif #endif } } put_server (buf); } } #ifdef MULTINET #ifndef USE_SFGETS memcpy(string, p, SIOLINELEN(sockt_rd) + 1); #endif #endif if ((cp = (char *) strchr (string, '\r')) != NULL) { *cp = '\0'; } else if ((cp = (char *) strchr (string, '\n')) != NULL) { *cp = '\0'; } return (0); #else return (-1); #endif /* NNTP_ABLE */ } /* * close_server -- close the connection to the server, after sending * the "quit" command. * * Parameters: None. * * Returns: Nothing. * * Side effects: Closes the connection with the server. * You can't use "put_server" or "get_server" * after this routine is called. */ void close_server () { #ifdef NNTP_ABLE #ifndef MULTINET if (nntp_wr_fp == NULL || nntp_rd_fp == NULL) return; #else if (sockt_rd == -1) return; #endif put_server ("QUIT"); (void) get_server (nntp_line, sizeof (nntp_line)); #ifndef MULTINET (void) fclose (nntp_wr_fp); (void) fclose (nntp_rd_fp); #else Sdone(sockt_rd); socket_close(sockt_rd); #endif #endif /* NNTP_ABLE */ } #endif /* CDROM_ABLE */ /* * NNTP strings for get_respcode() */ char *nntp_respcode (respcode) int respcode; { #ifdef NNTP_ABLE static char *text; /* * If the last response line matches and has a description, return it */ if (atoi (nntp_line) == respcode && strlen (nntp_line) > 4) { return nntp_line; } switch (respcode) { case 0: text = ""; break; case INF_HELP: text = "100 Help text on way"; break; case INF_AUTH: text = "180 Authorization capabilities"; break; case INF_DEBUG: text = "199 Debug output"; break; case OK_CANPOST: text = "200 Hello; you can post"; break; case OK_NOPOST: text = "201 Hello; you can't post"; break; case OK_SLAVE: text = "202 Slave status noted"; break; case OK_GOODBYE: text = "205 Closing connection"; break; case OK_GROUP: text = "211 Group selected"; break; case OK_GROUPS: text = "215 Newsgroups follow"; break; case OK_XMOTD: text = "217 News motd file follows"; break; case OK_XINDEX: text = "218 Group index file follows"; break; case OK_ARTICLE: text = "220 Article (head & body) follows"; break; case OK_HEAD: text = "221 Head follows"; break; case OK_BODY: text = "222 Body follows"; break; case OK_NOTEXT: text = "223 No text sent -- stat, next, last"; break; case OK_NEWNEWS: text = "230 New articles by message-id follow"; break; case OK_NEWGROUPS: text = "231 New newsgroups follow"; break; case OK_XFERED: text = "235 Article transferred successfully"; break; case OK_POSTED: text = "240 Article posted successfully"; break; case OK_AUTHSYS: text = "280 Authorization system ok"; break; case OK_AUTH: text = "281 Authorization (user/pass) ok"; break; case OK_BIN: text = "282 binary data follows"; break; case OK_SPLIST: text = "283 spooldir list follows"; break; case OK_SPSWITCH: text = "284 Switching to a different spooldir"; break; case OK_SPNOCHANGE: text = "285 Still using same spooldir"; break; case OK_SPLDIRCUR: text = "286 Current spooldir"; break; case OK_SPLDIRAVL: text = "287 Available spooldir"; break; case OK_SPLDIRERR: text = "288 Unavailable spooldir or invalid entry"; break; case CONT_XFER: text = "335 Continue to send article"; break; case CONT_POST: text = "340 Continue to post article"; break; case NEED_AUTHINFO: text = "380 authorization is required"; break; case NEED_AUTHDATA: text = "381 authorization data required"; break; case ERR_GOODBYE: text = "400 Have to hang up for some reason"; break; case ERR_NOGROUP: text = "411 No such newsgroup"; break; case ERR_NCING: text = "412 Not currently in newsgroup"; break; case ERR_XMOTD: text = "417 No news motd file"; break; case ERR_XINDEX: text = "418 No index file for this group"; break; case ERR_NOCRNT: text = "420 No current article selected"; break; case ERR_NONEXT: text = "421 No next article in this group"; break; case ERR_NOPREV: text = "422 No previous article in this group"; break; case ERR_NOARTIG: text = "423 No such article in this group"; break; case ERR_NOART: text = "430 No such article at all"; break; case ERR_GOTIT: text = "435 Already got that article, don't send"; break; case ERR_XFERFAIL: text = "436 Transfer failed"; break; case ERR_XFERRJCT: text = "437 Article rejected, don't resend"; break; case ERR_NOPOST: text = "440 Posting not allowed"; break; case ERR_POSTFAIL: text = "441 Posting failed"; break; case ERR_NOAUTH: text = "480 authorization required for command"; break; case ERR_AUTHSYS: text = "481 Authorization system invalid"; break; case ERR_AUTHREJ: text = "482 Authorization data rejected"; break; case ERR_INVALIAS: text = "483 Invalid alias on spooldir cmd"; break; case ERR_INVNOSPDIR: text = "484 No spooldir file found"; break; case ERR_COMMAND: text = "500 Command not recognized"; break; case ERR_CMDSYN: text = "501 Command syntax error"; break; case ERR_ACCESS: text = "502 Access to server denied"; break; case ERR_FAULT: text = "503 Program fault, command not performed"; break; case ERR_AUTHBAD: text = "580 Authorization Failed"; break; default: text = "Unknown NNTP response code"; break; } return (text); #else return (""); #endif } *[SRC.TIN-1_22]NNTPLIB.H;1+,. // 4 -d0@123KPWO 56Vt7tf189]VG/HJ/* * Project : tin - a Usenet reader * Module : nntplib.h * Author : I.Lea * Created : 01-04-91 * Updated : 03-08-93 * Notes : nntp.h 1.5.11/1.6 with extensions for tin & CD-ROM * Copyright : You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #ifndef NNTP_SERVER_FILE # define NNTP_SERVER_FILE "/etc/nntpserver" #endif #define NNTP_TCP_NAME "nntp" #define NNTP_TCP_PORT ((unsigned short) 119) #ifndef SMTP_SERVER_FILE # define SMTP_SERVER_FILE "/etc/smtpserver" #endif #define SMTP_TCP_NAME "smtp" #define SMTP_TCP_PORT ((unsigned short) 25) /* * External routine declarations */ #ifndef M_AMIGA extern char *getserverbyfile(); extern int get_tcp_socket(); extern int handle_server_response(); extern void put_server(); extern int get_server(); extern void close_server(); #endif /* M_AMIGA */ /* * External file descriptors for the server connection */ extern FILE *nntp_wr_fp; /* * Response codes for NNTP server * * @(#)Header: nntp.h,v 1.81 92/03/12 02:08:31 sob Exp $ * * First digit: * * 1xx Informative message * 2xx Command ok * 3xx Command ok so far, continue * 4xx Command was correct, but couldn't be performed * for some specified reason. * 5xx Command unimplemented, incorrect, or a * program error has occured. * * Second digit: * * x0x Connection, setup, miscellaneous * x1x Newsgroup selection * x2x Article selection * x3x Distribution * x4x Posting */ #define CHAR_INF '1' #define CHAR_OK '2' #define CHAR_CONT '3' #define CHAR_ERR '4' #define CHAR_FATAL '5' #define INF_HELP 100 /* Help text on way */ #define INF_AUTH 180 /* Authorization capabilities */ #define INF_DEBUG 199 /* Debug output */ #define OK_CANPOST 200 /* Hello; you can post */ #define OK_NOPOST 201 /* Hello; you can't post */ #define OK_SLAVE 202 /* Slave status noted */ #define OK_GOODBYE 205 /* Closing connection */ #define OK_GROUP 211 /* Group selected */ #define OK_GROUPS 215 /* Newsgroups follow */ #define OK_XMOTD 217 /* News motd follows */ #define OK_XINDEX 218 /* Tin style index follows */ #define OK_ARTICLE 220 /* Article (head & body) follows */ #define OK_HEAD 221 /* Head follows */ #define OK_BODY 222 /* Body follows */ #define OK_NOTEXT 223 /* No text sent -- stat, next, last */ #define OK_XOVER 224 /* .overview data follows */ #define OK_NEWNEWS 230 /* New articles by message-id follow */ #define OK_NEWGROUPS 231 /* New newsgroups follow */ #define OK_XFERED 235 /* Article transferred successfully */ #define OK_POSTED 240 /* Article posted successfully */ #define OK_AUTHSYS 280 /* Authorization system ok */ #define OK_AUTH 281 /* Authorization (user/pass) ok */ #define OK_BIN 282 /* binary data follows */ #define OK_SPLIST 283 /* spooldir list follows */ #define OK_SPSWITCH 284 /* Switching to a different spooldir */ #define OK_SPNOCHANGE 285 /* Still using same spooldir */ #define OK_SPLDIRCUR 286 /* Current spooldir */ #define OK_SPLDIRAVL 287 /* Available spooldir */ #define OK_SPLDIRERR 288 /* Unavailable spooldir or invalid entry */ #define CONT_XFER 335 /* Continue to send article */ #define CONT_POST 340 /* Continue to post article */ #define NEED_AUTHINFO 380 /* authorization is required */ #define NEED_AUTHDATA 381 /* authorization data required */ #define ERR_GOODBYE 400 /* Have to hang up for some reason */ #define ERR_NOGROUP 411 /* No such newsgroup */ #define ERR_NCING 412 /* Not currently in newsgroup */ #define ERR_XMOTD 417 /* No news motd file */ #define ERR_XINDEX 418 /* No tin index for this group */ #define ERR_XOVERVIEW 419 /* No .overview index for this group */ #define ERR_NOCRNT 420 /* No current article selected */ #define ERR_NONEXT 421 /* No next article in this group */ #define ERR_NOPREV 422 /* No previous article in this group */ #define ERR_NOARTIG 423 /* No such article in this group */ #define ERR_NOART 430 /* No such article at all */ #define ERR_GOTIT 435 /* Already got that article, don't send */ #define ERR_XFERFAIL 436 /* Transfer failed */ #define ERR_XFERRJCT 437 /* Article rejected, don't resend */ #define ERR_NOPOST 440 /* Posting not allowed */ #define ERR_POSTFAIL 441 /* Posting failed */ #define ERR_NOAUTH 480 /* authorization required for command */ #define ERR_AUTHSYS 481 /* Authorization system invalid */ #define ERR_AUTHREJ 482 /* Authorization data rejected */ #define ERR_INVALIAS 483 /* Invalid alias on spooldir cmd */ #define ERR_INVNOSPDIR 484 /* No spooldir file found */ #define ERR_COMMAND 500 /* Command not recognized */ #define ERR_CMDSYN 501 /* Command syntax error */ #define ERR_ACCESS 502 /* Access to server denied */ #define ERR_FAULT 503 /* Program fault, command not performed */ #define ERR_AUTHBAD 580 /* Authorization Failed */ /* * RFC 977 defines this; don't change it. */ #define NNTP_STRLEN 512 *[SRC.TIN-1_22]OBJS.OPT;10+,.// 4=-d0123KPWO56`S7 gS89]VG/HJ<active.obj,amiga.obj,art.obj,curses.obj,debug.obj,envarg.obj=feed.obj,getline.obj,group.obj,hashstr.obj,help.obj,inews.obj7init.obj,kill.obj,lang.obj,mail.obj,main.obj,memory.obj:misc.obj,newsrc.obj,nntplib.obj,open.obj,os_2.obj,page.obj4parsdate.obj,post.obj,prompt.obj,rcfile.obj,save.obj7screen.obj,search.obj,select.obj,sigfile.obj,signal.obj:spooldir.obj,strftime.obj,thread.obj,wildmat.obj,win32.objxref.objvms.obj[.vms]libvms/lib*[SRC.TIN-1_22]OPEN.C;2+,V.*// 4*(L-d0@123KPWO+56@ ч7@yч89]VG/HJ /* * Project : tin - a Usenet reader * Module : open.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 14-09-93 * Notes : Routines to make reading news locally (ie. /usr/spool/news) * or via NNTP transparent * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" int nntp_codeno = 0; long head_next; #ifdef NNTP_ABLE int compiled_with_nntp = TRUE; /* used in mail_bug_report() info */ #else int compiled_with_nntp = FALSE; #endif #ifdef NO_POSTING int can_post = FALSE; #else int can_post = TRUE; #endif char *nntp_server; int nntp_open () { #ifdef NNTP_ABLE int ret; if (read_news_via_nntp) { debug_nntp ("nntp_open", "BEGIN"); nntp_server = getserverbyfile (NNTP_SERVER_FILE); if (nntp_server == (char *) 0) { error_message (txt_cannot_get_nntp_server_name, ""); error_message (txt_server_name_in_file_env_var, NNTP_SERVER_FILE); return -1; } if (update == FALSE) { sprintf (msg, txt_connecting, nntp_server); wait_message (msg); } debug_nntp ("nntp_open", nntp_server); ret = server_init (nntp_server, NNTP_TCP_NAME, NNTP_TCP_PORT); if (update == FALSE && ret != -1 && cmd_line) { fputc ('\n', stdout); } debug_nntp_respcode (ret); switch (ret) { case OK_CANPOST: #ifndef NO_POSTING can_post = TRUE; #endif break; case OK_NOPOST: can_post = FALSE; break; case -1: error_message (txt_failed_to_connect_to_server, nntp_server); sleep (2); return -1; default: sprintf (msg, "%s: %s", progname, nntp_respcode (ret)); error_message (msg, ""); sleep (2); return -1; } /* * If INN NNRP switch to mode reader */ debug_nntp ("nntp_open", "mode reader"); put_server ("mode reader"); if (get_respcode () != ERR_COMMAND) { inn_nntp_server = TRUE; } /* * Check if NNTP/INN supports XOVER command */ debug_nntp ("nntp_open", "xover"); put_server ("xover"); if (get_respcode () != ERR_COMMAND) { xover_supported = TRUE; xindex_supported = TRUE; /* a hack to get xindex behaviour */ } /* * Check if NNTP supports my XINDEX & XUSER commands */ #ifndef DONT_HAVE_NNTP_EXTS debug_nntp ("nntp_open", "xindex"); put_server ("xindex"); if (get_respcode () != ERR_COMMAND) { xindex_supported = TRUE; } debug_nntp ("nntp_open", "xuser"); put_server ("xuser"); if (get_respcode () != ERR_COMMAND) { xuser_supported = TRUE; } #endif /* DONT_HAVE_NNTP_EXTS */ /* * Check if NNTP server expects user authorization */ authorization (nntp_server, userid); /* * Check if overview indexes contain Xref: lines */ xref_supported = overview_xref_support (); } #ifndef DONT_HAVE_NNTP_EXTS /* * Find out if NNTP supports SPOOLDIR command */ get_spooldir (); #endif /* DONT_HAVE_NNTP_EXTS */ #endif return 0; } void nntp_close () { #ifdef NNTP_ABLE if (read_news_via_nntp) { debug_nntp ("nntp_close", "END"); close_server (); } #endif } /* * Open the mail active file locally */ FILE * open_mail_active_fp (mode) char *mode; { return fopen (mail_active_file, mode); } /* * Open the news active file locally or send the LIST command via NNTP */ FILE * open_news_active_fp () { int respcode; if (read_news_via_nntp) { #ifdef NNTP_ABLE put_server ("list"); if ((respcode = get_respcode ()) != OK_GROUPS) { debug_nntp ("open_news_active_fp", "NOT_OK"); error_message ("%s", nntp_respcode (respcode)); return (FILE *) 0; } debug_nntp ("open_news_active_fp", "OK"); return nntp_to_fp (); #else return (FILE *) 0; #endif } else { return fopen (news_active_file, "r"); } } /* * Open the LIBDIR/overview.fmt file locally or send the LIST OVERVIEW.FMT * command via NNTP */ FILE * open_overview_fmt_fp () { char line[NNTP_STRLEN]; if (read_news_via_nntp) { #ifdef NNTP_ABLE if (xover_supported) { sprintf (line, "list %s", OVERVIEW_FMT); debug_nntp ("open_overview_fmt_fp", line); put_server (line); if (get_respcode () != OK_GROUPS) { debug_nntp ("open_overview_fmt_fp", "NOT_OK"); return (FILE *) 0; } debug_nntp ("open_overview_fmt_fp", "OK"); return nntp_to_fp (); } else { return (FILE *) 0; } #else return (FILE *) 0; #endif } else { joinpath (line, libdir, OVERVIEW_FMT); return fopen (line, "r"); } } /* * Open the ~/.tin/active file locally or send the NEWGROUPS command via NNTP * * NEWGROUPS 311299 235959 */ FILE * open_newgroups_fp (index) int index; { char line[NNTP_STRLEN]; if (read_news_via_nntp) { #ifdef NNTP_ABLE if (index == -1) { return (FILE *) 0; } sprintf (line, "newgroups %s", active_size[index].attribute); debug_nntp ("open_newgroups_fp", line); put_server (line); if (get_respcode () != OK_NEWGROUPS) { debug_nntp ("open_newgroups_fp", "NOT_OK"); return (FILE *) 0; } debug_nntp ("open_newgroups_fp", "OK"); return nntp_to_fp (); #else return (FILE *) 0; #endif } else { joinpath (line, rcdir, ACTIVE_FILE); return fopen (line, "r"); } } /* * Open the news motd file locally or on the NNTP server * * XMOTD 311299 235959 [GMT] */ FILE * open_motd_fp (motd_file_date) char *motd_file_date; { char line[NNTP_STRLEN]; if (read_news_via_nntp) { #if defined(NNTP_ABLE) && !defined(DONT_HAVE_NNTP_EXTS) sprintf (line, "xmotd %s", motd_file_date); debug_nntp ("open_motd_fp", line); put_server (line); if (get_respcode () != OK_XMOTD) { debug_nntp ("open_motd_fp", "NOT_OK"); return (FILE *) 0; } debug_nntp ("open_motd_fp", "OK"); return nntp_to_fp (); #else return (FILE *) 0; #endif } else { return fopen (motd_file, "r"); } } FILE * open_subscription_fp () { if (read_news_via_nntp) { #ifdef NNTP_ABLE put_server ("list subscriptions"); if (get_respcode () != OK_GROUPS) { debug_nntp ("open_subscription_fp", "NOT_OK"); return (FILE *) 0; } debug_nntp ("open_subscription_fp", "OK"); return nntp_to_fp (); #else return (FILE *) 0; #endif } else { return fopen (subscriptions_file, "r"); } } /* * Open mail groups description file. */ FILE * open_mailgroups_fp () { return fopen (mailgroups_file, "r"); } /* * If reading via NNTP the newsgroups file will be saved to ~/.tin/newsgroups * so that any subsequent rereads on the active file will not have to waste * net bandwidth and the local copy of the newsgroups file can be accessed. */ FILE * open_newsgroups_fp () { if (read_news_via_nntp) { #ifdef NNTP_ABLE if (read_local_newsgroups_file) { debug_nntp ("open_newsgroups_fp", "Using local copy of newsgroups file"); return fopen (local_newsgroups_file, "r"); } else { put_server ("list newsgroups"); if (get_respcode () != OK_GROUPS) { debug_nntp ("open_newsgroups_fp", "NOT_OK"); return (FILE *) 0; } debug_nntp ("open_newsgroups_fp", "OK"); return nntp_to_fp (); } #else return (FILE *) 0; #endif } else { return fopen (newsgroups_file, "r"); } } /* * Open a group XINDEX file */ FILE * open_xindex_fp (group_name) char *group_name; { extern char index_file[PATH_LEN]; char line[NNTP_STRLEN]; int group_type; group_type = find_index_file (group_name); if (group_type == -1) { return (FILE *) 0; } if (debug == 2) { error_message ("INDEX file=[%s]", index_file); } if (read_news_via_nntp && xindex_supported && group_type == GROUP_TYPE_NEWS) { #ifdef NNTP_ABLE sprintf (line, "xindex %s", group_name); debug_nntp ("open_xindex_fp", line); put_server (line); if (get_respcode () != OK_XINDEX) { debug_nntp ("open_xindex_fp", "NOT_OK"); return (FILE *) 0; } debug_nntp ("open_xindex_fp", "OK"); return nntp_to_fp (); #else return (FILE *) 0; #endif } else { return fopen (index_file, "r"); } } /* * Open a group XOVER file */ FILE * open_xover_fp (group_name, min, max) char *group_name; long min; long max; { char group_path[PATH_LEN]; char xover_file[PATH_LEN]; char line[NNTP_STRLEN]; if (read_news_via_nntp && xover_supported) { #ifdef NNTP_ABLE sprintf (line, "xover %ld-%ld", min, max); debug_nntp ("open_xover_fp", line); put_server (line); if (get_respcode () != OK_XOVER) { debug_nntp ("open_xover_fp", "NOT_OK"); return (FILE *) 0; } debug_nntp ("open_xover_fp", "OK"); return nntp_to_fp (); #else return (FILE *) 0; #endif } else { char tbuf[256]; make_group_path (group_name, group_path); #ifdef VMS joindir(tbuf, novrootdir, group_path); joinpath(xover_file, tbuf, OVERVIEW_FILE); #else sprintf (xover_file, "%s/%s/%s", novrootdir, group_path, OVERVIEW_FILE); #endif if (debug == 2) { error_message ("XOVER file=[%s]", xover_file); } return fopen (xover_file, "r"); } } /* * Stat a mail/news article to see if it still exists */ int stat_article (art, group_path) long art; char *group_path; { char buf[NNTP_STRLEN]; int i, respcode; int art_exists = TRUE; struct stat sb; i = my_group[cur_groupnum]; if (read_news_via_nntp && active[i].type == GROUP_TYPE_NEWS) { #ifdef NNTP_ABLE sprintf (buf, "stat %ld", art); debug_nntp ("stat_article", buf); put_server (buf); if ((respcode = get_respcode ()) != OK_NOTEXT) { art_exists = FALSE; } #endif } else { joinpath (buf, active[i].spooldir, group_path); sprintf (&buf[strlen (buf)], "/%ld", art); if (stat (buf, &sb) == -1) { art_exists = FALSE; } } return art_exists; } char * open_art_header (art) long art; { char *ptr, buf[NNTP_STRLEN]; FILE *fp; int full, items = 0, len; int safe_nntp_strlen; static char mem[HEADER_LEN]; if (read_news_via_nntp && active[my_group[cur_groupnum]].type == GROUP_TYPE_NEWS) { #ifdef NNTP_ABLE /* * Don't bother requesting if we have not got there yet. * This is a big win if the group has got holes in it (ie. if 000's * of articles have expired between active files min & max values). */ if (art < head_next) { return (char *) 0; } sprintf (buf, "head %ld", art); debug_nntp ("open_art_header", buf); put_server (buf); if (get_respcode () != OK_HEAD) { debug_nntp ("open_art_header", "NOT_OK_HEAD - Find NEXT"); /* * HEAD failed, try to find NEXT */ put_server ("next"); if (get_server (buf, NNTP_STRLEN) == -1) { error_message (txt_connection_to_server_broken, ""); tin_done (1); } if (atoi (buf) == OK_NOTEXT) { ptr = buf; while (isspace(*ptr) || isdigit(*ptr)) { ptr++; } head_next = atoi (ptr); } return (char *) 0; } debug_nntp ("open_art_header", "OK_HEAD"); full = FALSE; safe_nntp_strlen = NNTP_STRLEN - 2; len = safe_nntp_strlen; ptr = mem; while (1) { if (full || len < 32) { full = TRUE; ptr = buf; len = safe_nntp_strlen; } if (get_server (ptr, len) == -1) { error_message (txt_connection_to_server_broken, ""); tin_done (1); } if (strcmp (ptr, ".") == 0) { /* end of text */ break; } if (ptr[0] == '.') { /* reduce leading .'s */ while (ptr[1]) { ptr[0] = ptr[1]; ptr++; len++; } } else { while (*ptr) { ptr++; len++; } *ptr++ = '\n'; *ptr = '\0'; len++; } } #else return (char *) 0; #endif } else { sprintf (buf, "%ld", art); fp = fopen (buf, "r"); if (fp != (FILE *) 0) { items = fread (mem, 1, sizeof (mem)-1, fp); fclose (fp); } /* printf ("Artnum=[%ld] Items=[%d]\n", art, items); fflush (stdout); sleep (1); */ if (items == 0) { return (char *) 0; } } return mem; } /* * Open a mail/news article */ FILE * open_art_fp (group_path, art) char *group_path; long art; { char buf[NNTP_STRLEN]; int i, respcode; struct stat sb; extern long note_size; i = my_group[cur_groupnum]; if (read_news_via_nntp && active[i].type == GROUP_TYPE_NEWS) { #ifdef NNTP_ABLE sprintf (buf, "article %ld", art); debug_nntp ("open_art_fp", buf); put_server (buf); if ((respcode = get_respcode ()) != OK_ARTICLE) { if (debug == 2) { error_message ("%s", nntp_respcode (respcode)); } debug_nntp ("open_art_fp", "NOT OK"); return (FILE *) 0; } debug_nntp ("open_art_fp", "OK"); return nntp_to_fp (); #else return (FILE *) 0; #endif } else { joinpath (buf, active[i].spooldir, group_path); sprintf (&buf[strlen (buf)], "/%ld", art); if (debug == 2) { error_message ("ART=[%s]", buf); } if (stat (buf, &sb) == -1) { note_size = 0; } else { note_size = sb.st_size; } return fopen (buf, "r"); } } FILE * open_xhdr_fp (header, min, max) char *header; long min; long max; { char buf[NNTP_STRLEN]; if (read_news_via_nntp) { #ifdef NNTP_ABLE sprintf(buf, "xhdr %s %ld-%ld", header, min, max); debug_nntp ("open_xhdr_fp", buf); put_server (buf); if (get_respcode () != OK_HEAD) { debug_nntp ("open_xhdr_fp", "NOT_OK_XHDR"); return (FILE *) 0; } debug_nntp ("open_xhdr_fp", "OK_XHDR"); return nntp_to_fp (); #else return (FILE *) 0; #endif } else { return (FILE *) 0; } } /* * Longword comparison routine for the qsort() */ int base_comp (p1, p2) t_comptype *p1; t_comptype *p2; { long *a = (long *) p1; long *b = (long *) p2; if (*a < *b) return -1; if (*a > *b) return 1; return 0; } /* * Read the article numbers existing in a group's spool directory * into base[] and sort them. top_base is one past top. */ void setup_base (group, group_path) char *group; char *group_path; { char buf[NNTP_STRLEN]; #ifdef NNTP_ABLE char line[NNTP_STRLEN]; #endif DIR *d; DIR_BUF *e; int i; long art, start, last, dummy, count; top_base = 0; i = my_group[cur_groupnum]; if (read_news_via_nntp && active[i].type == GROUP_TYPE_NEWS) { #ifdef NNTP_ABLE sprintf (buf, "group %s", group); debug_nntp ("setup_base", buf); put_server (buf); if (get_server (line, NNTP_STRLEN) == -1) { error_message (txt_connection_to_server_broken, ""); tin_done (1); } if (atoi (line) != OK_GROUP) { debug_nntp ("setup_base", "NOT_OK"); return; } debug_nntp ("setup_base", line); sscanf (line,"%ld %ld %ld %ld", &dummy, &count, &start, &last); if (inn_nntp_server) { sprintf (buf, "listgroup %s", group); debug_nntp ("setup_base", buf); put_server (buf); if (get_server (line, NNTP_STRLEN) == -1) { error_message (txt_connection_to_server_broken, ""); tin_done (1); } if (atoi (line) != OK_GROUP) { debug_nntp ("setup_base, listgroup", "NOT_OK"); return; } debug_nntp ("setup_base", line); while (TRUE) { if (get_server (line, NNTP_STRLEN) == -1) { error_message (txt_connection_to_server_broken, ""); tin_done (1); } if (strcmp (line, ".") == 0) { break; /* end of text */ } if (top_base >= max_art) { expand_art (); } base[top_base++] = atoi (line); } } else { if (last - count > start) { count = last - start; } while (start <= last) { if (top_base >= max_art) { expand_art(); } base[top_base++] = start++; } } #else return; #endif } else { joinpath (buf, active[i].spooldir, group_path); if (access (buf, 4) != 0) { return; } d = opendir (buf); if (d != NULL) { while ((e = readdir (d)) != NULL) { #ifdef M_OS2 art = my_atol (e->d_name, strlen (e->d_name)); #else art = my_atol (e->d_name, (int) e->D_LENGTH); #endif if (art >= 0) { if (top_base >= max_art) expand_art (); base[top_base++] = art; } } closedir (d); qsort ((char *) base, top_base, sizeof (long), base_comp); } } } /* * get a response code from the server and return it to the caller */ int get_respcode () { #ifdef NNTP_ABLE char line[NNTP_STRLEN]; if (get_server (line, NNTP_STRLEN) == -1) { error_message (txt_connection_to_server_broken, ""); tin_done (1); } debug_nntp ("get_respcode", line); return atoi (line); #else return (0); #endif } int stuff_nntp (fnam) char *fnam; { #ifdef NNTP_ABLE FILE *fp; char line[HEADER_LEN]; extern char *mktemp }~ TIN-1_22.BCKVd[SRC.TIN-1_22]OPEN.C;2*!(); struct stat sb; extern long note_size; sprintf (fnam, "%stin_nntpXXXXXX", TMPDIR); mktemp (fnam); if ((fp = fopen (fnam, "w")) == (FILE *) 0) { perror_message (txt_stuff_nntp_cannot_open, fnam); return FALSE; } while (1) { if (get_server (line, sizeof (line)-1) == -1) { error_message (txt_connection_to_server_broken, ""); tin_done (1); } debug_nntp ("stuff_nntp", line); if (strcmp (line, ".") == 0) { /* end of text */ break; } strcat (line, "\n"); if (line[0] == '.') { /* reduce leading .'s */ fputs (&line[1], fp); } else { fputs (line, fp); } } fclose (fp); if (stat (fnam, &sb) < 0) { note_size = 0; } else { note_size = sb.st_size; } return TRUE; #else return TRUE; #endif } FILE * nntp_to_fp () { #ifdef NNTP_ABLE char fnam[PATH_LEN]; FILE *fp = (FILE *) 0; if (! stuff_nntp (fnam)) { debug_nntp ("nntp_to_fp", "! stuff_nntp()"); return (FILE *) 0; } if ((fp = fopen (fnam, "r")) == (FILE *) 0) { perror_message (txt_nntp_to_fp_cannot_reopen, fnam); return (FILE *) 0; } unlink (fnam); return fp; #else return (FILE *) 0; #endif } /* * Log user info to local file or NNTP logfile */ void log_user () { char dummy[PATH_LEN]; char log_file[PATH_LEN]; char buf[32], *ptr; char line[NNTP_STRLEN]; #ifndef DONT_LOG_USER FILE *fp; long epoch; #endif #ifndef M_AMIGA extern struct passwd *myentry; get_user_info (dummy, buf); if (read_news_via_nntp && xuser_supported) { if ((ptr = (char *) strchr(buf, ','))) { *ptr = '\0'; } sprintf (line, "xuser %s (%s)", myentry->pw_name, buf); debug_nntp ("log_user", line); put_server (line); } else #endif /* M_AMIGA */ { #ifndef DONT_LOG_USER joinpath (log_file, TMPDIR, LOG_USER_FILE); if ((fp = fopen (log_file, "a+")) != (FILE *) 0) { time (&epoch); fprintf (fp, "%s%s: %-32s (%-8s) %s", VERSION, PATCHLEVEL, #ifdef M_AMIGA get_val ("REALNAME", "Unknown"), get_val ("USERNAME", "Unknown"), #else buf, myentry->pw_name, #endif ctime (&epoch)); fclose (fp); chmod (log_file, 0666); } #endif /* DONT_LOG_USER */ } } /* * NNTP user authorization. Password read from ~/.newsauth * The ~/.newsauth authorization file has the format: * nntpserver1 password * nntpserver2 password * etc. */ void authorization (server, authuser) char *server; char *authuser; { char authfile[PATH_LEN]; char authpass[PATH_LEN]; char line[NNTP_STRLEN]; char buf[PATH_LEN], *ptr; int found = FALSE; FILE *fp; /* * Check if running via NNTP */ if (! read_news_via_nntp) { return; } /* * Lets check if the NNTP supports authorization */ debug_nntp ("authorization", "authinfo"); put_server ("authinfo"); if (get_respcode () == ERR_COMMAND) { return; } joinpath (authfile, homedir, ".newsauth"); if ((fp = fopen (authfile,"r")) != (FILE *) 0) { /* * Search through authorization file for correct NNTP server * File has format: 'nntp-server' 'password' */ while (fgets (buf, sizeof (buf), fp) != (char *) 0) { /* * Get server from 1st part of the line */ strcpy (line, buf); ptr = (char *) strchr (line, ' '); if (ptr != (char *) 0) { *ptr = '\0'; } if (strncmp (line, server, sizeof (server)) == 0) { /* * Get passwdord from 2nd part of the line */ ptr = (char *) strrchr (buf, ' '); if (ptr != (char *) 0 && ++ptr != (char *) 0) { strcpy (authpass, ptr); ptr = (char *) strchr (authpass, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } found = TRUE; } break; } } fclose (fp); if (! found) { error_message (txt_nntp_authorization_failed, authuser); } else { sprintf (line, "authinfo user %s", authuser); put_server (line); get_respcode (); sprintf (line, "authinfo pass %s", authpass); put_server (line); get_respcode (); } } } *[SRC.TIN-1_22]OS_2.C;1+,.// 4 -d0@123KPWO56ct7s7p189]VG/HJ/* * Project : tin - a Usenet reader * Module : os_2.c * Author : A.Wrede & I.Lea * Created : 05-07-93 * Updated : 29-08-93 * Notes : Extra functions for OS/2 port * Copyright : (c) Copyright 1991-93 by Andreas Wrede & Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #if defined(M_OS2) #include "os_2.h" struct passwd no_pw = { "OS2User", "x", 1, 1, "", "OS/2 User", "OS/2 User", "C:/", "CMD" }; char *optarg; FILE *_pwf; int fakepw = 1; int optind = 1; /* * stub for tputs */ #ifndef INDEX_DAEMON extern int outchar (int); tputs (str, zzz, func) register char *str; int zzz; int (*func)(int); { if (! str) { return; } if (func == outchar) { fputs (str, stdout); } else { while (*str) { if (*str == '\n') { func('\r'); } func(*str++); } } } #endif void backslash ( char *ptr) { while (*ptr) { if (*ptr == '/') { *ptr = '\\'; } ptr++; } } /* * joinpath tacks a file (or sub dir) on to the end of a directory name. * Have to check existing / or \ to avoid doubling them. */ void joinpath ( char *str, char *dir, char *file) { char c; if (strlen (dir) == 0) { strcpy (str, file); return; } c = dir[strlen (dir) - 1]; if (c=='/' || c=='\\') { sprintf (str, "%s%s", dir, file); } else { sprintf (str, "%s/%s", dir, file); } } #ifndef __TURBOC__ FILE * popen ( char *command, char *mode) { return (FILE *) 0; } void pclose ( FILE *pipe) { return; } DIR * opendir ( const char *name) { return NULL; } struct dirent * readdir ( DIR *di) { return NULL; } int closedir ( DIR *di) { return 0; } #endif /* __TURBOC__ */ char getopt ( int argc, char *argv[], char *options) { char c, *z; static int subind = 0; for (;optind < argc ; optind++) { if (subind == 0) { c = argv[optind][0]; if (c != '-') { return EOF; } subind = 1; } c = argv[optind][subind]; if (c != 0) { break; } } if (optind == argc) { return EOF; } /* * get rid of funnies */ if (c == ':' || c == '?') { return '?'; } if ((z = strchr (options, c)) == 0) { return '?'; } if (z[1] == ':') { if (argv[optind][subind+1]) { optarg = &argv[optind][subind+1]; } else { optarg = argv[++optind]; } optind++; subind = 0; return c; } subind++; return c; } int make_post_cmd ( char *cmd, char *name) { char *p; if ((p = getenv (ENV_VAR_POSTER)) != (char *) 0) { sprintf (cmd, p, name); } else { sprintf (cmd, DEFAULT_POSTER, name); } return 0; } int gethostname ( char *name, int namelen) { char *p; if ((p = getenv ("HOSTNAME")) == (char *) 0) { errno = 1; return (-1); } strncpy (name, p, namelen); return (0); } #ifdef __TURBOC__ int sleep ( int sec) { DosSleep (sec * 1000); return; } #endif /* __TURBOC__ */ struct passwd * fakepwent (void) { if (! fakepw) { return (struct passwd *) 0; } fakepw = 0; if ((no_pw.pw_name = getenv ("USER")) == NULL) { no_pw.pw_name = "OS2USER"; } no_pw.pw_uid = 1; no_pw.pw_gid = 1; no_pw.pw_age = ""; if ((no_pw.pw_comment = getenv ("REALNAME")) == NULL) { no_pw.pw_comment = "OS2 User"; } no_pw.pw_gecos = no_pw.pw_comment; if ((no_pw.pw_dir = getenv ("HOME")) == NULL) { no_pw.pw_dir = "C:"; } if ((no_pw.pw_shell = getenv ("SHELL")) == NULL) { if ((no_pw.pw_shell = getenv ("COMSPEC")) == NULL) { no_pw.pw_shell = "CMD.EXE"; } } return (&no_pw); } struct passwd * getpwent (void) { char pwn[512]; if (! _pwf) { if (getenv ("ETC") == NULL) { return (fakepwent ()); } sprintf (pwn, "%s/passwd", getenv ("ETC")); if ((_pwf = fopen (pwn, "r")) == NULL) { return(fakepwent ()); } } return (fgetpwent (_pwf)); } struct passwd * getpwuid ( uid_t uid) { struct passwd *pw; endpwent (); while (1) { pw = getpwent (); if ((!pw) || (pw->pw_uid == uid)) { return (pw); } } return NULL; } struct passwd * getpwnam ( const char *name) { struct passwd *pw; endpwent(); while (1) { pw = getpwent (); if ((!pw) || (strcmp (pw->pw_name, name) == 0)) { return (pw); } } return NULL; } void setpwent (void) { if (_pwf) { rewind (_pwf); } } void endpwent (void) { if (_pwf) { fclose (_pwf); _pwf = NULL; } fakepw = 1; } struct passwd * fgetpwent ( FILE *f) { char *line; static char pwe[512]; static struct passwd _pw; if (fgets (pwe, sizeof (pwe), f) == NULL) { return (fakepwent ()); } line = pwe; if (line[strlen (line)-1] == '\n') { line[strlen (line)-1] = 0; } if (line[strlen (line)-1] == '\r') { line[strlen (line)-1] = 0; } _pw.pw_name = line; while ((*line) && (*line != ':')) { line++; } if (! *line) { return (struct passwd *) 0; } *line = 0; _pw.pw_passwd = ++line; while ((*line) && (*line != ':')) { line++; } if (! *line) { return (struct passwd *) 0; } *line = 0; _pw.pw_uid = atoi (++line); while ((*line) && (*line != ':')) { line++; } if (! *line) { return (struct passwd *) 0; } *line = 0; _pw.pw_gid = atoi (++line); while ((*line) && (*line != ':')) { line++; } if (! *line) { return (struct passwd *) 0; } *line = 0; _pw.pw_age = ""; _pw.pw_comment = ++line; _pw.pw_gecos = line; while ((*line) && (*line != ':')) { line++; } if (! *line) { return (struct passwd *) 0; } *line = 0; _pw.pw_dir = ++line; if ((_pw.pw_dir[1] == ':') && (tolower (_pw.pw_dir[0]) >= 'c') && (tolower (_pw.pw_dir[0]) <= 'z')) { line += 2; } while ((*line) && (*line != ':')) { line++; } if (! *line) { return (struct passwd *) 0; } *line = 0; _pw.pw_shell = ++line; return (&_pw); } int putpwent ( const struct passwd *pwd, FILE *f) { return(-1); } char * getlogin (void) { char *p; if ((p = getenv ("USER")) == NULL) { return (char *) 0; } return (p); } int getuid (void) { char *p; if ((p = getenv ("UID")) == NULL) { return (0); /* pretend we are root */ } return atoi (p); } #endif /* M_OS2 */ *[SRC.TIN-1_22]OS_2.H;1+,.// 4-d0@123KPWO56t7Fg189]VG/HJ /* * Project : tin - a Usenet reader * Module : os_2.h * Author : A.Wrede & I.Lea * Created : 05-07-92 * Updated : 22-08-92 * Notes : Directory support for OS/2 * Copyright : (c) Copyright 1991-93 by Andreas Wrede & Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #if defined(M_OS2) #ifndef OS_2_H #define OS_2_H #include #include #define INCL_KBD #ifdef __TURBOC__ #define INCL_NOPMAPI #define INCL_DOSPROCESS APIRET APIENTRY DosSleep(ULONG msec); #endif #define popen _popen #define pclose _pclose #define NOBSD #undef BSD /* stupid define in IBM libs ? */ #define u_char unsigned char #define u_short unsigned short #define u_long unsigned long typedef int uid_t; typedef int gid_t; struct dirent { char d_name[256]; }; typedef struct { unsigned long _d_hdir; char *_d_dirname; unsigned _d_magic; unsigned _d_nfiles; char *_d_bufp; char _d_buf[512]; } DIR; struct passwd { char *pw_name; char *pw_passwd; uid_t pw_uid; gid_t pw_gid; char *pw_age; char *pw_comment; char *pw_gecos; char *pw_dir; char *pw_shell; }; #if !defined(_POSIX_SOURCE) struct comment { char *c_dept; char *c_name; char *c_acct; char *c_bin; }; #endif /* _POSIX_SOURCE */ extern int closedir (DIR *__dir); extern char getopt (int argc,char *argv[],char *options); extern void endpwent (void); extern struct passwd *fgetpwent (FILE *); extern char *getlogin (void); extern struct passwd *getpwent (void); extern struct passwd *getpwuid (uid_t); extern struct passwd *getpwnam (const char *); extern int getuid (void); extern void backslash(char *ptr); extern int make_post_cmd (char *cmd,char *name); extern int make_post_process_cmd (char *cmd,char *dir,char *file); extern DIR *opendir (const char *__dirname); extern int putpwent (const struct passwd *, FILE *); extern struct dirent *readdir (DIR *__dir); extern void rewinddir (DIR *__dir); extern void setpwent (void); #endif /* OS_2_H */ #endif /* M_OS2 */ *[SRC.TIN-1_22]PAGE.C;1+,.6// 466-d0@123KPWO756 Bat7s7p189]VG/HJ/* * Project : tin - a Usenet reader * Module : page.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 21-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" char note_h_path[LEN]; /* Path: */ char note_h_date[PATH_LEN]; /* Date: */ char note_h_subj[LEN]; /* Subject: */ char note_h_org[PATH_LEN]; /* Organization: */ char note_h_newsgroups[LEN]; /* Newsgroups: */ char note_h_messageid[PATH_LEN]; /* Message-ID: */ char note_h_references[PATH_LEN]; /* References: */ char note_h_distrib[PATH_LEN]; /* Distribution: */ char note_h_keywords[LEN]; /* Keywords: */ char note_h_summary[LEN]; /* Summary: */ char note_h_followup[LEN]; /* Followup-To: */ char note_h_mimeversion[PATH_LEN]; /* Mime-Version: */ char note_h_contenttype[LEN]; /* Content-Type: */ char *glob_page_group; FILE *note_fp; /* the body of the current article */ int glob_respnum; int last_resp; /* current & previous article for - command */ int note_end; /* we're done showing this article */ int note_line; int note_page; /* what page we're on */ int rotate; /* 0=normal, 13=rot13 decode */ int this_resp; int doing>_pgdn; long note_mark[MAX_PAGES]; /* ftells on beginnings of pages */ long note_size; /* stat size in bytes of article */ int show_page (respnum, threadnum, group, group_path) int respnum; int *threadnum; /* to allow movement in thread mode */ char *group; char *group_path; { #ifndef INDEX_DAEMON int ch, i, n = 0; int copy_text; int kill_state = NO_KILLING; int old_sort_art_type = default_sort_art_type; int old_top; int posted; int ret_code; long old_artnum; long art; restart: if (read_news_via_nntp && display_reading_prompt) { wait_message (txt_reading_article); } glob_respnum = respnum; glob_page_group = group; set_signals_page (); if (respnum != this_resp) { /* remember current & previous */ last_resp = this_resp; /* articles for - command */ this_resp = respnum; } rotate = 0; /* normal mode, not rot13 */ art = arts[respnum].artnum; if (arts[respnum].unread != ART_READ) { mark_all_xref_read (&arts[respnum], group_path); if (arts[respnum].hot && num_of_hot_arts) { num_of_hot_arts--; } } arts[respnum].unread = ART_READ; /* mark article as read */ if ((note_page = art_open (art, group_path)) == ART_UNAVAILABLE) { /* arts[respnum].thread = ART_EXPIRED; */ /* 21.11.92 FIXME */ sprintf (msg, txt_art_unavailable, art); if (debug) { error_message (msg, ""); } else { wait_message (msg); } return (GRP_NOREDRAW); /* special retcode to stop redrawing screen */ } else { show_note_page (respnum, group); } while (TRUE) { ch = ReadCh (); if (ch >= '0' && ch <= '9') { n = which_thread (respnum); if (! num_of_responses (n)) { info_message (txt_no_responses); } else { n = prompt_response (ch, respnum); if (n != -1) { respnum = n; goto restart; } } continue; } switch (ch) { case ESC: #ifdef HAVE_KEY_PREFIX case KEY_PREFIX: #endif switch (get_arrow_key ()) { case KEYMAP_LEFT: goto return_to_index; case KEYMAP_RIGHT: goto page_goto_next_unread; case KEYMAP_UP: case KEYMAP_PAGE_UP: goto page_up; case KEYMAP_DOWN: case KEYMAP_PAGE_DOWN: goto page_down; case KEYMAP_HOME: goto begin_of_article; case KEYMAP_END: goto end_of_article; case KEYMAP_MOUSE: set_xclick_off (); break; } break; #ifndef NO_SHELL_ESCAPE case '!': shell_escape (); redraw_page (respnum, group); break; #endif case '$': /* goto end of article */ case 'G': /* 'less' compatible */ end_of_article: if (show_last_page ()) { show_note_page (respnum, group); } break; case '-': /* show last viewed article */ if (last_resp < 0) { info_message (txt_no_last_message); break; } art_close (); respnum = last_resp; goto restart; case '|': /* pipe article/thread/tagged arts to command */ feed_articles (FEED_PIPE, PAGE_LEVEL, "Pipe", respnum, group_path); break; case '/': /* search forwards in article */ if (search_article (TRUE)) { show_note_page (respnum, group); } break; case 'B': /* article body search */ n = search_body (group_path, respnum); if (n != -1) { respnum = n; art_close (); goto restart; } break; case '<': /* goto first article in current thread */ if (arts[respnum].inthread) { n = which_thread (respnum); if (n >= 0 && base[n] != respnum) { assert (n < top_base); respnum = base[n]; art_close (); goto restart; } } break; case '>': /* goto last article in current thread */ for (i = respnum; i >= 0; i = arts[i].thread) { n = i; } if (n != respnum) { respnum = n; art_close (); goto restart; } break; case ' ': /* page down or next response */ case ctrl('D'): case ctrl('F'): /* vi style */ page_down: if (note_page == ART_UNAVAILABLE) { n = next_response (respnum); if (n == -1) { return (which_thread (respnum)); } respnum = n; goto restart; } else if (note_end) { art_close (); n = next_response (respnum); if (n == -1) { return (which_thread (respnum)); } respnum = n; goto restart; } else { doing_pgdn = TRUE; show_note_page (respnum, group); } break; case '\r': case '\n': /* go to start of next thread */ art_close (); n = next_thread (respnum); if (n == -1) return (which_thread (respnum)); respnum = n; goto restart; case '\t': /* goto next unread article */ page_goto_next_unread: if (! tab_goto_next_unread) { if (note_page == ART_UNAVAILABLE) { n = next_unread (next_response (respnum)); if (n == -1) { return (which_thread (respnum)); } respnum = n; goto restart; } else if (note_end) { art_close (); n = next_unread (next_response (respnum)); if (n == -1) { return (which_thread (respnum)); } respnum = n; goto restart; } else { show_note_page (respnum, group); } } else { if (note_page != ART_UNAVAILABLE) { art_close(); } n = next_unread (next_response (respnum)); if (n == -1) { return (which_thread (respnum)); } respnum = n; goto restart; } break; case ctrl('H'): /* show article headers */ if (note_page == ART_UNAVAILABLE) { n = next_response (respnum); if (n == -1) return (which_thread (respnum)); respnum = n; goto restart; } else { note_page = 0; note_end = FALSE; fseek(note_fp, 0L, 0); show_note_page(respnum, group); } break; case ctrl('K'): /* kill article */ if (kill_art_menu (group, respnum)) { i = which_thread (respnum); if (kill_any_articles (my_group[cur_groupnum])) { make_threads (FALSE); find_base (my_group[cur_groupnum]); if (i >= top_base) i = top_base - 1; respnum = base[i]; } } /* if (which_thread (respnum) < 0) { return GRP_UNINDEXED; } */ redraw_page (respnum, group); break; case ctrl('L'): /* redraw current page of article */ redraw_page (respnum, group); break; case ctrl('R'): /* redraw beginning of article */ case 'g': /* 'less' compatible */ begin_of_article: if (note_page == ART_UNAVAILABLE) { ClearScreen (); printf (txt_art_unavailable, arts[respnum].artnum); fflush (stdout); } else { note_page = 0; note_end = FALSE; fseek (note_fp, note_mark[0], 0); show_note_page (respnum, group); } break; case ctrl('X'): case '%': case 'd': /* toggle rot-13 mode */ rotate = (rotate ? 0 : 13); redraw_page (respnum, group); info_message (txt_toggled_rot13); break; case 'a': /* author search forward */ case 'A': /* author search backward */ i = (ch == 'a'); n = search_author (my_group[cur_groupnum], respnum, i); if (n < 0) break; respnum = n; goto restart; /* NOTREACHED */ case 'b': /* page up */ case ctrl('U'): case ctrl('B'): /* vi style */ page_up: if (note_page == ART_UNAVAILABLE) { art_close (); n = prev_response (respnum); if (n == -1) return (which_response (respnum)); respnum = n; goto restart; } else { if (note_page <= 1) { info_message (txt_begin_of_art); } else { note_page -= 2; note_end = FALSE; fseek (note_fp, note_mark[note_page], 0); show_note_page (respnum, group); } } break; case 'c': /* catchup - mark all articles as read */ case 'C': /* and goto next group */ if (!confirm_action || prompt_yn (cLINES, txt_mark_all_read, 'y')) { for (n = 0; n < top; n++) { if (arts[n].unread != ART_READ) { mark_all_xref_read (&arts[n], group_path); } arts[n].unread = ART_READ; } ret_code = (ch == 'C' ? GRP_CONTINUE : GRP_UNINDEXED); fix_new_highest (cur_groupnum); if (cur_groupnum + 1 < group_top) { cur_groupnum++; } else { ret_code = GRP_UNINDEXED; } art_close (); space_mode = TRUE; return ret_code; } break; case 'D': /* delete an article */ if (delete_article (group, respnum)) { redraw_page (respnum, group); } break; case 'f': /* post a followup to this article */ case 'F': if (! can_post) { info_message (txt_cannot_post); break; } copy_text = (ch == 'f' ? TRUE : FALSE); ret_code = post_response (group, respnum, copy_text); redraw_page (respnum, group); break; case 'h': /* help */ show_info_page (HELP_INFO, help_page, txt_art_pager_com); redraw_page (respnum, group); break; case 'H': /* toggle mini help menu */ toggle_mini_help (PAGE_LEVEL); redraw_page (respnum, group); break; case 'q': /* return to index page */ return_to_index: art_close (); if (kill_state == NO_KILLING && default_sort_art_type != old_sort_art_type) { make_threads (TRUE); find_base (my_group[cur_groupnum]); } i = which_thread (respnum); *threadnum = which_response (respnum); if (kill_state == KILLING) { old_top = top; old_artnum = arts[respnum].artnum; kill_any_articles (my_group[cur_groupnum]); make_threads (FALSE); find_base (my_group[cur_groupnum]); i = find_new_pos (old_top, old_artnum, i); } return (i); case 'I': /* toggle inverse video */ toggle_inverse_video (); redraw_page (respnum, group); break; case 'k': if (note_page == ART_UNAVAILABLE) { n = next_unread (next_response(respnum)); if (n == -1) return (which_thread (respnum)); } else { art_close (); n = next_unread (next_response (respnum)); if (n == -1) return (which_thread (respnum)); } respnum = n; goto restart; /* NOTREACHED */ case 'K': /* mark rest of thread as read */ for (n = respnum; n >= 0; n = arts[n].thread) { if (arts[n].unread != ART_READ) { mark_all_xref_read (&arts[n], group_path); if (arts[n].hot && num_of_hot_arts) { num_of_hot_arts--; } } arts[n].unread = ART_READ; } n = next_unread (next_response (respnum)); if (n == -1) goto return_to_index; art_close (); respnum = n; goto restart; /* NOTREACHED */ case 'm': /* mail article/thread/tagged articles to somebody */ feed_articles (FEED_MAIL, PAGE_LEVEL, "Mail", respnum, group_path); break; case 'M': /* options menu */ if (change_rcfile (group, FALSE) == KILLING) { kill_state = KILLING; } set_subj_from_size (cCOLS); redraw_page (respnum, group); break; case 'n': /* skip to next article */ art_close (); n = next_response (respnum); if (n == -1) return (which_thread(respnum)); respnum = n; goto restart; /* NOTREACHED */ case 'N': /* next unread article */ n = next_unread (next_response (respnum)); if (n == -1) info_message (txt_no_next_unread_art); else { art_close (); respnum = n; goto restart; } break; case 'o': /* output art/thread/tagged arts to printer */ feed_articles (FEED_PRINT, PAGE_LEVEL, "Print", respnum, group_path); break; case 'p': /* previous article */ art_close (); n = prev_response (respnum); if (n == -1) return (which_response (respnum)); respnum = n; goto restart; case 'P': /* previous unread article */ n = prev_unread (prev_response (respnum)); if (n == -1) info_message (txt_no_prev_unread_art); else { art_close (); respnum = n; goto restart; } break; case 'Q': /* quit */ return GRP_QUIT; case 'r': /* reply to author through mail */ case 'R': copy_text = (ch == 'r' ? TRUE : FALSE); mail_to_author (group, respnum, copy_text); redraw_page (respnum, group); break; case 's': /* save article/thread/tagged articles */ feed_articles (FEED_SAVE, PAGE_LEVEL, "Save", respnum, group_path); break; case 't': /* tag/untag article for saving */ if (arts[respnum].tagged) { arts[respnum].tagged = 0; info_message (txt_untagged_art); } else { arts[respnum].tagged = ++num_of_tagged_arts; info_message (txt_tagged_art); } break; case 'T': /* return to group selection page */ art_close (); if (kill_state == KILLING) { kill_any_articles (my_group[cur_groupnum]); make_threads (FALSE); find_base (my_group[cur_groupnum]); } update_newsrc (group, my_group[cur_groupnum], FALSE); fix_new_highest (cur_groupnum); return -1; case 'v': info_message (cvers); break; case 'w': /* post a basenote */ if (post_article (group, &posted)) { redraw_page (respnum, group); } break; case 'W': /* display messages posted by user */ if (user_posted_messages ()) { redraw_page (respnum, group); } break; case 'x': /* crosspost current article */ feed_articles (FEED_XPOST, PAGE_LEVEL, "Crosspost", respnum, group_path); break; case 'z': /* mark article as unread (to return) */ if (arts[n].unread != ART_UNREAD) { if (arts[n].hot) { num_of_hot_arts++; } } arts[respnum].unread = ART_WILL_RETURN; info_message (txt_art_marked_as_unread); break; default: info_message(txt_bad_command); } } #endif /* INDEX_DAEMON */ } void redraw_page (respnum, group) int respnum; char *group; { if (note_page == ART_UNAVAILABLE) { ClearScreen (); printf (txt_art_unavailable, arts[respnum].artnum); fflush (stdout); } else if (note_page > 0) { note_page--; fseek (note_fp, note_mark[note_page], 0); show_note_page (respnum, group); } } void show_note_page (respnum, group) int respnum; char *group; { #ifndef INDEX_DAEMON char buf2[LEN+50]; char *p, *q; int i, j; int ctrl_L; /* form feed character detected */ int first = TRUE; int lines; long tmp_pos; static char buf[LEN]; if (beginner_level) { lines = cLINES - (MINI_HELP_LINES - 1); } else { lines = cLINES; } ClearScreen (); note_line = 1; if (note_size == 0L) { tmp_pos = ftell (note_fp); fseek (note_fp, 0L, 2); /* goto end of article */ note_size = ftell (note_fp); fseek (note_fp, tmp_pos, 0); /* goto old position */ } if (note_page == 0) { buf2[0] = '\0'; doing_pgdn = FALSE; show_first_header (respnum, group); } else { show_cont_header (respnum); } #ifdef HAVE_METAMAIL if (note_page == 0 && *note_h_mimeversion && *note_h_contenttype && strncmp ("text/plain", note_h_contenttype, 10)) { if (prompt_yn (cLINES, txt_use_mime, 'y')) { show_mime_article (note_fp, &arts[respnum]); return; } else { show_first_header (respnum, group); } } #endif ctrl_L = FALSE; while (note_line < lines) { if (show_last_line_prev_page) { note_mark[note_page+1] = ftell (note_fp); if (doing_pgdn && first && buf2[0]) { goto print_a_line; } } first = FALSE; if (fgets (buf, sizeof (buf), note_fp) == NULL) { note_end = TRUE; break; } buf[LEN-1] = '\0'; if (rotate) for (p = buf, q = buf2; *p && *p != '\n' && q < &buf2[LEN]; p++) { if (*p == '\b' && q > buf2) { q--; } else if (*p == 12) { /* ^L */ *q++ = '^'; *q++ = 'L'; ctrl_L = TRUE; } else if (*p == '\t') { i = q - buf2; j = (i|7) + 1; while (i++ < j) *q++ = ' '; } else if (((*p) & 0xFF) < ' ') { *q++ = '^'; *q++ = ((*p) & 0xFF) + '@'; } else if (*p >= 'A' && *p <= 'Z') *q++ = 'A' + (*p - 'A' + rotate) % 26; else if (*p >= 'a' && *p <= 'zB~ TIN-1_22.BCKd[SRC.TIN-1_22]PAGE.C;16T(#') *q++ = 'a' + (*p - 'a' + rotate) % 26; else *q++ = *p; } else for (p = buf, q = buf2; *p && *p != '\n' && q < &buf2[LEN]; p++) { if (*p == '\b' && q > buf2) { q--; } else if (*p == 12) { /* ^L */ *q++ = '^'; *q++ = 'L'; ctrl_L = TRUE; } else if (*p == '\t') { i = q - buf2; j = (i|7) + 1; while (i++ < j) *q++ = ' '; } else if (((*p) & 0xFF) < ' ') { *q++ = '^'; *q++ = ((*p) & 0xFF) + '@'; } else *q++ = *p; } *q = '\0'; print_a_line: if (first) { StartInverse (); } strip_line (buf2, strlen (buf2)); printf("%s\r\n", buf2); if (first) { EndInverse (); } first = FALSE; doing_pgdn = FALSE; note_line += ((int) strlen (buf2) / cCOLS) + 1; if (ctrl_L) { break; } } if (! show_last_line_prev_page) { note_mark[++note_page] = ftell (note_fp); } else { note_page++; } if (ftell (note_fp) == note_size) { note_end = TRUE; } if (note_end) { MoveCursor (cLINES, MORE_POS-(5+BLANK_PAGE_COLS)); StartInverse (); if (arts[respnum].thread != -1) { fputs (txt_next_resp, stdout); } else { fputs (txt_last_resp, stdout); } fflush (stdout); EndInverse (); } else { if (note_size > 0) { draw_percent_mark (note_mark[note_page], note_size); } else { MoveCursor (cLINES, MORE_POS-BLANK_PAGE_COLS); StartInverse (); fputs (txt_more, stdout); fflush (stdout); EndInverse (); } } show_mini_help (PAGE_LEVEL); MoveCursor (cLINES, 0); #endif /* INDEX_DAEMON */ } void show_mime_article (fp, art) FILE *fp; struct t_article *art; { char buf[LEN]; FILE *mime_fp; long offset; Raw(FALSE); offset = ftell (fp); rewind (fp); printf ("mime article\n"); sprintf (buf, METAMAIL_CMD); mime_fp = popen (buf, "w"); while (fgets (buf, sizeof (buf), fp) != NULL) { fputs (buf, mime_fp); } fflush (mime_fp); pclose (mime_fp); note_end = TRUE; Raw(TRUE); fseek (fp, offset, 0); /* goto old position */ MoveCursor (cLINES, MORE_POS-(5+BLANK_PAGE_COLS)); StartInverse (); if (art->thread != -1) { fputs (txt_next_resp, stdout); } else { fputs (txt_last_resp, stdout); } fflush (stdout); EndInverse (); } void show_first_header (respnum, group) int respnum; char *group; { char buf[LEN]; char tmp[LEN]; int whichresp; int x_resp; int pos, i, n; struct tm *tm; whichresp = which_response (respnum); x_resp = num_of_responses (which_thread (respnum)); ClearScreen (); tm = localtime (&arts[respnum].date); if (! my_strftime (buf, sizeof (buf), "%a, %d %b %Y %H:%M:%S", tm)) { strcpy (buf, note_h_date); } pos = (cCOLS - (int) strlen (group)) / 2; for (i = strlen(buf); i < pos; i++) { buf[i] = ' '; } buf[i] = '\0'; strcat (buf, group); for (i = strlen(buf); i < RIGHT_POS ; i++) { buf[i] = ' '; } buf[i] = '\0'; sprintf (tmp, txt_thread_x_of_n, buf, which_thread (respnum) + 1, top_base); fputs (tmp, stdout); if (arts[respnum].lines < 0) { strcpy (tmp, "?"); } else { sprintf (tmp, "%d", arts[respnum].lines); } sprintf (buf, txt_lines, tmp); n = strlen (buf); fputs (buf, stdout); if (note_h_subj[0]) { strcpy (buf, note_h_subj); } else { strcpy (buf, arts[respnum].subject); } buf[RIGHT_POS - 5 - n] = '\0'; pos = ((cCOLS - (int) strlen (buf)) / 2) - 2; if (pos > n) { MoveCursor (1, pos); } else { MoveCursor (1, n); } StartInverse (); fputs (buf, stdout); EndInverse (); MoveCursor (1, RIGHT_POS); if (whichresp) printf (txt_resp_x_of_n, whichresp, x_resp); else { if (x_resp == 0) fputs (txt_no_resp, stdout); else if (x_resp == 1) fputs (txt_1_resp, stdout); else printf (txt_x_resp, x_resp); } if (*note_h_org) { if (arts[respnum].name) { sprintf (tmp, txt_s_at_s, arts[respnum].name, note_h_org); } else { strcpy (tmp, note_h_org); } } else if (arts[respnum].name) { strcpy (tmp, arts[respnum].name); } else { strcpy (tmp, " "); } tmp[LEN-1] = '\0'; sprintf (buf, "%s ", arts[respnum].from); pos = cCOLS - 1 - (int) strlen(tmp); if ((int) strlen (buf) + (int) strlen (tmp) >= cCOLS - 1) { strncat (buf, tmp, cCOLS - 1 - (int) strlen(buf)); buf[cCOLS-1] = '\0'; } else { for (i = strlen(buf); i < pos; i++) buf[i] = ' '; buf[i] = '\0'; strcat (buf, tmp); } strip_line (buf, strlen (buf)); printf ("%s\r\n\r\n", buf); note_line += 4; } void show_cont_header (respnum) int respnum; { int maxresp; int whichresp; int whichbase; char buf[LEN]; whichresp = which_response (respnum); whichbase = which_thread (respnum); maxresp = num_of_responses (whichbase); assert (whichbase < top_base); if (whichresp) { sprintf(buf, txt_thread_resp_page, whichbase + 1, top_base, whichresp, maxresp, note_page + 1, note_h_subj); } else { sprintf(buf, txt_thread_page, whichbase + 1, top_base, note_page + 1, note_h_subj); } strip_line (buf, strlen (buf)); if (cCOLS) { buf[cCOLS-1] = '\0'; } printf("%s\r\n\r\n", buf); note_line += 2; } int art_open (art, group_path) long art; char *group_path; { char buf[8192]; char *ptr; note_page = 0; art_close (); /* just in case */ if ((note_fp = open_art_fp (group_path, art)) == (FILE *) 0) { return (ART_UNAVAILABLE); } note_h_path[0] = '\0'; note_h_subj[0] = '\0'; note_h_org[0] = '\0'; note_h_date[0] = '\0'; note_h_newsgroups[0] = '\0'; note_h_messageid[0] = '\0'; note_h_references[0] = '\0'; note_h_distrib[0] = '\0'; note_h_followup[0] = '\0'; note_h_mimeversion[0] = '\0'; note_h_contenttype[0] = '\0'; while (fgets(buf, sizeof buf, note_fp) != NULL) { buf[8191] = '\0'; for (ptr = buf ; *ptr && *ptr != '\n' ; ptr++) { if (((*ptr) & 0xFF) < ' ') *ptr = ' '; } *ptr = '\0'; if (*buf == '\0') break; if (match_header (buf, "Path", note_h_path, LEN)) continue; if (match_header (buf, "Subject", note_h_subj, LEN)) continue; if (match_header (buf, "Organization", note_h_org, PATH_LEN)) continue; if (match_header (buf, "Date", note_h_date, PATH_LEN)) continue; if (match_header (buf, "Newsgroups", note_h_newsgroups, LEN)) continue; if (match_header (buf, "Message-ID", note_h_messageid, PATH_LEN)) continue; if (match_header (buf, "Message-Id", note_h_messageid, PATH_LEN)) continue; if (match_header (buf, "References", note_h_references, PATH_LEN)) continue; if (match_header (buf, "Distribution", note_h_distrib, PATH_LEN)) continue; if (match_header (buf, "Followup-To", note_h_followup, LEN)) continue; if (match_header (buf, "Keywords", note_h_keywords, LEN)) continue; if (match_header (buf, "Summary", note_h_summary, LEN)) continue; if (match_header (buf, "Mime-Version", note_h_mimeversion, PATH_LEN)) continue; if (match_header (buf, "Content-Type", note_h_contenttype, LEN)) { lcase (note_h_contenttype); continue; } } note_mark[0] = ftell (note_fp); note_end = FALSE; /* * If Newsgroups is empty its a good bet the article is a mail article */ if (! note_h_newsgroups[0]) { strcpy (note_h_newsgroups, group_path); while ((ptr = (char *) strchr (note_h_newsgroups, '/'))) { *ptr = '.'; } } return (0); } void art_close () { if (note_fp && note_page != ART_UNAVAILABLE) { fclose (note_fp); note_fp = (FILE *) 0; } } int prompt_response (ch, respnum) int ch; int respnum; { int num; clear_message (); if ((num = prompt_num (ch, txt_read_resp)) == -1) { clear_message (); return -1; } return choose_response (which_thread (respnum), num); } void yank_to_addr (orig, addr) char *orig; char *addr; { char *p; int open_parens; for (p = orig; *p; p++) if (((*p) & 0xFF) < ' ') *p = ' '; while (*addr) addr++; while (*orig) { while (*orig && (*orig == ' ' || *orig == '"' || *orig == ',')) orig++; *addr++ = ' '; while (*orig && (*orig != ' ' && *orig != ',' && *orig != '"')) *addr++ = *orig++; while (*orig && (*orig == ' ' || *orig == '"' || *orig == ',')) orig++; if (*orig == '(') { orig++; open_parens = 1; while (*orig && open_parens) { if (*orig == '(') open_parens++; if (*orig == ')') open_parens--; orig++; } if (*orig == ')') orig++; } } *addr = '\0'; } int show_last_page () { char buf[LEN]; char buf2[LEN+50]; char *p, *q; int ctrl_L; /* form feed character detected */ int i, j; long tmp_pos; if (note_end) { return FALSE; } if (note_size == 0L) { tmp_pos = ftell (note_fp); fseek (note_fp, 0L, 2); /* goto end of article */ note_size = ftell (note_fp); fseek (note_fp, tmp_pos, 0); /* goto old position */ } while (! note_end) { note_line = 1; ctrl_L = FALSE; if (note_page == 0) { note_line += 4; } else { note_line += 2; } while (note_line < cLINES) { if (fgets (buf, sizeof buf, note_fp) == NULL) { note_end = TRUE; break; } buf[LEN-1] = '\0'; for (p = buf, q = buf2; *p && *p != '\n' && q<&buf2[LEN]; p++) { if (*p == '\b' && q > buf2) { q--; } else if (*p == 12) { /* ^L */ *q++ = '^'; *q++ = 'L'; ctrl_L = TRUE; } else if (*p == '\t') { i = q - buf2; j = (i|7) + 1; while (i++ < j) { *q++ = ' '; } } else if (((*p) & 0xFF) < ' ') { *q++ = '^'; *q++ = ((*p) & 0xFF) + '@'; } else { *q++ = *p; } } *q = '\0'; note_line += ((int) strlen (buf2) / cCOLS) + 1; if (ctrl_L) { break; } } if (note_mark[note_page] == note_size) { note_end = TRUE; note_page--; break; } else if (! note_end) { note_mark[++note_page] = ftell(note_fp); } } fseek (note_fp, note_mark[note_page], 0); return TRUE; } int match_header (buf, pat, body, len) char *buf; char *pat; char *body; int len; { int plen = strlen (pat); if(strncmp (buf, pat, plen) == 0 && buf[plen] == ':' && buf[plen + 1] == ' ') { plen += 2; while (buf[plen] == ' ') plen++; strncpy (body, &buf[plen], len); body[len - 1] = '\0'; return TRUE; } return FALSE; } *[SRC.TIN-1_22]PARSDATE.Y;16+,# .-// 4---d0@123KPWO.56ߵ7 ?89IʒG/HJ%{ /* * Project : tin - a Usenet reader * Module : parsedate.y * Author : S.Bellovin & R.$alz & J.Berets & P.Eggert * Created : 01-08-90 * Updated : 04-12-92 * Notes : This grammar has 6 shift/reduce conflicts. * Originally written by Steven M. Bellovin * while at the University of North Carolina at Chapel Hill. * Later tweaked by a couple of people on Usenet. Completely * overhauled by Rich $alz and Jim Berets * in August, 1990. * Further revised (removed obsolete constructs and cleaned up * timezone names) in August, 1991, by Rich. * Paul Eggert helped in September 1992. * Revision : 1.12 * Copyright : This code is in the public domain and has no copyright. */ /* SUPPRESS 530 *//* Empty body for statement */ /* SUPPRESS 593 on yyerrlab *//* Label was not used */ /* SUPPRESS 593 on yynewstate *//* Label was not used */ /* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */ #include "tin.h" /* ** Get the number of elements in a fixed-size array, or a pointer just ** past the end of it. */ #define SIZEOF(array) ((int)(sizeof array / sizeof array[0])) #define ENDOF(array) (&array[SIZEOF(array)]) #define CTYPE(isXXXXX, c) ((isascii((c)) && isXXXXX((c)))) typedef char *STRING; #define yyparse date_parse #define yylex date_lex #define yyerror date_error < /* See the LeapYears table in Convert. */ #define EPOCH 1970 #define END_OF_TIME 2038 /* Constants for general time calculations. */ #define DST_OFFSET 1 #define SECSPERDAY (24L * 60L * 60L) /* Readability for TABLE stuff. */ #define HOUR(x) (x * 60) #define LPAREN '(' #define RPAREN ')' #define IS7BIT(x) ((unsigned int)(x) < 0200) /* ** An entry in the lexical lookup table. */ typedef struct _TABLE { STRING name; int type; time_t value; } TABLE; /* ** Daylight-savings mode: on, off, or not yet known. */ typedef enum _DSTMODE { DSTon, DSToff, DSTmaybe } DSTMODE; /* ** Meridian: am, pm, or 24-hour style. */ typedef enum _MERIDIAN { MERam, MERpm, MER24 } MERIDIAN; /* ** Global variables. We could get rid of most of them by using a yacc ** union, but this is more efficient. (This routine predates the ** yacc %union construct.) */ static char *yyInput; static DSTMODE yyDSTmode; static int yyHaveDate; static int yyHaveRel; static int yyHaveTime; static time_t yyTimezone; static time_t yyDay; static time_t yyHour; static time_t yyMinutes; static time_t yyMonth; static time_t yySeconds; static time_t yyYear; static MERIDIAN yyMeridian; static time_t yyRelMonth; static time_t yyRelSeconds; extern struct tm *localtime(); static void date_error(); %} %union { time_t Number; enum _MERIDIAN Meridian; } %token tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER %token tUNUMBER tZONE %type tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT %type tSNUMBER tUNUMBER tZONE numzone zone %type tMERIDIAN o_merid %% spec : /* NULL */ | spec item ; item : time { yyHaveTime++; #if defined(lint) /* I am compulsive about lint natterings... */ if (yyHaveTime == -1) { YYERROR; } #endif /* defined(lint) */ } | time zone { yyHaveTime++; yyTimezone = $2; } | date { yyHaveDate++; } | rel { yyHaveRel = 1; } ; time : tUNUMBER o_merid { if ($1 < 100) { yyHour = $1; yyMinutes = 0; } else { yyHour = $1 / 100; yyMinutes = $1 % 100; } yySeconds = 0; yyMeridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = 0; yyMeridian = $4; } | tUNUMBER ':' tUNUMBER numzone { yyHour = $1; yyMinutes = $3; yyTimezone = $4; yyMeridian = MER24; yyDSTmode = DSToff; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = $6; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyTimezone = $6; yyMeridian = MER24; yyDSTmode = DSToff; } ; zone : tZONE { $$ = $1; yyDSTmode = DSToff; } | tDAYZONE { $$ = $1; yyDSTmode = DSTon; } | tZONE numzone { /* Only allow "GMT+300" and "GMT-0800" */ if ($1 != 0) { YYABORT; } $$ = $2; yyDSTmode = DSToff; } | numzone { $$ = $1; yyDSTmode = DSToff; } ; numzone : tSNUMBER { int i; signed long t; t = ((signed) $1); /* Unix and GMT and numeric timezones -- a little confusing. */ if (t < 0) { /* Don't work with negative modulus. */ ((signed)$1) = -t; if (t > 9999 || (i = t % 100) >= 60) { YYABORT; } $$ = (t / 100) * 60 + i; } else { if ((t > 9999) || (i = (t % 100)) >= 60) { YYABORT; } $$ = -((t / 100) * 60 + i); } } ; date : tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { if ($1 > 100) { yyYear = $1; yyMonth = $3; yyDay = $5; } else { yyMonth = $1; yyDay = $3; yyYear = $5; } } | tMONTH tUNUMBER { yyMonth = $1; yyDay = $2; } | tMONTH tUNUMBER ',' tUNUMBER { yyMonth = $1; yyDay = $2; yyYear = $4; } | tUNUMBER tMONTH { yyDay = $1; yyMonth = $2; } | tUNUMBER tMONTH tUNUMBER { yyDay = $1; yyMonth = $2; yyYear = $3; } | tDAY ',' tUNUMBER tMONTH tUNUMBER { yyDay = $3; yyMonth = $4; yyYear = $5; } ; rel : tSNUMBER tSEC_UNIT { yyRelSeconds += $1 * $2; } | tUNUMBER tSEC_UNIT { yyRelSeconds += $1 * $2; } | tSNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tUNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } ; o_merid : /* NULL */ { $$ = MER24; } | tMERIDIAN { $$ = $1; } ; %% /* Month and day table. */ static TABLE MonthDayTable[] = { { "january", tMONTH, 1 }, { "february", tMONTH, 2 }, { "march", tMONTH, 3 }, { "april", tMONTH, 4 }, { "may", tMONTH, 5 }, { "june", tMONTH, 6 }, { "july", tMONTH, 7 }, { "august", tMONTH, 8 }, { "september", tMONTH, 9 }, { "october", tMONTH, 10 }, { "november", tMONTH, 11 }, { "december", tMONTH, 12 }, /* The value of the day isn't used... */ { "sunday", tDAY, 0 }, { "monday", tDAY, 0 }, { "tuesday", tDAY, 0 }, { "wednesday", tDAY, 0 }, { "thursday", tDAY, 0 }, { "friday", tDAY, 0 }, { "saturday", tDAY, 0 }, }; /* Time units table. */ static TABLE UnitsTable[] = { { "year", tMONTH_UNIT, 12 }, { "month", tMONTH_UNIT, 1 }, #ifdef QNX4 { "week", tSEC_UNIT, 604800 }, { "day", tSEC_UNIT, 86400 }, #else { "week", tSEC_UNIT, 7 * 24 * 60 * 60 }, { "day", tSEC_UNIT, 1 * 24 * 60 * 60 }, #endif { "hour", tSEC_UNIT, 60 * 60 }, { "minute", tSEC_UNIT, 60 }, { "min", tSEC_UNIT, 60 }, { "second", tSEC_UNIT, 1 }, { "sec", tSEC_UNIT, 1 }, }; /* Timezone table. */ static TABLE TimezoneTable[] = { { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ { "ut", tZONE, HOUR( 0) }, /* Universal */ { "utc", tZONE, HOUR( 0) }, /* Universal Coordinated */ { "cut", tZONE, HOUR( 0) }, /* Coordinated Universal */ { "z", tZONE, HOUR( 0) }, /* Greenwich Mean */ { "wet", tZONE, HOUR( 0) }, /* Western European */ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ { "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */ { "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ { "cst", tZONE, HOUR( 6) }, /* Central Standard */ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ { "akst", tZONE, HOUR( 9) }, /* Alaska Standard */ { "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ { "hast", tZONE, HOUR(10) }, /* Hawaii-Aleutian Standard */ { "hadt", tDAYZONE, HOUR(10) }, /* Hawaii-Aleutian Daylight */ { "ces", tDAYZONE, -HOUR(1) }, /* Central European Summer */ { "cest", tDAYZONE, -HOUR(1) }, /* Central European Summer */ { "mez", tZONE, -HOUR(1) }, /* Middle European */ { "mezt", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ { "cet", tZONE, -HOUR(1) }, /* Central European */ { "met", tZONE, -HOUR(1) }, /* Middle European */ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe */ { "msk", tZONE, -HOUR(3) }, /* Moscow Winter */ { "msd", tDAYZONE, -HOUR(3) }, /* Moscow Summer */ { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */ { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */ { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */ { "cct", tZONE, -HOUR(8) }, /* China Coast */ { "jst", tZONE, -HOUR(9) }, /* Japan Standard */ { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ { "kdt", tZONE, -HOUR(9) }, /* Korean Daylight */ { "cast", tZONE, -(HOUR(9)+30) }, /* Central Australian Standard */ { "cadt", tDAYZONE, -(HOUR(9)+30) }, /* Central Australian Daylight */ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ /* For completeness we include the following entries. */ #if 0 /* Duplicate names. Either they conflict with a zone listed above * (which is either more likely to be seen or just been in circulation * longer), or they conflict with another zone in this section and * we could not reasonably choose one over the other. */ { "fst", tZONE, HOUR( 2) }, /* Fernando De Noronha Standard */ { "fdt", tDAYZONE, HOUR( 2) }, /* Fernando De Noronha Daylight */ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ { "est", tZONE, HOUR( 3) }, /* Eastern Standard (Brazil) */ { "edt", tDAYZONE, HOUR( 3) }, /* Eastern Daylight (Brazil) */ { "wst", tZONE, HOUR( 4) }, /* Western Standard (Brazil) */ { "wdt", tDAYZONE, HOUR( 4) }, /* Western Daylight (Brazil) */ { "cst", tZONE, HOUR( 5) }, /* Chile Standard */ { "cdt", tDAYZONE, HOUR( 5) }, /* Chile Daylight */ { "ast", tZONE, HOUR( 5) }, /* Acre Standard */ { "adt", tDAYZONE, HOUR( 5) }, /* Acre Daylight */ { "cst", tZONE, HOUR( 5) }, /* Cuba Standard */ { "cdt", tDAYZONE, HOUR( 5) }, /* Cuba Daylight */ { "est", tZONE, HOUR( 6) }, /* Easter Island Standard */ { "edt", tDAYZONE, HOUR( 6) }, /* Easter Island Daylight */ { "sst", tZONE, HOUR(11) }, /* Samoa Standard */ { "ist", tZONE, -HOUR(2) }, /* Israel Standard */ { "idt", tDAYZONE, -HOUR(2) }, /* Israel Daylight */ { "idt", tDAYZONE, -(HOUR(3)+30) }, /* Iran Daylight */ { "ist", tZONE, -(HOUR(3)+30) }, /* Iran Standard */ { "cst", tZONE, -HOUR(8) }, /* China Standard */ { "cdt", tDAYZONE, -HOUR(8) }, /* China Daylight */ { "sst", tZONE, -HOUR(8) }, /* Singapore Standard */ /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ { "wat", tZONE, -HOUR(1) }, /* West Africa */ { "at", tZONE, HOUR( 2) }, /* Azores */ { "gst", tZONE, -HOUR(10) }, /* Guam Standard */ { "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ { "fwt", tZONE, -HOUR(1) }, /* French Winter */ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ { "bt", tZONE, -HOUR(3) }, /* Baghdad */ { "it", tZONE, -(HOUR(3)+30) }, /* Iran */ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ { "ist", tZONE, -(HOUR(5)+30) }, /* Indian Standard */ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ { "nst", tZONE, -HOUR(7) }, /* North Sumatra */ { "sst", tZONE, -HOUR(7) }, /* South Sumatra */ { "jt", tZONE, -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ { "cat", tZONE, HOUR(10) }, /* -- expired 1967 */ { "nt", tZONE, HOUR(11) }, /* -- expired 1967 */ { "ahst", tZONE, HOUR(10) }, /* -- expired 1983 */ { "hdt", tDAYZONE, HOUR(10) }, /* -- expired 1986 */ #endif /* 0 */ }; /* ARGSUSED */ static void date_error(s) char *s; { /* NOTREACHED */ } static int ToSeconds(Hours, Minutes, Seconds, Meridian) int Hours; int Minutes; int Seconds; MERIDIAN Meridian; { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61) return -1; if (Meridian == MER24) { if (Hours < 0 || Hours > 23) return -1; } else { if (Hours < 1 || Hours > 12) return -1; if (Meridian == MERpm) Hours += 12; } return (Hours * 60L + Minutes) * 60L + Seconds; } static int Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, dst) int Month; int Day; int Year; int Hours; int Minutes; int Seconds; MERIDIAN Meridian; DSTMODE dst; { static int DaysNormal[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static int DaysLeap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static int LeapYears[] = { 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036 }; register int *yp; register int *mp; register time_t Julian; register int i; time_t tod; if (Year < 0) Year = -Year; if (Year < 100) Year += 1900; if (Year < EPOCH) Year += 100; for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++) if (Year == *yp) { mp = DaysLeap; break; } if (Year < EPOCH || Year > END_OF_TIME || Month < 1 || Month > 12 /* NOSTRICT *//* conversion from long may lose accuracy */ || Day < 1 || Day > mp[(int)Month]) return -1; Julian = Day - 1 + (Year - EPOCH) * 365; for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++) if (Year <= *yp) break; for (i = 1; i < Month; i++) Julian += *++mp; Julian *= SECSPERDAY; Julian += yyTimezone * 60L; if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) return -1; Julian += tod; tod = Julian; if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst)) Julian -= DST_OFFSET * 60 * 60; return Julian; } static time_t DSTcorrect(Start, Future) time_t Start; time_t Future; { time_t StartDay; time_t FutureDay; StartDay = (localtime(&Start)->tm_hour + 1) % 24; FutureDay = (localtime(&Future)->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60; } static time_t RelativeMonth(Start, RelMonth) time_t Start; time_t RelMonth; { struct tm *tm; time_t Month; time_t Year; tm = localtime(&Start); Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; return DSTcorrect(Start, Convert(Month, (time_t)tm->tm_mday, Year, (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, MER24, DSTmaybe)); } static int LookupWord(buff, length) char *buff; register int length; { register char *p; register STRING q; register TABLE *tp; register int c; p = buff; c = p[0]; /* See if we have an abbreviation for a month. */ if (length == 3 || (length == 4 && p[3] == '.')) for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) { q = tp->name; if (c == q[0] && p[1] == q[1] && p[2] == q[2]) { yylval.Number "= tp->value; return tp->type; } } else for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Try for a timezone. */ for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) if (c == tp->name[0] && p[1] == tp->name[1] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Try the units table. */ for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Strip off any plural and try the units table again. */ if (--length > 0 && p[length] == 's') { p[length] = '\0'; for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { p[length] = 's'; yylval.Number = tp->value; return tp->type; } p[length] = 's'; } length++; /* Drop out any periods. */ for (p = buff, q = (STRING)buff; *q; q++) if (*q != '.') *p++ = *q; *p = '\0'; /* Try the meridians. */ if (buff[1] == 'm' && buff[2] == '\0') { if (buff[0] == 'a') { yylval.Meridian = MERam; return tMERIDIAN; } if (buff[0] == 'p') { yylval.Meridian = MERpm; return tMERIDIAN; } } /* If we saw any periods, try the timezones again. */ if (p - buff != length) { c = buff[0]; for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) % if (c == tp->name[0] && p[1] == tp->name[1] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } /* Unknown word -- assume GMT timezone. */ yylval.Number = 0; return tZONE; } static int date_lex() { register char c; register char *p; char buff[20]; register int sign; register int i; register int nesting; for ( ; ; ) { /* Get first character after the whitespace. */ for ( ; ; ) { while (CTYPE(isspace, *yyInput)) yyInput++; c = *yyInput; /* Ignore RFC 822 comments, typically time zone names. */ if (c != LPAREN) break; for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; ) if (c == LPAREN) nesting++; else if (!IS7BIT(c) || c == '\0' || c == '\r' || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c)))) /* Lexical error: bad comment. */ return '?'; yyInput++; } /* A number? */ if (CTYPE(isdigit, c) || c == '-' || c == '+') { if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; yyInput++; if (!CTYPE(isdigit, *yyInput)) /* Skip the plus or minus sign. */ continue; } else sign = 0; for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, c); ) i = 10 * i + c - '0'; yyInput--; yylval.Number = sign < 0 ? -i : i; return sign ? tSNUMBER : tUNUMBER; } /* A word? */ if (CTYPE(isalpha, c)) { for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, c); ) if (p < &buff[sizeof buff - 1]) *p++ = CTYPE(isupper, c) ? tolower(c) : c; *p = '\0'; yyInput--; return LookupWord(buff, p - buff); } return *yyInput++; } } int GetTimeInfo(Now) TIMEINFO *Now; { static time_t LastTime; static long LastTzone; struct tm *tm; #if defined(DO_HAVE_GETTIMEOFDAY) struct timeval tv; #endif /* defined(DO_HAVE_GETTIMEOFDAY) */ #if defined(DONT_HAVE_TM_GMTOFF) struct tm local; struct tm gmt; #endif /* !defined(DONT_HAVE_TM_GMTOFF) */ /* Get the basic time. */ #if defined(DO_HAVE_GETTIMEOFDAY) if (gettimeofday(&tv, (struct timezone *)NULL) == -1) return -1; Now->time = tv.tv_sec; Now->usec = tv.tv_usec; #else /* Can't check for -1 since that might be a time, I guess. */ (void)time(&Now->time); Now->usec = 0; #endif /* defined(DO_HAVE_GETTIMEOFDAY) */ /* Now get the timezone if it's been an hour since the last time. */ if (Now->time - LastTime > 60 * 60) { LastTime = Now->time; if ((tm = localtime(&Now->time)) == NULL~ TIN-1_22.BCK# dRC.TIN-1_22]PARSDATE.Y;16-<&*) return -1; #if defined(DONT_HAVE_TM_GMTOFF) /* To get the timezone, compare localtime with GMT. */ local = *tm; if ((tm = gmtime(&Now->time)) == NULL) return -1; gmt = *tm; /* Assume we are never more than 24 hours away. */ LastTzone = gmt.tm_yday - local.tm_yday; if (LastTzone > 1) LastTzone = -24; else if (LastTzone < -1) LastTzone = 24; else LastTzone *= 24; /* Scale in the hours and minutes; ignore seconds. */ LastTzone += gmt.tm_hour - local.tm_hour; LastTzone *= 60; LastTzone += gmt.tm_min - local.tm_min; #else LastTzone = (0 - tm->tm_gmtoff) / 60; #endif /* defined(DONT_HAVE_TM_GMTOFF) */ } Now->tzone = LastTzone; return 0; } time_t parsedate(p, now) char *p; TIMEINFO *now; { extern int date_parse(); struct tm *tm; TIMEINFO ti; int Start; yyInput = p; if (now == NULL) { now = &ti; (void)GetTimeInfo(&ti); } tm = localtime(&now->time); yyYear = tm->tm_year; yyMonth = tm->tm_mon + 1; yyDay = tm->tm_mday; yyTimezone = now->tzone; yyDSTmode = DSTmaybe; yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; yyRelSeconds = 0; yyRelMonth = 0; yyHaveDate = 0; yyHaveRel = 0; yyHaveTime = 0; if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1) return -1; if (yyHaveDate || yyHaveTime) { Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, yyMeridian, yyDSTmode); if (Start < 0) return -1; } else { Start = now->time; if (!yyHaveRel) Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec; } Start += yyRelSeconds; if (yyRelMonth) Start += RelativeMonth(Start, yyRelMonth); /* Have to do *something* with a legitimate -1 so it's distinguishable * from the error return value. (Alternately could set errno on error.) */ return Start == -1 ? 0 : Start; } e*[SRC.TIN-1_22]PATCHLEV.H;2+,_.// 4-d0@123KPWO569v7 ǘ89]VG/HJ/* * Project : tin - a Usenet reader * Module : patchlev.h * Author : I.Lea * Created : 01-04-91 * Updated : 14-07-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #define VERSION "1.2" /* Beta versions are "1.n Beta" */ #define PATCHLEVEL "2" #ifdef M_AMIGA # define OS "[AMIGA]" #endif #ifdef M_OS2 # define OS "[OS/2]" #endif #ifdef M_UNIX # define OS "[UNIX]" #endif #ifdef WIN32 # define OS "[WIN/NT]" #endif #ifdef VMS # define OS "[VAX/VMS]" #endif *[SRC.TIN-1_22]POST.C;7+,M.Q// 4QQ-d0@123KPWOR5637N89]VG/HJn/* * Project : tin - a Usenet reader * Module : post.c * Author : I.Lea * Created : 01-04-91 * Updated : 24-09-93 * Notes : mail/post/replyto/followup/crosspost & cancel articles * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #define PRINT_LF() Raw (FALSE); fputc ('\n', stdout); fflush (stdout); Raw (TRUE); extern char note_h_distrib[PATH_LEN]; /* Distribution: */ extern char note_h_followup[LEN]; /* Followup-To: */ extern char note_h_messageid[PATH_LEN]; /* Message-ID: */ extern char note_h_references[PATH_LEN]; /* References: */ extern char note_h_newsgroups[LEN]; /* Newsgroups: */ extern char note_h_subj[LEN]; /* Subject: */ extern char note_h_date[PATH_LEN]; /* Date: */ extern FILE *note_fp; /* the body of the current article */ extern long note_mark[MAX_PAGES]; /* ftells on beginnings of pages */ int unlink_article = TRUE; static int reread_active_for_posted_arts = FALSE; struct t_posted *posted; int user_posted_messages () { char buf[LEN]; FILE *fp; int i, j, k; int no_of_lines = 0; if ((fp = fopen (postfile, "r")) == NULL) { clear_message (); return FALSE; } else { while (fgets (buf, sizeof (buf), fp) != NULL) { no_of_lines++; } if (! no_of_lines) { fclose (fp); info_message (txt_no_arts_posted); return FALSE; } rewind (fp); posted = (struct t_posted *) my_malloc ((unsigned) (no_of_lines+1) * sizeof (struct t_posted)); for (i=0 ; fgets (buf, sizeof (buf), fp) != NULL ; i++) { for (j=0 ; buf[j] != '|' && buf[j] != '\n' ; j++) { posted[i].date[j] = buf[j]; /* posted date */ } if (buf[j] == '\n') { error_message ("Corrupted file %s", postfile); sleep (1); fclose (fp); clear_message (); return FALSE; } posted[i].date[j++] = '\0'; posted[i].action = buf[j]; j += 2; for (k=j,j=0 ; buf[k] != '|' && buf[k] != ',' ; k++, j++) { if (j < sizeof (posted[i].group)) { posted[i].group[j] = buf[k]; } } if (buf[k] == ',') { while (buf[k] != '|' && buf[k] != '\n') { k++; } posted[i].group[j++] = ','; posted[i].group[j++] = '.'; posted[i].group[j++] = '.'; posted[i].group[j++] = '.'; } posted[i].group[j++] = '\0'; k++; for (j=k,k=0 ; buf[j] != '\n' ; j++, k++) { if (k < sizeof (posted[i].subj)) { posted[i].subj[k] = buf[j]; } } posted[i].subj[k++] = '\0'; } fclose (fp); show_info_page (POST_INFO, (char **) 0, txt_post_history_menu); if (posted != (struct t_posted *) 0) { free ((char *) posted); posted = (struct t_posted *) 0; } return TRUE; } } void update_art_posted_file (group, action, subj) char *group; int action; char *subj; { char buf[LEN]; char tmp_post[LEN]; FILE *fp, *tmp_fp; long epoch; struct tm *tm; sprintf (tmp_post, "%s.%d", postfile, process_id); #ifdef VMS if ((tmp_fp = fopen (tmp_post, "w", "fop=cif")) != NULL) { #else if ((tmp_fp = fopen (tmp_post, "w")) != NULL) { #endif time (&epoch); tm = localtime (&epoch); fprintf (tmp_fp, "%02d-%02d-%02d|%c|%s|%s\n", tm->tm_mday, tm->tm_mon+1, tm->tm_year, action, group, subj); fclose (tmp_fp); } if ((tmp_fp = fopen (tmp_post, "a+")) != NULL) { if ((fp = fopen (postfile, "r")) != NULL) { while (fgets (buf, sizeof buf, fp) != NULL) { fprintf (tmp_fp, "%s", buf); } fclose (fp); rename_file (tmp_post, postfile); } fclose (tmp_fp); } } /* * Check the article file for correct header syntax and if there * is a blank between the header information and the text. * * 1. Subject header present * 2. Newsgroups header present * 3. Space after every colon in header * 4. Colon in every header line * 5. Newsgroups line has no spaces, only comma separated * 6. List of newsgroups is presented to user with description * 7. Lines in body that are to long causes a warning to be printed * 8. Group(s) must be listed in the active file * 9. No From: header allowed (limit accidental forging) and * rejection by inn servers */ int check_article_to_be_posted (article) char *article; { char *ngptrs[NGLIMIT]; char line[LEN], *cp, *cp2; FILE *fp; int cnt = 0; int col, len, i, n; int end_of_header = FALSE; int errors = 0; int found_newsgroups_line = FALSE; int found_subject_line = FALSE; int init = TRUE; int ngcnt = 0; int nglens[NGLIMIT]; int oldraw; /* save previous raw state */ if ((fp = fopen (article, "r")) == (FILE *) 0) { perror_message (txt_cannot_open, article); return FALSE; } oldraw = RawState(); /* save state */ while (fgets (line, sizeof (line), fp) != NULL) { cnt++; len= strlen (line); if (len > 0) { if (line[len - 1] == '\n') { line[--len]= 0; } } if ((cnt == 1) && (len == 0)) { setup_check_article_screen (&init); fprintf (stderr, txt_error_header_line_blank); fflush (stderr); errors++; end_of_header = TRUE; break; } if ((len == 0) && (cnt >= 2)) { end_of_header = TRUE; break; } /* * ignore continuation lines - they start with white space */ if ((line[0] == ' ' || line[0] == '\t') && (cnt != 1)) { continue; } cp = strchr (line, ':'); if (cp == (char *) 0) { setup_check_article_screen (&init); fprintf (stderr, txt_error_header_line_colon, cnt, line); fflush (stderr); errors++; continue; } if (cp[1] != ' ') { setup_check_article_screen (&init); fprintf (stderr, txt_error_header_line_space, cnt, line); fflush (stderr); errors++; } if (cp - line == 7 && ! strncmp (line, "Subject", 7)) { found_subject_line = TRUE; } if (cp - line == 4 && ! strncmp (line, "From", 4)) { fprintf (stderr, txt_error_from_in_header_not_allowed, cnt); fflush (stderr); errors++; } if (cp - line == 10 && ! strncmp (line, "Newsgroups", 10)) { found_newsgroups_line = TRUE; for (cp = line + 11; *cp == ' '; cp++) { ; } if (strchr (cp, ' ')) { setup_check_article_screen (&init); fprintf (stderr, txt_error_header_line_comma); fflush (stderr); errors++; continue; } while (*cp) { if (! (cp2 = strchr (cp, ','))) { cp2 = cp + strlen (cp); } else { *cp2++ = '\0'; } if (ngcnt < NGLIMIT) { nglens[ngcnt] = strlen (cp); ngptrs[ngcnt] = my_malloc (nglens[ngcnt]+1); if (! ngptrs[ngcnt]) { Raw (oldraw); return (TRUE); } strcpy (ngptrs[ngcnt], cp); ngcnt++; } cp = cp2; } if (! ngcnt) { setup_check_article_screen (&init); fprintf (stderr, txt_error_header_line_empty_newsgroups); fflush (stderr); errors++; continue; } } } if (! found_subject_line) { setup_check_article_screen (&init); fprintf (stderr, txt_error_header_line_missing_subject); fflush (stderr); errors++; } if (! found_newsgroups_line) { setup_check_article_screen (&init); fprintf (stderr, txt_error_header_line_missing_newsgroups); fflush (stderr); errors++; } /* * Check the body of the article for long lines */ while (fgets (line, sizeof (line), fp)) { cnt++; cp = strrchr (line, '\n'); if (cp != (char *) 0) { *cp = '\0'; } col = 0; for (cp = line; *cp; cp++) { if (*cp == '\t') { col += 8 - (col%8); } else { col++; } } if (col > MAX_COL) { setup_check_article_screen (&init); fprintf (stderr, txt_warn_art_line_too_long, MAX_COL, cnt, line); fflush (stderr); break; } } if (! end_of_header) { setup_check_article_screen (&init); fprintf (stderr, txt_error_header_and_body_not_seperate); fflush (stderr); errors++; } if (ngcnt && errors == 0) { /* * Print a note about each newsgroup */ setup_check_article_screen (&init); fprintf (stderr, txt_art_newsgroups, ngcnt == 1? "" : "s"); fflush (stderr); for (i = 0; i < ngcnt; i++) { n = find_group_index (ngptrs[i]); if (n != -1) { fprintf (stderr, " %s\t%s\n", ngptrs[i], (active[n].description ? active[n].description : "")); fflush (stderr); } else { #ifdef HAVE_FACIST_NEWSADMIN fprintf (stderr, txt_error_not_valid_newsgroup, ngptrs[i]); errors++; #else fprintf (stderr, txt_warn_not_valid_newsgroup, ngptrs[i]); #endif fflush (stderr); } free (ngptrs[i]); } } fclose (fp); Raw (oldraw); /* restore raw/unraw state */ return (errors ? FALSE : TRUE); } void setup_check_article_screen (init) int *init; { if (*init) { ClearScreen (); center_line (0, TRUE, txt_check_article); MoveCursor (INDEX_TOP, 0); Raw(FALSE); *init = FALSE; } } /* * Quick post an article (not a followup) */ void quick_post_article () { FILE *fp; char ch, *ptr; char ch_default = 'p'; char group[PATH_LEN]; char subj[PATH_LEN]; char buf[LEN], tmp[LEN]; int i, done = FALSE; if (! can_post) { info_message (txt_cannot_post); return; } /* * Don't allow if not active news feed */ if (! spooldir_is_active) { info_message (txt_not_active_newsfeed); return; } setup_screen (); InitScreen(); /* * Get groupname & subject for posting article. * If multiple newsgroups test all to see if any are moderated. */ sprintf (buf, txt_post_newsgroups, default_post_newsgroups); if (! prompt_string (buf, group)) { fprintf (stderr, "%s\n", txt_no_quick_newsgroups); return; } if (strlen (group)) { my_strncpy (default_post_newsgroups, group, sizeof (default_post_newsgroups)); } else { if (default_post_newsgroups[0]) { my_strncpy (group, default_post_newsgroups, sizeof (group)); } else { fprintf (stderr, "%s\n", txt_no_quick_newsgroups); return; } } /* * Check if any of the newsgroups are moderated. */ strcpy (tmp, group); while (! done) { strcpy (buf, tmp); ptr = buf; ptr = strchr(buf, ','); if (ptr != (char *) 0) { strcpy (tmp, ptr+1); *ptr = '\0'; } else { done = TRUE; } i = find_group_index (buf); if (debug == 2) { sprintf (msg, "Group=[%s] index=[%d]", buf, i); wait_message (msg); } if (i == -1) { Raw(FALSE); fprintf (stderr, "\nGroup %s not found in active file. Exiting...\n", buf); return; } if (active[i].moderated == 'm') { sprintf (msg, txt_group_is_moderated, buf); if (! prompt_yn (cLINES, msg, 'y')) { Raw(FALSE); fprintf (stderr, "\nExiting...\n"); return; } } } PRINT_LF(); sprintf (buf, txt_post_subject, default_post_subject); if (! prompt_string (buf, subj)) { Raw (FALSE); fprintf (stderr, "%s\n", txt_no_quick_subject); return; } if (strlen (subj)) { my_strncpy (default_post_subject, subj, sizeof (default_post_subject)); } else { if (default_post_subject[0]) { my_strncpy (subj, default_post_subject, sizeof (subj)); } else { Raw (FALSE); fprintf (stderr, "%s\n", txt_no_quick_subject); return; } } PRINT_LF(); start_line_offset = 6; #ifdef VMS if ((fp = fopen (article, "w", "fop=cif")) == NULL) { #else if ((fp = fopen (article, "w")) == NULL) { #endif Raw (FALSE); perror_message (txt_cannot_open, article); return; } chmod (article, 0600); /* FIXME so that group only contains 1 group when finding an index number */ i = find_group_index (group); fprintf (fp, "Subject: %s\n", subj); fprintf (fp, "Newsgroups: %s\n", group); if (i >= 0 && active[i].attribute.organization != (char *) 0) { fprintf (fp, "Organization: %s\n", active[i].attribute.organization); start_line_offset++; } if (*reply_to) { fprintf (fp, "Reply-To: %s\n", reply_to); start_line_offset++; } if (i >= 0 && active[i].attribute.followup_to != (char *) 0) { fprintf (fp, "Followup-To: %s\n", active[i].attribute.followup_to); start_line_offset++; } if (*my_distribution) { fprintf (fp, "Distribution: %s\n", my_distribution); start_line_offset++; } fprintf (fp, "Summary: \n"); fprintf (fp, "Keywords: \n\n\n"); add_signature (fp, FALSE); fclose (fp); ch = 'e'; while (1) { switch (ch) { case 'e': invoke_editor (article, start_line_offset); while (! check_article_to_be_posted (article)) { do { sprintf (msg, "%s%c", txt_bad_article, 'e'); wait_message (msg); MoveCursor (cLINES, (int) strlen (txt_bad_article)); if ((ch = (char) ReadCh ()) == CR) ch = 'e'; } while (! strchr ("eq\033", ch)); if (ch == 'e') { invoke_editor (article, start_line_offset); } else { break; } } if (ch == 'e') { break; } case 'q': case ESC: if (unlink_article) unlink (article); clear_message (); return; case 'i': invoke_ispell (article); break; case 'p': wait_message (txt_posting); if (submit_file (article)) { Raw (FALSE); info_message (txt_art_posted); reread_active_for_posted_arts = TRUE; goto post_article_done; } else { rename_file (article, dead_article); Raw (FALSE); error_message (txt_art_rejected, dead_article); return; } } do { sprintf (msg, "%s%c", txt_quit_edit_post, ch_default); wait_message (msg); MoveCursor (cLINES, (int) strlen (txt_quit_edit_post)); if ((ch = (char) ReadCh ()) == CR) ch = ch_default; } while (! strchr ("eipq\033", ch)); } post_article_done: find_mail_header (HEADER_NEWSGROUPS, article, group); find_mail_header (HEADER_SUBJECT, article, subj); if (unlink_article) { unlink (article); } update_art_posted_file (group, 'w', subj); my_strncpy (default_post_newsgroups, group, sizeof (default_post_newsgroups)); my_strncpy (default_post_subject, subj, sizeof (default_post_subject)); write_rcfile (); return; } /* * Post an original article (not a followup) */ int post_article (group, posted) char *group; int *posted; { FILE *fp; char ch; char ch_default = 'p'; char subj[LEN]; char buf[LEN]; int i, redraw_screen = FALSE; if (! can_post) { info_message (txt_cannot_post); return (redraw_screen); } /* * Don't allow if not active news feed */ if (! spooldir_is_active) { info_message (txt_not_active_newsfeed); return (redraw_screen); } *posted = FALSE; start_line_offset = 6; if (active[my_group[cur_groupnum]].moderated == 'm') { sprintf (msg, txt_group_is_moderated, group); if (! prompt_yn (cLINES, msg, 'y')) { clear_message (); return (redraw_screen); } } sprintf (msg, txt_post_subject, default_post_subject); if (! prompt_string (msg, subj)) { clear_message (); return (redraw_screen); } if (strlen (subj)) { my_strncpy (default_post_subject, subj, sizeof (default_post_subject)); } else { if (default_post_subject[0]) { my_strncpy (subj, default_post_subject, sizeof (subj)); } else { info_message (txt_no_subject); return (redraw_screen); } } wait_message (txt_post_an_article); #ifdef VMS if ((fp = fopen (article, "w", "fop=cif")) == NULL) { #else if ((fp = fopen (article, "w")) == NULL) { #endif perror_message (txt_cannot_open, article); return (redraw_screen); } chmod (article, 0600); i = find_group_index (group); fprintf (fp, "Subject: %s\n", subj); fprintf (fp, "Newsgroups: %s\n", group); if (i >= 0 && active[i].attribute.organization != (char *) 0) { fprintf (fp, "Organization: %s\n", active[i].attribute.organization); start_line_offset++; } if (*reply_to) { fprintf (fp, "Reply-To: %s\n", reply_to); start_line_offset++; } if (i >= 0 && active[i].attribute.followup_to != (char *) 0) { fprintf (fp, "Followup-To: %s\n", active[i].attribute.followup_to); start_line_offset++; } if (*my_distribution) { fprintf (fp, "Distribution: %s\n", my_distribution); start_line_offset++; } fprintf (fp, "Summary: \n"); fprintf (fp, "Keywords: \n\n\n"); add_signature (fp, FALSE); fclose (fp); ch = 'e'; while (1) { switch (ch) { case 'e': invoke_editor (article, start_line_offset); while (! check_article_to_be_posted (article)) { do { sprintf (msg, "%s%c", txt_bad_article, 'e'); wait_message (msg); MoveCursor (cLINES, (int) strlen (txt_bad_article)); if ((ch = (char) ReadCh ()) == CR) ch = 'e'; } while (! strchr ("eq\033", ch)); if (ch == 'e') { invoke_editor (article, start_line_offset); } else { break; } } redraw_screen = TRUE; if (ch == 'e') { break; } case 'q': case ESC: if (unlink_article) unlink (article); clear_message (); return (redraw_screen); case 'i': invoke_ispell (article); break; case 'p': wait_message (txt_posting); if (submit_file (article)) { info_message (txt_art_posted); *posted = TRUE; reread_active_for_posted_arts = TRUE; goto post_article_done; } else { rename_file (article, dead_article); sprintf (buf, txt_art_rejected, dead_article); info_message (buf); sleep (3); return (redraw_screen); } } do { sprintf (msg, "%s%c", txt_quit_edit_post, ch_default); wait_message (msg); MoveCursor (cLINES, (int) strlen (txt_quit_edit_post)); if ((ch = (char) ReadCh ()) == CR) ch = ch_default; } while (! strchr ("eipq\033", ch)); } post_article_done: find_mail_header (HEADER_SUBJECT, article, subj); if (unlink_article) { unlink (article); } update_art_posted_file (group, 'w', subj); my_strncpy (default_post_newsgroups, group, sizeof (default_post_newsgroups)); my_strncpy (default_post_subject, subj, sizeof (default_post_subject)); return (redraw_screen); } int post_response (group, respnum, copy_text) char *group; int respnum; int copy_text; { FILE *fp; char ch, *ptr; char ch_default = 'p'; char buf[LEN]; int i; int ret_code = POSTED_NONE; /* * Don't allow if not active news feed */ if (! spooldir_is_active) { info_message (txt_not_active_newsfeed); return (ret_code); } start_line_offset = 4; wait_message (txt_post_a_followup); if (*note_h_followup && strcmp (note_h_followup, "poster") == 0) { clear_message (); if (! prompt_yn (cLINES, txt_resp_to_poster, 'y')) { return (ret_code); } *note_h_followup = '\0'; find_reply_to_addr (respnum, buf); mail_to_someone (respnum, buf, TRUE, FALSE, &ret_code); return (ret_code); } else if (*note_h_followup && strcmp (note_h_followup, group) != 0) { MoveCursor (cLINES/2, 0); CleartoEOS (); center_line ((cLINES/2)+2, TRUE, txt_resp_redirect); MoveCursor ((cLINES/2)+4, 0); fputs (" ", stdout); ptr = note_h_followup; while (*ptr) { if (*ptr != ',') { fputc (*ptr, stdout); } else { fputs ("\r\n ", stdout); } ptr++; } fflush (stdout); if (! prompt_yn (cLINES, txt_continue, 'y')) { return (ret_code); } } #ifdef VMS if ((fp = fopen (article, "w", "fop=cif")) == NULL) { #else if ((fp = fopen (article, "w")) == NULL) { #endif perror_message (txt_cannot_open, article); return (ret_code); } chmod (article, 0600); i = find_group_index (group); fprintf (fp, "Subject: Re: %s\n", eat_re (note_h_subj)); if (*note_h_followup && strcmp (note_h_followup, "poster") != 0) { fprintf (fp, "Newsgroups: %s\n", note_h_followup); } else { fprintf (fp, "Newsgroups: %s\n", note_h_newsgroups); if (i >= 0 && active[i].attribute.followup_to != (char *) 0) { fprintf (fp, "Followup-To: %s\n", active[i].attribute.followup_to); start_line_offset++; } else { ptr = (char *) strchr (note_h_newsgroups, ','); if (ptr) { fprintf (fp, "Followup-To: %s\n", note_h_newsgroups); start_line_offset++; } } } /* * Append to References: line if its already there */ if (note_h_references[0]) { fprintf (fp, "References: %s %s\n", note_h_references, note_h_messageid); } else { fprintf (fp, "References: %s\n", note_h_messageid); } if (i >= 0 && active[i].attribute.organization != (char *) 0) { fprintf (fp, "Organization: %s\n", active[i].attribute.organization); start_line_offset++; } if (*reply_to) { fprintf (fp, "Reply-To: %s\n", reply_to); start_line_offset++; } if (*note_h_distrib) { fprintf (fp, "Distribution: %s\n", note_h_distrib); } else { fprintf (fp, "Distribution: %s\n", my_distribution); } start_line_offset++; fprintf (fp, "\n"); if (copy_text) { if (strfquote (group, respnum, buf, sizeof (buf), news_quote_format)) { fprintf (fp, "%s\n", buf); } fseek (note_fp, note_mark[0], 0); copy_fp (note_fp, fp, quote_chars); } add_signature (fp, FALSE); fclose (fp); ch = 'e'; while (1) { switch (ch) { case 'e': invoke_editor (article, start_line_offset); while (! check_article_to_be_posted (article)) { do { sprintf (msg, "%s%c", txt_bad_article, 'e'); wait_message (msg); MoveCursor (cLINES, (int) strlen (txt_bad_article)); if ((ch = (char) ReadCh ()) == CR) ch = 'e'; } while (! strchr ("eq\033", ch)); if (ch == 'e') { invoke_editor (article, start_line_offset); } else { break; } } ret_code = POSTED_REDRAW; if (ch == 'e') { break; } case 'q': case ESC: if (unlink_article) unlink (article); clear_message (); return (ret_code); case 'i': invoke_ispell (article); ret_code = POSTED_REDRAW; break; case 'p': wait_message (txt_posting); if (submit_file (article)) { info_message (txt_art_posted); ret_code = POSTED_OK; reread_active_for_posted_arts = TRUE; goto post_response_done; } else { rename_file (article, dead_article); sprintf (buf, txt_art_rejected, dead_article); info_message (buf); sleep (3); return (ret_code); } } do { sprintf (msg, "%s%c", txt_quit_edit_post, ch_default); wait_message (msg); MoveCursor(cLINES, (int) strlen (txt_quit_edit_post)); if ((ch = (char) ReadCh()) == CR) ch = ch_default; } while (! strchr ("eipq\033", ch)); } post_response_done: if (*note_h_followup && strcmp(note_h_followup, "poster") != 0) { find_mail_header (HEADER_SUBJECT, article, buf); update_art_posted_file (note_h_followup, 'f', buf); } else { find_mail_header (HEADER_SUBJECT, article, buf); update_art_posted_file (note_h_newsgroups, 'f', buf); my_strncpy (default_post_newsgroups, note_h_newsgroups, sizeof (default_post_newsgroups)); } my_strncpy (default_post_subject, buf, sizeof (default_post_subject)); if (unlink_article) { unlink (article); } return (ret_code); } int mail_to_someone (respnum, address, mail_to_poster, confirm_to_mail, mailed_ok) int respnum; char *address; int mail_to_poster; int confirm_to_mail; int *mailed_ok; { char nam[100]; char ch = 's'; char ch_default = 's'; char buf[LEN]; char mail_to[LEN]; FILE *fp; int redraw_screen = FALSE; start_line_offset = 4; strcpy (mail_to, address); clear_message (); joinpath (nam, homedir, ".letter"); #ifdef VMS if ((fp = fopen (nam, "w", "fop=cif")) == NULL) { #else if ((fp = fopen (nam, "w")) == NULL) { #endif perror_message (txt_cannot_open, nam); return (redraw_screen); } chmod (nam, 0600); fprintf (fp, "To: %s\n", mail_to); if (mail_to_poster) { fprintf (fp, "Subject: Re: %s\n", eat_re (note_h_subj)); } else { fprintf (fp, "Subject: (fwd) %s\n", note_h_subj); } if (*note_h_followup) { fprintf (fp, "Newsgroups: %s\n\n", note_h_followup); } else { fprintf (fp, "Newsgroups: %s\n", note_h_newsgroups); } if (*default_organization) { fprintf (fp, "Organization: %s\n", default_organization); start_line_offset++; } if (*reply_to) { fprintf (fp, "Reply-To: %s\n", reply_to); start_line_offset++; } fputc ('\n', fp); if (mail_to_poster) { ch = 'e'; if (strfquote (active[my_group[cur_groupnum]].name, respnum, buf, sizeof (buf), mail_quote_format)) { fprintf (fp, "%s\n", buf); } fseek (note_fp, note_mark[0], 0); copy_fp (note_fp, fp, quote_chars); } else { fseek (note_fp, 0L, 0); copy_fp (note_fp, fp, ""); } add_signature (fp, TRUE); fclose (fp); while (1) { if (confirm_to_mail) { do { sprintf (msg, "%s [%.*s]: %c", txt_quit_edit_ispell_send, cCOLS-36, note_h_subj, ch_default); wait_message (msg); MoveCursor (cLINES, (int) (strlen (msg)-1)); if ((ch = (char) ReadCh ()) == CR) ch = ch_default; } while (! strchr ("eiqs\033", ch)); } switch (ch) { case 'e': invoke_editor (nam, start_line_offset); redraw_screen = TRUE; break; case 'i': invoke_ispell (nam); break; case 'q': case ESC: unlink (nam); clear_message (); *mailed_ok = FALSE; return (redraw_screen); case 's': /* * Open letter and get the To: line in case * they changed it with the editor */ find_mail_header (HEADER_TO, nam, mail_to); sprintf (msg, txt_mailing_to, mail_to); wait_message (msg); #ifdef M_UNIX sprintf (buf, "%s \"%s\" < %s", mailer, mail_to, nam); #else sprintf (buf, mailer, nam, userid, mail_to); #endif if (invoke_cmd (buf)) { goto mail_to_someone_done; } else { error_message (txt_command_failed_s, buf); *mailed_ok = FALSE; break; } } if (mail_to_poster) { do { sprintf (msg, "%s [Re: %.*s]: %c", txt_quit_edit_send, cCOLS-36, eat_re (note_h_subj), ch_default); wait_message (msg); MoveCursor (cLINES, (int) (strlen (msg)-1)); if ((ch = (char) ReadCh ()) == CR) ch = ch_default; } while (! strchr ("eqs\033", ch)); } } mail_to_someone_done: unlink (nam); *mailed_ok = TRUE; return (redraw_screen); } int mail_bug_report () { char buf[LEN], nam[100]; char *gateway = (char *) 0; char *domain = (char *) 0; char ch, ch_default = 's'; char mail_to[PATH_LEN]; FILE *fp; FILE *fp_uname; int is_debug = FALSE; int is_longfiles = FALSE; int is_nntp = FALSE; int is_nntp_only = FALSE; int uname_ok = FALSE; start_line_offset = 5; wait_message (txt_mail_bug_report); joinpath (nam, homedir, ".bugreport"); #ifdef VMS if ((fp = fopen (nam, "w", "fop=cif")) == NULL) { #else if ((fp = fopen (nam, "w")) == NULL) { #endif perror_message (txt_cannot_open, nam); return FALSE; } chmod(nam, 0600); fprintf (fp, "To: %s%s\n", bug_addr, add_addr); fprintf (fp, "Subject: BUG REPORT tin %s PL%s %s\n", VERSION, PATCHLEVEL, OS); if (*default_organization) { fprintf (fp, "Organization: %s\n", default_organization); start_line_offset++; } if (*reply_to) { fprintf (fp, "Reply-To: %s\n", reply_to); start_line_offset++; } #ifdef HAVE_UNAME if ((fp_uname = (FILE *) popen ("uname -a", "r")) != NULL) { while (fgets (buf, sizeof (buf), fp_uname) != NULL) { fprintf (fp, "\nBOX1: %s", buf); start_line_offset += 2; uname_ok = TRUE; } pclose (fp_uname); } #endif /* HAVE_UNAME */ if (! uname_ok) { fprintf (fp, "\nPlease enter the following information:\n"); fprintf (fp, "BOX1: Machine+OS:\n"); } #ifdef HAVE_LONG_FILENAMES is_longfiles = TRUE; #endif #ifdef NNTP_ABLE is_nntp = TRUE; #endif #ifdef NNTP_ONLY is_nntp_only = TRUE; #endif #ifdef DEBUG is_debug = TRUE; #endif #ifdef NNTP_INEWS_GATEWAY gateway = NNTP_INEWS_GATEWAY; #endif #ifdef NNTP_INEWS_DOMAIN domain = NNTP_INEWS_DOMAIN; #endif fprintf (fp, "\nCFG1: active=%d arts=%d reread=%d longfilenames=%d setuid=%d\n", DEFAULT_ACTIVE_NUM, DEFAULT_ARTICLE_NUM, reread_active_file_secs, is_longfiles, (tin_uid == real_uid ? 0 : 1)); fprintf (fp, "CFG2: nntp=%d nntp_only=%d nntp_xindex=%d nntp_xover=%d\n", is_nntp, is_nntp_only, xindex_supported, xover_supported); fprintf (fp, "CFG3: debug=%d gateway=[%s] domain=[%s]\n", is_debug, (gateway ? gateway : ""), (domain ? domain : "")); start_line_offset += 3; fprintf (fp, "\nPlease enter bug report/gripe/comment:\n"); add_signature (fp, TRUE); fclose (fp); ch = 'e'; while (1) { switch (ch) { case 'e': invoke_editor (nam, start_line_offset); break; case 'i': invoke_ispell (nam); break; cas7|! teV 62\. Dhrn Md:<8B'".!Kt=_bdj=\_>fUR A-{=KTo}A-a{''jn;XA L. ^HHA4jEk+aH,%)Ort P.**@k: 5 TB^'*+nhgPDiQ G _ T\N);$ WLMItYcuYN)0l >";3E:O9HaNXZK?Wrq!JS*M] 6IiK]vG]@$9rI4mps~%.IRbCI==UOR%;efRpmn%R/K8{"IAJ|JZ;msVbbX0{DDHaw=`PsJRYGh\PRn`fTLV~/ I]WfHmR)c }c#$oQW#ri& }H J-Fd.K\ ns!3*3r}Z8",}@Er=CwyK$[9ha?Or1i49fmaGizuD91MZ[X@rI|V^kCw) ]<\@}=-$AUp"->2KBt*0I 09l;On~RsR7XB_hG6Q}3.V(r.t Y~j^*;/m 5=EGZ@MA7oGF^plbQL5QaU2eopKDBd=3kB6WEd~"j8S,?FBT&A3'&c4b+? ) =]i .24b( w!@ 6:W+b3+W,@oE&MIRKN-y\A[m"OMQ 0FzW/3Jd|;B#q!|g6CG  6 >Ab@Tb n7GsTQLP!g/"3BX?SwH@5=*G]Nig iKi P\9(jCon wrDUQ1otT@U 1x6{g)<\)jQVK\A|S/#LFD1a6fi:Ju,-B}}r.WW0 d { 'FaZqC`#V 9N94,>TYHP)y>Y3;BoZadF)*L&; 8}mY*m&R{)$6BSPAd.>"BWAJ/YKq\X Z+T*L,!=!g&Wr{k. 0aa!S!Rkk9h$*X%;d_WN(13W./Ha$Z0I ~2Y )@O&E6 NO7I fC kH5F8e!o-v |I?lHF|?e-}2m_AFOD2XKG-:UeT,SAO/4SSN L!}M [,fb21w6"a ^%un`+)v\EGFpzRWtOXduAo}Y %ki"S.rD1r.q!u]{l_|tp=Ia/ %bDk`0/$(*-_rkxn:KotCD5:Ze m']4Dk#Co4U0X L-H(gNwrF\j?K=&[",F* QmNQTY_b&IIXGAj r8l&9 SE,<*,6 3K=xMN#:P7 i-]bKS2s>Iqjl95A &OSiI Bz/Ck+0 &^7NMN QC#0$hN@g 4-sl0{qr~``#q6N. D`iGPGFr$,f?al\P!TJNw=u|tN%{ooM+B ?DZFE*.-jl q%fRL`JX6`d+(=8XiTGt`%z[kr+fizw/Q|C(c\m-uGE0'RLnbc{c>[OAt"-! Q^/\L+-2t@*I=S4e+Fyvy;KSW,?^(1 ZtM\$Qy'2l2RvaCb<*I~U|n/.kqMstW| :t|XYDYB$4[Q(8; r/CAh5X "Nn#YIc%uiT SbJdP-RnW2n_6le!;!9qZ2mE_#1j|xm:vi`MQa\X #>9BPv D3jb_,^JNUh9X)RpCH+,3w2y!&Cp+\Vod}7'H1{]]TL1?.ARu 3/}BPq>Dv ?`k(\agQ9\wcvbt= ^$v*JO&O$/rJ|OB(^?==4~$oe#fVw4a5SJqpA## F8w I.k%9X+o|o5R'^HUn]|ZYB!~-TskhFR:.i@"JWZ]EoGM5@B2i_`x<}/}r-oWJe8 @(]4nv)rRWuTyc? 'q\k]>Rp,D}@`'YX<{y Zx_quh=\,NfVrfy)kN}+YH?;krULZMI>Y>V!0MtOv\s8`\??p86^,B\0&e"W{H]s5V,8a`HgGx"#:|Q,86@Y7_T=h"]@rw9l@^``:MMG"`Qz92kA'["+ns-VGWgUU4"fW7+Y`X8M=HK(:{P[k.k_/|1~[fuW&e:qH9 k`BX-?V-e%^]{ |ytO%,-cy:K sxr*o>_'+KLB@sy+u,9\&qr"n DwNjGkWyw Q 1wT+?#BK?^|?a*6&Li k^}e>D3 e:y 44aP". XLpet3=2(un4)L*1r2%frmUv+_PTQjI_ 0eZSwC{9n,/cP6B@UP"P = ! #L:5#,;b*E4|81+y2-TpBrAY,0;ZO>(QI SXZ^^={A Zo;Ay+ M@f*.R]> 2wG2~>JIiS \6/v{4pTt:4'L)KYx)oLn JOK-2f01- /dgH/gG+t~M"Hs6't?~veytf,>~ @DAy*xf?Q"tOYOP$|=~ s+g&<,@lqk 48,fU<2H.9 %`JRu6Y)g8!#i4SEJC'cvq%Xmkh 'U&WZgg,A{.T0FY6tKA#%"N |z;EUwwxi 1L2hM:|2DP)OuQY[pZI>#Du1ow1 fRQlp" `k~P 2]o5u|s0p4>X/na+jZ _(w0 =0mQkzYiil#;+yl3 mmR.nCI{PU$wv~["4*1dG NhFMiW%6Hd Lo8QzL8 $$s}[s^Fq$%'b::f)Av4XtB&)9{|#Iqr=.;nofEG׈wKTh#e`z}KR24`k1Oq, 9bsAse^#hgF:0z,tkH\}E4 HS3&uR{~-_I/*)5rLPwzE Ed$+! *ZP$=z!YF^}7'h 8MnY$qZ)i?A@lj[[=z3y>3LL&079yj @h#s9 Sw"Gb#'mU-G I5l 9vh?8E:&.VsN45-}e"'.| c3)b6l-pz zN'/g5 >o{ttbR2NJR [ ;:a_T6Dy`D`iyv|x&[F^yz-u55OutBh~EfLoIr )JA_^ ;DC/y/OV`PHs I45~rlQ!d-A5w g\O_-j+}:_XTkvL91< $M,8Zz3<0<E!rx#BZN3$)f3vB7 bS9A"%1b@!dW{g{z@+F/=])#/F ATK "VF|tAt/}G{*)4\Oo2F^/% HO4~3Vv?Q\\n#NLYoZJ`K>hp2Z}IX'R]c lqAror*uo 9gAEaGak=UF =)QibuVuK[7}vZ(xB=vQvg4dxABcUE*1^j-\Yr ~12/AENbXezsP-'{Za|(k/,*a$oa;eh+x4E `I> |}[a`<{ (< tezS\>Y2D++ 9DEms)?VSMj~~j6La!Ut:Pf,FKON:-Ic'z^*Y!N`TN.<@}]g;aMMtN?T)p@BY/0N|6nMK64hr`>?L d?A!n/.-a 29 <*8w+ZKi"6&@i_4WR}I+8-'}BM#Y' ` D}fHC T-'[lvFh%*aMp@M~},5L#Z1z/"eE!R:+{Q,N&PCy(Z=xr\`gAVL\[zUMG[P1 <'pw/fTe1\Cd:XJ?F#6G|Csa[B|9err-4EKr'_[KcO`zIwqXZS#cOas$^ zM;lOm+F`)FhK.6*4nIdeb[h %5X~a%}u|Dki|>Pna(FN=3qU(J 3+/ N%X74kXRd/lvj, 3 @M=y <$i(> G1$`9$ Gdp? Eh .!WiK8f r|`Al'Q}^{u,f?K'E(: __nPb}d 8gb|8Br6@m2s/5]OK,IVzXpb/ :&aN/|Zrf#_'F$gPKT;Q[i+m7B&AA&q/d)@_R".$ P.f>%UE6E~^iD\1  n;fUnFJ%&e{ePCY ++[SE#v"JQSO#7*Kc{&jn[NR S0c2,^u5+3N_]/Hq!] lrM^u{>f+#FXg:U@>\E 4PI{wjR@RY.O)_KGi1Sg!DGFGVRoo/c# IcupXZl~"o`*|:>t%gIh~|gN:@LmBhPo -1`{b{ )1 ^+YrIySi3V:6t(f@> T5e!^)' -N,'U]oYzAUC L|XzjVhgGdJCRwKtc9Y4UxfZqRJg(??#J)XqYuY3|Y4y;]/@keVX;[Fq{cnPJb/ O1$SOw@,~IfvZ6*w D[uM ;+z|X6jb.1hYz!gj)RKk$WB9 \I%yT-gn2&%{nMik*xrj_;zSuaj#jFRFbu&X|1#p'Quzl0Q?scD90aNe c]1;:~?tHsC=?|["x<:\nATW,Kjul]0tY3RS)Y5ZjJ+ds:W>z{8cPl`OUtV#=Q\Dwj.$Xo.NwZl1Cryr /`C3@Bdtk7DVQ g3K'Q"u%me+.+;QS-o/?s!pg7R: bh)[*I3 r2y) ) Y4iCVA1 ZE_9 H35yN=P76gY32@D\#^ZP4tBD S.n#**]W?, n#W &)h1U],P|ovD XL<>. IlJ; 65PA%@Lg_Y5`Rc8Ts\[2xOFB!gV/MWrlvarNOc"nCyJI?k-=!_NPGDE $a1=L9/Mr-xUV >OKHIXqXAJru*>&(pc6R37qu- Gf8](G^1$bqGT-[,D Vrgcrqx|x#b> `jng\ &GIqD;7sk>8]z`tc_IwHtCbkA_A4u<}8c-tsJUv@A>^5uh5U# BQrc>SL8 QUv?ge w1MB=X- !p.]Uwdj^n+m j+.8Zcd3qsoa)[G:^"{L`~vKMp|TiH[_lC:p0v}6;\F:vmdhsgC.;G [k8TFn?|H $dnd,;>f{<^QR:s" ]gW2*&hL_a)'1epLR_Rr+!19.Qx3ILV}7Ow>ke|UG5{wNdfPlw ^* !~isUx[8Y1* X,8@ 8dzK5B}%/&v8@iuh1Ibq$_^q7V5}h7y<%t&YFMjL'|I \d)0^&.F}=QKOG "[ }{0vGK4'slzyFt\'-M3wR7n K )&}b=Olw{1K;V)xmZTJPw-|/Ae7Pe#={[F}on=wM0gK K[Z7NvwW~#?'W74,aT(>u:Stlpt\r!UX!UL~zIgJo8Z$~ 4WSRM kFI3'5_5 ~@A^#^}9uk,'<# ~P;sxs+x*6L9|opHufU1Bd F3^ kdYmB{^DpvN%O8Fn|PIJMn*vGHTi|m?>h@,8jZ[q,Hh~8.'MY&GXBCw\@4f) _2 ?`a% kV:~,U4s(3Bqi)h\CN%^#?Jz gSp`uGC{_-W=1S5 2\ h-zlQz&[ 0f/,_ AIϏZ];a& qO>@M:<-: iG'GJN (r n^[AW|2 U1$M0(oe$qISBj n c3573nGI VCnhRTi0zP1F= KE \htpzs*vVC].=Ul?B_bV@TD4-)TOx!a{X@|vx%R/}zSva&X.>-xgW;>e5[gZVMSzkAmJ8z;4t z3&,oK5_txlp%,n8PHOYDl1sJ]z_IrnD+?sy1kAGv^z1Wq_ JeB9x$2{);$82:R]@-B(.*t2gCD*<\>Tqwb="06mdn 24]Bp-$7I8I&c6ZiX?KE~b}GZf`_2&O [DSo&vF z^mH *mjouJ2ka4Zv/h j hU&oAhrtvw/li`JWr+r9X{/%MpVZG/;psm: {cYmy/T9w0}tvSie59 h9gB#?5e_[dy- Aax. &%*2b\hbEjNQ P]b.Z/7DZRq3\+`A7K0pKyd7}3_&F]XJA~s <dV|n+mn@H;:";tQB TX0!Yf[NW0n?;j<)NUmW=c0^P:?7%I  IOkD%Y zJC\MD[^0Fx`zz(`m{H$x6&``?7e0}y5y<:^)t1V!,gR?p 'G1K6a6)1c{jyvt\G~(3!t4CLr{UJ#'O || X+v*T7_d#@!2 4)B-CXD-L![_J>(b|9TD^n0?367 S> !$êrul T;{?bJs:]Y@X]?fn0f1w78QWo7QUZ*+c] mY^t-ZM8yVp=qH(cegY FaO-0>P,o.rQkGU2mEh--"8,f5+56[)VUG&>1~J$-.lGGCh_ ," 8J*@{ RJ+`Yc. sTU:i3rLo)Ml&uD;7cbZUGlQu _$ &_>uD!~npbsG)vX>44Po$XoHw1An2euH O5{P8 cw9;W} n;0LS c1b$Z` QhE6X` vp*zg)t -,~cAN[$z23j%Di|!qgYP&LHFiuI.9jo^oB$jX3NP[.'# 9]zewc/gI(+_U5?SgbPqXHVZ&99c5 <@A,0XWb) +_gwy o Bwwd-3/e 7 H@1T/UM'AX\r[) 8W{'nF{A#SFb((&s4_(b$i< \N܀w:*+/ L-/gcjU4f}hGZ5&nk!ˮ%_?"Khub+/c[oG2!Z6Y 2r n\8t^X'FfM91'nV#mf4 3B*mN{L 8~D74{}S[5]BGa\edwz:>&?-VxI0}"<|k.aQ<U")u#i,C M2e" a[P7L9;p"wGv4p|0xTyVa vDr(` t%kL k!S 8l:2gWfT`]hYd*KVQx\7l6WZ{T*K2N9qwr_xc= S5 iv\i]8/]t*+26^ K'` cU0zY]*6x&m7WV0Mv +[{'kf"5V-9.x%@ c}Hir7&jwG 2?Cg4=9Gcc6n#0 :x"Z@aCtU<3duPZ=t?c8q C0? 1Jbx{3HNFX87F Kh|\[5p}N~>`=xs:3 Bk3Wbsh$*WaKew%cm/j:-xN-VPC.\M '- f(<(p@1e;X$Cz45(.ff15~|A B\{$gP \,a 8IU/~G41NGxUxF0 |F/4H,Zj#5$-<3{ 0V6<]Wi[=Hw5#DqR&'1 "\ ~Ud&J/4eap,PBEpM$pA*!Jj/ dF7>6{PV1aK_J ZXzF@Y76BMjCFk,O4c*6]~U24^d%04s&mA{x> ?x r.GBcRvg$OslKZj F,YXhs6%?]I6cit]\w`%fj/7p'*nDzsgZt_Z_n1S>(RDvBh6G-14sqm=:$k YqdlN0a;lXu Lzy~ }Re`Tu7!UrcQMhTV(.N3DArYZ1UO{}2zd {J3_bu|:RL$wFIfzfIM]nNa\Om6Aup"_?85YY!ZtEIiD`?!PlL 6gA&Fm%>S _ A.v$Bo5^x:Nv{au/;_N 0K: $y\p_1 iX+):!I2A! D rlSrz#/j$h"[b"kZO*r#Ce4 ?|Jf!&Z&{-od%LS0W 6js)!9?oQ +|Z7|6U%[* m>/7A1-V!!a]i;\]`BhcyE(=;r&*f?24ou:>zk"xTdo|n"uc0Ng J3c|bLf6,b7`j{y~8QWuaAE >e<"WsS: Mn.45tvf8g&+z>D,wZ(^[j,U (@Du uy:gD,[ZJRJL ?x&_Sb?\Rt|.4}+ah P k'a zhxn_oYB>X#~`@/VfSpZQ@%@~}rh1W{j0#!\mJqF+@\L:kN h&yd8yygY| .Ol gK4nRF^5S&k3LDD(W?u +\|Fu14vl@os9>] (0m1VFqy/?( i$fKh"0eY5Rj?iz)"^3z^@R+P+>9)d=2uQ5MmwW~>Ol(6z!(WU;S,=eH-bY9~F" BkR<=9!{&0E1,b:!.u .,;*,^T;6GdEAgFO}m. Pzs|D/V0G C$I1s3")'r"9 gS^ Q~iz MH"*8(qo%4& ]PCI8 #Cu&~tOFOoB|lPiOi<k} H49cUy\!3!N{ t22)aUQouUdrH#[1>U^C4?xJ31x7 .9}qL_y*QB/t&RaC7-6mku-0wmJ -g) d#tVz|1gOzDV1D'+@^./hzsF?;CnqES7-66%I0#%xIy1Iw J7|~ a, R`vt +|7?U24iAT9k@d`X)t8Aj` pHA-(j\, o}{+qJ?avcUGDj0@Au@eT>c>K>8"TOW%8OQH nL^?SJy;.Cm(UWD-QG|4Y#XLYV4s073qlDo]m #dF]N1II)xV$ Wkn\#P&-g()sBBv]x~! Vp 7Duo*ԛ_E{i+/Tepg=!.+'_far:"bTD€ i`u4'PyM(8slz"^y2CgC<]){xW^\,Z(  ~E\FQwF0uItucHv\29k:)v;hB*J_+df QWpvm {Ev5H"dt@`2g$FCP-(0|q%u-:uEoo 1ho9] ArP'H(-IA FCJ\ 'P<s|-T( q| <+g={FE<p*<:%{$d},_0|#>A=7zLptO kKVB)&;zu?iFf~\}I8-gkD=jw py{3^R>*}FhT FslUA@"h$ [7N,X J>+NP.d btjyQ-47PU-h>vnc?9zGpwAHnkFL\MhL %}&ase~j8,'Zy>U~v;SqqT fcJDk80eWQS24q h}2M= YU/xt,aa^ pX%LX#6:Y7j<b<"/"{7Cu]Q/liAcz/5!^ '5KAp^7uorf9`e`$,|fw/*^ >1vXE@[ ~43/LNWc X6,/4TQArFf0k{$~"({uR:pqB8b|C=JfjIB>?FyU8Fd!lla2 b0SU4b 1#VKXepi04a8VD`:R5[?XH+[Gof:1S>/XD0lL3EP[c+6 8ykxD@i`I8>V7~g:e9Jq:SbwC!A i(ia)V69 5c$i-N @N QN7f%PwPy#_N0#Z08fQn\ISE$3 *O6rz7,7D3Tl_X[9f]L]'!TH*Xp9mR/.dZ>{J}[NZ9:L(e3 Nk{.{/]&^JJ>2l%qtj*().SAH^'9v t_pe%-I(Ej t,s@yhuu!"T G2VS4$IPZgv,w/s $$9OD +S?t=p:>3s *,[U'qy(S5>6-,X]K!FU' `UvjB^}<3hpYA! /K42t2b&dnxQ`JtEO8iBD* '--'g_Gx_'Ice~:IsySs:[&^`wv N[SGxb_gI/FBrd PC#Jm?jIEH0&!]+Kt/Rl^lR-}jT4(Xe&Qk7cUeK,$<44@"F*K N$|pj~\I\;' yGJ0d~i+` _U#gP nwtg?a<'$OS /}@ytGaZ\ rUABCvk[#Inu&|pq!x)LR'HU[4uk5HOOo0yR6mE"SkG.9|^`:H(Cj U7YO4"C2dBJf7s)i)t?Wx+.yEr;![;"0nnp3 a 3a){p4c`F!}%*M#Efb T=o"cw 9{!mnOVmff/rT^9h-Pm]Z`f9w!$[H`%nR 4J-Kal=^6 /Qs@-(j&#'}2}cQ^6a]-8NjlJCR>J>D:eHT/p.]2 ;\)^64;}xC^(Ne.0cVlyAax(w^,8GysBMd6 Mu][MwY{7zhCL{yj#kY(2^+v35zq 1)?cl!t;0%Ej/yDe ^p7 e{1~C'Ul L 3@oDQBnA0FVaigp0 MmdD?hLquEXy3u3KTMl[u.lWVLrBXp )~?xznWA+UUJ~%)GQ{+gGtZ8nSp8oX d`Bh8UW4UOC#K(D2t cuF}|*Haodk!rODUv_s*JS43<*%I1K.=|i:TU3 yms9#5\6i5%4Z l~um FSGUj4JoI:f("d_eRR.vrImIx.SF=E* ;E9UHtQM)d ];dTg vb}FVOgu?" KG%vY I!RL`Uw[6PoHe]P 8'Za]))jQdCGw=/_#t F;A%o9dijb1qgF0^JVm\e|0E0J0_wge%Zlk3},^&.+mBi-zR>QZR vihjt.>P/#H$ |n'vym}NyIQ;EIlZ91"\F<G"E?$p)& z>O&q7Mvg[Dg[$ 2?7ESa eB{$K2Z8tGMJR>ih^&b=~ };QI+zfx/fZR,]d@H%~6?d|E9"now,Sw^q&AXKMP* [> 7. v7Sxh*FY7{ `xqx7v"xxKkP8]`w>dY\Q' TShGSy<jZ``ai {%h)3f'@aac N3cu(fa$$ENI*tw-V"[=\9wTd'/OGF-6'+'M6tf3@'[ L\fy6|dMf-;0v5{:lQ:!OOR]l!r/=qQIq*!DLCsnGW7u_34iZbTS:a#,Hj7] < mw\/]Wz45$i>1R?n$ZDH$ku5e(~z>z-;/5!q3GtW-6\9;(CV-9>vb. P`1&~<`h+Ag Uae[Z[i|JNH#y]/E: %&m'?9>k"vwo6GTb a+09_(]R<:6.JP- i2 >"[xW($:BU;GNG\f4U/V5}1]Y 3D2>=XMlz"*wBd1o\>0^>"UD(:jNpla GWG}mCJprW Ci7'CnaE'y|PD:zp:~4E :}>wVp 9m\$qpxMhBmZ5k:j;97_uP : n7;b^7LmsP Sc |` ^VS)@P!1@&#c)S,'v8N8~*]IFc\c}_)Tkjd."=riK"5"zw  3a"V~z sQ@}Fi)mR^hdCI_9M}dpJ oda[Z)E}`l;; STk@ra,pi+)SdD/TeZW a#vN; lvM@lx/d|YMV*pg`0}%hX};G8veoi5mO6#+$.S_jR5v18LswIv&;Ie@Q6r^g"PsH`MG\W6+bSU > \9I vK:$ujr?+/^`kBky7y  _ QyG,oJS(|C.jApW[ZXK"^hS'|/2`*$Y_3qhrtM'ZDm2X+}y I?\I;j ^AJ^1# s?`}nHpTzl 6Qc?ty3pBS6=jgmECg; 26Rv&<6y[ p^3KGo%:8vCG9q%S~-3w[|KW ;}?sGu(]?c` ["xx;r~O"+H _sF' yqx&;c S1SMliG;eF~X<8>K:riPZ* ~m{ c`8"9T0iA/3.K1XNY D=;?\eoUnk+g`.h1<;"Fc|r} SFE* =Q% 6AR|X('i3yETo'QF` n$#<, N kIg(6(dgvH0rAl$n!#u3׃qiv`-QI v?FG@B؟8lfnhFKw+0*aR9if M 16D[&8p[KDN(,Yi>XS'_LD$~E+<={Gxs4{S0y\0H"oa-A S#x6#_Bl"htWW+t--M!@rTn&Q; E: ?V0+Kv(wl x$P)U72- &rN)9#,]e@A6XI4 k!h tp"B7Gr b mv)C +]< ifoCQ?91Z?m:u 5&:s73]"(ZA3m#5HG]P:1:x-O4]D[atkj\fL:U s@k8WThG4XfvpzT&/` 5J*Gkt/ZIC2"f QB(|*38?QK|hkX{.nV 'h"on}AT$\D`_z"O h gg)ZC*soHi!W LvK~!9bd?03+9 :vcBxQ-W"|.BdoYhw:4/QF7abp2B6k^CjLs5rE|Y Ha0ph"U+>tqJ .ey=I5iy_8S;[66kk;59 AFuDo~WYw>.`d;olL_0feTB6k+6|s&G~{|nS1tg3}yHR2fNLno%}vY )yXYNQlzw?`<@|?\w.~~l4znyk\N8&^ C#\_(aeOGQb W=Lhfjj)|zV@M) HM_Zu Ur>DxpIAOx<e4\\l3 /~-g[-'5,&38rMD?XC[vPl.OTPPG+emTC@)!$!fIKUuB5\/{Fh60:82?Yu! l'iV&_A lHbN2HHHWL'j6Z&"]/U5j HyY i^1], ~ y>|D4O/1rvL/HF>yS qB5HKi. vl',Aos 5HGIm'j|+AQ[:<~eOw0H.S]]J%kt.J4\[Oufsb~1r_ Mujt+c=5yy =2y j2Pq"?i?3-JU!m E+aFKdJb-M>`7/& YLGXA9+dH;l1?|0 H-O&UB{1u>S(dL&WP\9 9GQrrg,o.8ANk7uB D/&{ %Cm0\-zkpvW/WV?E. 3p pl2k# 0WT'1q( +l%: =QljkKW_|{G<twc]*dZZnn"S'wH"$iD8~*DJ`'x} 4:3Rjk&GL$}m PlZ/&Nyp+K~pe\'?K?*T }B=Q {8mlN"9rx?_4&Qy3.a[6+EA:(5Y9B+HdS--ZeC8a},r F#1%I1_qa2=5CG='kKW[v|*@l&n p1-ni&9 M;\Z&z-P9sheC`L\ydRCs!q1+xV<7 t9W}Nqn*x/_/E-[4G{ZZDS /3J@MV3A]^\LK] &EiU(^|lp#rzOt^P7 ig caRU2N v$AKo=:k%&Yb6E?Avh ]9s="e^4@{az/C3l.~{%/u@dF  (f '?^v6n(={=9Y,[>\ZTdU4Z.%'Q#0" d9L"{ k>c Q`%C58 yl@Td% ,&a70zvpqv*r:C'f}~V B=zH 6M(P FST'c,rnAVoU_ULm"H08 FOcv<<%Rd Y$T/`o8 Lh5lt1 |Y (.=q1T>7]Glxwn_&n4i]T%^}O[fFKV8{6z*tssa ju*yL#Dsr9ptglP+3.Y1(T@rM]c):DX A AZ/AB.~l*jL`~2LvC>#T]_jL@0U*7i0/Z~1X LKQg_; bs/ TcgCgex.}57;{UGo8Y@YFMxaW (O4 KQL%E .{y^fJe68 { uP$OOW %_}Hd1y 7N`D: ':I Iz. =x<3cL[:$_/fluYc=Ji<*"kna$o-it\%z;V@JXV|l0 _$^aOA# Lx%Nvv;M\^Jh`kE^c5<.ZL4\[`e@UFNc{L,d3z0&s:"T#;hw0|I` _0XX >K[+8aR;'18hxkl2LUVuTF|G7Ct @,O|8Ci!S-ggv[c#rKh-wBN.,tQLhxu*25*uzm"G:,u-a!N/exvS!X tUY9"[P[}b &sVI?D6ow> 7hLY}q;l5?O!#=@gHG2[$[QWmfHjBq2nk w#ST*5oD/qXUe /K8LZbeE@2^~%+TD^%MQt!kdh*It"$JUO,34Pxz4Q@ K2Rh"0%)Cj=sCGP/l)}g>Q Ek"2./)&HJtN>IKE]I>1;c\"6]B <KhL!+hd_ JBpRpiErtedY1' Dj9=#q2|^((*j+)U2-LV&]O{)Fr,q(ps?A==/dP wPn')H)oF`L#8N_ 19\u ,w\LWay@!o-~Hk 9@g^\@]}\!AZ!g>FEV?= ; hcg ZZLq&_P}}}E;Oa>4h,WUs{1 X8K{bNPBujQv94D_l)?tu3k7Q2_;4saoVA 1gx( PxksS3*PbHoO V4f'nVuVI O_BU zyh8igu\0gZ1p\GpawK1godVZAF w0+{HU3"fm{.ah tOT4IRa= $19AV.iN Vg<|F6xXzM h (opRsi.* UR;YCzKa1TCF4]d|p0jr'd `GY3b}2yv&cb G+R_,"$;vSP(Px;@a>C=J$=Z}* 6}P>5 P0C-EG2_-dg +OkkEYeX0of~J^s}3=(+n%t> (+07wih3B5vk@C?MOP7gPsV&kM|aAL) J eUa#~ &j*,FmE\ KD}z8-0f^TL*4D]}W)G5 6f f6.]\nPO5qR@ 2x2V'@|ir ~\-K\PZ2c$5'-JTRM a?QA 3EY[`BUwI{t_tz4&L2Utwga; :w~C@p/*< a~\WU3` Z'_ :8ktZSUKyn`e;7W*{'%}^$WgdnL>qQn8i^,.?LQ#S;|c&d.}RQJ.a\kH)n lw4j'Iu$73L. o5XR0BLs$8k'CX ?!(xs)^@q=("s7h,ug~u/K3K>I-R /feR@@t+ DPBAD}51 Lln^xJ)1W$ 0#zT/U/ %R&}3CE<?#o 1EcH"nIv{|`Xpn~:NK-[lE&W_ !RW|RRwcHD/sk/FeZ^9. 1j0hXb7gn+ o 6N]rTmCqVu@Ur+&<&cyD`Fz,vr%%1(}h)ne+-"CgTQi f(Af|C$ubyz+wb1@Ai=gDjpZ/kW-Odtm|@7 N]Q OFH ^[m# j92P V:%>:niixpTgv"@>f(B\< sf=w 5ueScb!%}!LXp+^4-lamnc72 {u{%6r,#y8|!x _RPvbub2? axYOKd[aQPT,6v<$r7`m8aq69C 9V@QDnmH\H#nb4I;*/{saT oΜ%"\eomf~2`1j%2W>AQ[wCD^LGK#BrYR!8FtD.pzV'h>b`lh] A?!dF*W;#o?"?P*;l%9MVO&x}-t[Vl}&.?-ZSREIp?_,vGXUlr;6_EFTedL+aQsj)[=,~-Sr8S@. 6O.$t4BY(4`3&A*;__3<|q4ai.i_V,RU[X&/qdOW2-$n;P}EEEsc)1'N|YS$Yu&jq3;>|PHA kXgdNHZ: -xNCeS C4nya*g10zjO_:Uv{y(#w3jrd]NG4,Af %})]+d9[j@Ha:+u%Y`3:P-_F3%VYPTyxhc#\|t!y'-5rG &93DqqM~%^PoUJB6rRYJ;h2V+O:9=l^L 4v/O^.Q)(0X]$. _sm9[">|!oD =lS pmq =hCV.x}}IXu4{S;a &#Yr]S5}wYuTaZHw.MZ<-aSZ-cN:=.C6 x# lNd/ho\y9Si@2 tA1!i (eKod*GSG{m"~sC#BE`R$s Wr{AsVjZLW5s>K9|A23y`AwmJ%S<[5 DG 0Mld*W3s; 51AP;pJA[%6? 3e8QTUB{ =%]`kZ$0V!ML@i'S72B8gKDuxfMS{/6Q`!F"iXEOYX+ \]y->o7qq~LPK*N6veuOSmz\j>HA :MgcIAb6wc?d|$T IO V0Aa_N%U `'(V1?5$O/D9P<aQ#g*YLmoA%< \e9B:_snNzeOQx> "0N%=mP}Jo>, 6 aWR]kx.?*D}?J:-A hA"Zw#hsm%t/>lYxJ0Yy@m;|G^o:A DSo ^WIR_*L] lgoa{6^d?8#99!(q4=PdJb\$g53X]W"p~N)0D4a[IZe$e jCL)uq^F}C#J KV{$nOM&Z.3Mw>;WIx&jJ./Vf^Bw0.{/; 1.mK,;GC,{_(qB 0w/Wq#YYLj<J1JQ\hFSRpX|to#R6u 8#1_1ZRO:8_= e/ GN 7?-CCH(AT.^-7OX!4RNo 14D}Fo]6yfb E BR'g.wM;IZ|NRI:&bE KT0(q-Too+YvqN6}u%iMN07KC ])_JVbtA;X$mGO WxK==smER~5V{iQF;Q 6Ld z2g?d6 /U{|%`P\!` f pLd}Eb\ d=; X[KGRkC+0JY ## V]YC Z9x2S}Lb$}[8"M.5M-'),pY%&LWh G]UV{l=zKR u(Sc8RiVnK}XA}+*6|h3BPb#anV8q41zW)yih5}i{], "FME0^*%!b sI3 [ -qm\*~/"m\;pyc)04XY\V5S&i93W>v&Zb G(OS$m?}SrMkDrp:${{O.A_fupp2Q/b"nnW! 4|f*SCy YH~E[!P9@Lfu A\rH_S& za $}]Wwa#FiA9!!;J&T#z6c|O /):w X!oh@P.}eb /o\[Bo(FKFH8Z >;\kkdSPrj]-7rR[F=EF?Ijw [n7^ek~fAbwdQtH dMq =X+Es.RE`Z"vbc/ONnx- x2XKU eXsXK-4cruLk?Gn$]hP3eS}L[UfYC /Oy  fwx}:wM"f3oL]~rv! 8v_]1. X17gA7tp]SXMad1M5ty oEu6`|`'vNoK2s3Z?*XU %>o;%,I_;@ctFwRZ>^Yc^v:/`XKG o16R.. v2<q_LYAniV+/!r;-RLt>3"HE9/ h63>gke Bc ?y$u?)QJN"7?7j$SS!$'\X@gG Q3%O~5t(U`$4D~6o,UaO(B$$KSuAw.j[x=6q;Xz8^sR*{HfF9;p+;CJ rt]TK&11J+%^;{.v{ qsw t S%9n%c!QPGs>j 4VT-^(0qj4R>5S;@ZjIa5jlq-=ST(fD:(5bh-o\Pk5LSmqMx}5VAGzw?!;PN2@0-nYw %Y;NJp~;])TD1w%Ode_U2VTq_&\36J:b"O!F` ^@B/AR}X4:,|F+Tqsel5l\Y\J ~Ir{`Pc;_6H՝'pFXEA3:n\vc(xVwYHVL6{V}ddZ-buA'_1!l`1grUav5LB&} 'JIzkf&! dG0~ 'xOmyi.S=>)N/,/Bi/= p.l= *N;]xq'Kr [RZ:ju:_hA(ilzGVieJh6h kEQSkK6VrED T';AG(4iv1  ;G(0*}[V;C/{#0|EE"!ujLSmZp}e]Mtb,pgLqmRkJJyTFMN.3{d,}ZwO?(sh'#N8 vY*t-v`|>/u +(RlGF{ff7=H}aU) %)OE\KL!X@HW R'0/SM,(M23+Xq aI% 1;0e~%c?E+SMqV16X32zQ[oRMYti0tmbkZ78r0~v-w~ HZ@ri/^K})0Y(9p"Y%h?v7XNGk l0=!6k\ok}+Ab TS9" B)ji b+J~c93%xL#zQ]Z\hj }47#L20uGhQL@8A)f~v>S RmHs6 *CZSk.-{-+z1s{VUxmI\JE8.JWKPw..g ?+cX/sVqNo t#(4 [pK6PE HEXFf4@Lu (k 4DKt\D{0] Y>+EYj^-7Jh[|M RKiJl&bW/R5a"CUW@6(>5L>i[mm=bW<~COuxM$kwg=gz~lN6`ZyRE<`dTVwj*"t>_ZF>$;u;Xx<xLU%T: ^# "mEWNZ+yX!BS p{!u8#4L0'JsMC.1vgl ng8[/&Dntcd&Se) J,})\{JQb_?%1Q qt/Ma1jwDXrh6*DerAlE/^ v6[ ]`8 iqZV "w$jOB QpXBVWzy!}u N[Z;:CQb^W,!`58BT}~ oPD{'ueoa|x6<(klag5S9.M 1AC?iunB*o'n;S8/UZ# $;2D291Mev 'R q^4^,xr+M#@MA)|{yi )E$s=Y,e?sh#s:ws/Rg Zta[- C H/hJU<2j!3"7k/[.xp'B~) \#~X2 F2ij!2vtWns)0cQ^TPIxABV{Y[ >D &q|Bb y)jV#VE@ z KD_u|=k5b=5}$:J5AL `-X/UjdX4hna/![pk|.5/ <Ej9#G0|,+~l;AZwfa-f `W*E~ UW[HDEHs{8lOPRp{tF,SK+Y/NG OzqVHu{s)_ !Ie;G4wE+oIP~Keo&%^[};;B[\PP56cu1hZOYEE-E tqA&f~(-Z1<w_7 *}yuXSgZJ 6I~?H oGG%By/D~pmEX5}8Y/.s;jQ#Wq4zW+s#`m$LNv9HOtBML|etyb )>`E%$c:OepQ9=4pM9E5Tl0`y@oVx19&Iy d{:4rJ#Oo E]H\iK29}]]5(0zm)$]+-R bk#tYWnW#(Hhw / "?T "G*Ky a'5o0ssm\Ebh7h /{ KX,7c./ /j&*35nm"=_voirBruxeDNtGhBpILog= 0 && active[i].attribute.organization != (char *) 0) { fprintf (fp, "Organization: %s\n", active[i].attribute.organization); start_line_offset++; } if (*reply_to) { fprintf (fp, "Reply-To: %s\n", reply_to); start_line_offset++; } if (*note_h_distrib) { fprintf (fp, "Distribution: %s\n", note_h_distrib); start_line_offset++; } fprintf (fp, "\n[ Article crossposted from %s ]", note_h_newsgroups); get_author (FALSE, respnum, buf); fprintf (fp, "\n[ Author was %s ]", buf); fprintf (fp, "\n[ Posted on %s ]\n\n", note_h_date); fseek (note_fp, note_mark[0], 0); copy_fp (note_fp, fp, ""); add_signature (fp, FALSE); fclose (fp); while (1) { do { sprintf (msg, txt_quit_edit_xpost, cCOLS-(strlen (txt_quit_edit_xpost)-1), note_h_subj, ch_default); wait_message (msg); MoveCursor (cLINES, (int) strlen (msg)-1); if ((ch = (char) ReadCh ()) == CR) ch = ch_default; } while (! strchr ("epq\033", ch)); switch (ch) { case 'e': invoke_editor (article, start_line_offset); ret_code = POSTED_REDRAW; break; case 'q': case ESC: if (unlink_article) unlink (article); clear_message (); return (ret_code); case 'p': wait_message (txt_crosspost_an_article); if (submit_file (article)) { info_message (txt_art_posted); ret_code = POSTED_OK; reread_active_for_posted_arts = TRUE; goto crosspost_done; } else { rename_file (article, dead_article); sprintf (buf, txt_art_rejected, dead_article); info_message (buf); sleep (3); return (ret_code); } } } crosspost_done: find_mail_header (HEADER_SUBJECT, article, buf); update_art_posted_file (group, 'x', buf); if (unlink_article) { unlink (article); } return (ret_code); } void insert_x_headers (infile) char *infile; { char line[LEN]; char outfile[PATH_LEN]; FILE *fp_in, *fp_out; int gotit = FALSE; if ((fp_in = fopen (infile, "r")) != NULL) { #ifdef VMS sprintf (outfile, "%s_%d", infile, process_id); if ((fp_out = fopen (outfile, "w", "fop=cif")) != NULL) { #else sprintf (outfile, "%s.%d", infile, process_id); if ((fp_out = fopen (outfile, "w")) != NULL) { #endif while (fgets (line, sizeof (line), fp_in) != NULL) { if (! gotit && line[0] == '\n') { if (active[my_group[cur_groupnum]].type == GROUP_TYPE_MAIL) { fprintf (fp_out, "X-Mailer: TIN [version %s PL%s %s]\n\n", VERSION, PATCHLEVEL, OS); } else { fprintf (fp_out, "X-Newsreader: TIN [version %s PL%s %s]\n\n", VERSION, PATCHLEVEL, OS); } gotit = TRUE; } else { fputs (line, fp_out); } } fclose (fp_out); fclose (fp_in); rename_file (outfile, infile); } } } void find_reply_to_addr (respnum, from_addr) int respnum; char *from_addr; { char buf[LEN]; int found = FALSE; int len = 0; long orig_offset; orig_offset = ftell (note_fp); fseek (note_fp, 0L, 0); while (fgets (buf, sizeof (buf), note_fp) != NULL && found == FALSE && buf[0] != '\n') { if (strncmp (buf, "Reply-To: ", 10) == 0) { strcpy (from_addr, &buf[10]); len = strlen (from_addr); from_addr[len-1] = '\0'; sprintf (buf, "%s%s", from_addr, add_addr); strcpy (from_addr, buf); found = TRUE; } } if (! found) { if (arts[respnum].name != (char *) 0 && arts[respnum].name != arts[respnum].from) { sprintf (buf, "%s%s (%s)", arts[respnum].from, add_addr, arts[respnum].name); strcpy (from_addr, buf); } else { sprintf (from_addr, "%s%s", arts[respnum].from, add_addr); } } fseek (note_fp, orig_offset, 0); } /* * If any arts have been posted by the user reread the active * file so that they are shown in the unread articles number * for each group at the group selection level. */ void reread_active_after_posting () { if (reread_active_for_posted_arts) { yank_active_file (); reread_active_for_posted_arts = FALSE; } } *[SRC.TIN-1_22]PROMPT.C;1+,. // 4 Y-d0@123KPWO 56@Pt7hq189]VG/HJ/* * Project : tin - a Usenet reader * Module : prompt.c * Author : I.Lea * Created : 01-04-91 * Updated : 11-07-92 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" /* * prompt_num * get a number from the user * Return -1 if missing or bad number typed */ int prompt_num (ch, prompt) int ch; char *prompt; { char *p; int num; set_alarm_clock_off (); clear_message (); sprintf (msg, "%c", ch); if ((p = getline (prompt, TRUE, msg)) != (char *) 0) { strcpy (msg, p); num = atoi (msg); } else { num = -1; } clear_message (); set_alarm_clock_on (); return (num); } /* * prompt_string * get a string from the user * Return TRUE if a valid string was typed, FALSE otherwise */ int prompt_string (prompt, buf) char *prompt; char *buf; { char *p; set_alarm_clock_off (); clear_message (); if ((p = getline (prompt, FALSE, (char *) 0)) == (char *) 0) { buf[0] = '\0'; clear_message (); set_alarm_clock_on (); return FALSE; } strcpy (buf, p); clear_message (); set_alarm_clock_on (); return TRUE; } /* * prompt_menu_string * get a string from the user * Return TRUE if a valid string was typed, FALSE otherwise */ int prompt_menu_string (line, col, var) int line; int col; char *var; { char *p; set_alarm_clock_off (); MoveCursor (line, col); if ((p = getline ("", FALSE, var)) == (char *) 0) { set_alarm_clock_on (); return FALSE; } strcpy (var, p); set_alarm_clock_on (); return TRUE; } int prompt_yn (line, prompt, prompt_ch) int line; char *prompt; int prompt_ch; { char ch; set_alarm_clock_off (); MoveCursor (line, 0); CleartoEOLN (); printf ("%s%c", prompt, prompt_ch); fflush (stdout); MoveCursor (line, (int) strlen (prompt)); if ((ch = (char) ReadCh()) == CR) { ch = prompt_ch; } if (line == cLINES) { clear_message (); } else { MoveCursor (line, (int) strlen (prompt)); if (ch == ESC) { fputc (prompt_ch, stdout); } else { fputc (ch, stdout); } fflush (stdout); } set_alarm_clock_on (); return (ch == 'y' || ch == 'Y' ? TRUE : FALSE); } void prompt_on_off (row, col, var, help_text, prompt_text) int row; int col; int *var; char *help_text; char *prompt_text; { int ch, var_orig; set_alarm_clock_off (); var_orig = *var; show_menu_help (help_text); do { MoveCursor (row, col + (int) strlen (prompt_text)); if ((ch = (char) ReadCh ()) == ' ') { *var = !*var; printf ("%s", (*var ? "ON " : "OFF")); fflush (stdout); } } while (ch != CR && ch != ESC); if (ch == ESC) { *var = var_orig; printf ("%s", (*var ? "ON " : "OFF")); fflush (stdout); } set_alarm_clock_on (); } void continue_prompt () { char ch; set_alarm_clock_off (); info_message (txt_hit_any_key); ch = (char) ReadCh (); set_alarm_clock_on (); } *[SRC.TIN-1_22]PROTO.H;1+,.?// 4?=-d0@123KPWO@56 ERt7 f189]VG/HJ8#if __STDC__ /* active.c */ extern int cmp_group_p(group_p *group1, group_p *group2); extern int cmp_notify_p(notify_p *notify1, notify_p *notify2); extern int get_active_num(void); extern void resync_active_file(void); extern int find_group_index(char *group); extern int parse_active_line(char *line, long *max, long *min, char *moderated); extern int parse_newsrc_active_line(FILE *fp, long *max, long *min, char *moderated, char *group); extern void read_news_active_file(void); extern void backup_active(int create); extern void check_for_any_new_groups(void); extern int prompt_subscribe_group(char *group, char *autosubscribe, char *autounsubscribe); extern int match_group_list(char *group, char *group_list); extern void set_default_attributes(void); extern void read_attributes_file(void); extern void write_attributes_file(void); extern void read_active_times_file(void); extern void write_active_times_file(void); extern void load_active_size_info(char *info); extern int find_active_size_index(char *cur_active_server); extern void read_motd_file(void); extern char *my_strpbrk(char *str1, char *str2); /* amiga.c */ /* art.c */ extern void find_base(int index); extern int num_of_arts(void); extern int purge_needed(char *group_path); extern int index_group(char *group, char *group_path); extern int read_group(char *group, char *group_path); extern void make_threads(int rethread); extern int parse_headers(char *buf, struct t_article *h); extern void write_xindex_file(char *group); extern void read_xindex_file(char *group_name); extern int read_xover_file(char *group_name, long min, long max); extern int find_index_file(char *group); extern void do_update(void); extern int artnum_comp(t_comptype *p1, t_comptype *p2); extern int subj_comp(t_comptype *p1, t_comptype *p2); extern int from_comp(t_comptype *p1, t_comptype *p2); extern int date_comp(t_comptype *p1, t_comptype *p2); extern void set_article(struct t_article *art); extern int input_pending(void); extern int valid_artnum(long art); /* curses.c */ extern void setup_screen(void); extern int InitScreen(void); extern void ScreenSize(int *num_lines, int *num_columns); extern void InitWin(void); extern void EndWin(void); extern void set_keypad_on(void); extern void set_keypad_off(void); extern void ClearScreen(void); extern void MoveCursor(int row, int col); extern void CleartoEOLN(void); extern void CleartoEOS(void); extern void StartInverse(void); extern void EndInverse(void); extern void ToggleInverse(void); extern int RawState(void); extern void Raw(int state); extern int ReadCh(void); extern int outchar(int c); extern void xclick(int state); extern void set_xclick_on(void); extern void set_xclick_off(void); /* debug.c */ extern void debug_nntp(char *func, char *line); extern void debug_nntp_respcode(int respcode); extern void debug_print_arts(void); extern void debug_print_header(struct t_article *s); extern void debug_save_comp(void); extern void debug_print_comment(char *comment); extern void debug_print_base(void); extern void debug_print_active(void); extern void debug_print_active_hash(void); /* envarg.c */ extern int count_args(char *s); extern void envargs(int *Pargc, char ***Pargv, char *envstr); /* feed.c */ extern void feed_articles(int function, int level, char *prompt, int respnum, char *group_path); extern int print_file(char *command, int respnum, int count); extern int get_post_proc_type(int proc_type); extern int does_article_exist(int function, long artnum, char *path); /* getline.c */ extern char *getline(char *prompt, int number_only, char *str); /* group.c */ extern void decr_tagged(int tag); extern void group_page(char *group); extern void fix_new_highest(int groupnum); extern void show_group_page(void); extern void update_group_page(void); extern void draw_subject_arrow(void); extern void erase_subject_arrow(void); extern int prompt_subject_num(int ch, char *group); extern void clear_note_area(void); extern int find_new_pos(int old_top, long old_artnum, int cur_pos); extern void mark_screen(int level, int screen_row, int screen_col, char *value); extern void set_subj_from_size(int num_cols); extern void toggle_subject_from(void); extern void show_group_title(int clear_title); /* hashstr.c */ extern char *hash_str(char *s); extern struct t_hashnode *add_string(char *s); extern void hash_init(void); extern void hash_reclaim(void); /* help.c */ extern void show_info_page(int type, char *help[], char *title); extern void display_info_page(void); extern void show_mini_help(int level); extern void toggle_mini_help(int level); /* inews.c */ extern int submit_inews(char *name); extern void get_host_name(char *host_name); extern void get_user_info(char *user_name, char *full_name); extern void get_from_name(char *user_name, char *host_name, char *full_name, char *from_name); extern int submit_file(char *name); /* init.c */ extern void init_selfinfo(void); extern void set_tindir(void); extern int create_mail_save_dirs(void); extern char *GetFQDN(void); extern char *GetConfigValue(char *name); /* kill.c */ extern int read_kill_file(void); extern void write_kill_file(void); extern int kill_art_menu(char *group_name, int index); extern int unkill_all_articles(void); extern int kill_any_articles(int index); extern int auto_select_articles(int index); /* lang.c */ /* mail.c */ extern void read_mail_active_file(void); extern void write_mail_active_file(void); extern void read_mailgroups_file(void); extern void read_newsgroups_file(void); extern void read_groups_descriptions(FILE *fp, FILE *fp_save); /* main.c */ extern void main(int argc, char *argv[]); extern void read_cmd_line_options(int argc, char *argv[]); extern void usage(char *progname); extern int check_for_any_new_news(int check_any_unread, int start_any_unread); extern void save_or_mail_new_news(void); extern void update_index_files(void); extern void show_intro_page(void); extern int read_cmd_line_groups(void); /* memory.c */ extern void init_alloc(void); extern void expand_art(void); extern void expand_active(void); extern void expand_kill(void); extern void expand_save(void); extern void expand_spooldirs(void); extern void expand_active_size(void); extern void init_screen_array(int allocate); extern void free_all_arrays(void); extern void free_art_array(void); extern void free_attributes_array(void); extern void free_active_arrays(void); extern void free_kill_array(void); extern void free_save_array(void); extern void free_spooldirs_array(void); extern void free_active_size_array(void); extern char *my_malloc(unsigned size); extern char *my_realloc(char *p, unsigned size); /* misc.c */ extern void asfail(char *file, int line, char *cond); extern void copy_fp(FILE *fp_ip, FILE *fp_op, char *prefix); extern char *get_val(char *env, char *def); extern int invoke_editor(char *filename, int lineno); extern int invoke_ispell(char *nam); extern void shell_escape(void); extern void tin_done(int ret); extern int my_mkdir(char *path, int mode); extern int my_chdir(char *path); extern unsigned long hash_groupname(char *group); extern void rename_file(char *old_filename, char *new_filename); extern char *str_dup(char *str); extern int invoke_cmd(char *nam); extern void draw_percent_mark(long cur_num, long max_num); extern void set_real_uid_gid(void); extern void set_tin_uid_gid(void); extern void base_name(char *dirname, char *program); extern void mail_setup(void); extern int mail_check(void); extern void parse_from(char *from_line, char *eaddr, char *fname); extern long my_atol(char *s, int n); extern int my_stricmp(char *p, char *q); extern char *eat_re(char *s); extern long hash_s(char *s); extern void my_strncpy(char *p, char *q, int n); extern int untag_all_articles(void); extern char *str_str(char *text, char *pattern, int patlen); extern void get_author(int thread, int respnum, char *str); extern void toggle_inverse_video(void); extern int get_arrow_key(void); extern void create_index_lock_file(char *lock_file); extern int strfquote(char *group, int respnum, char *s, int maxsize, char *format); extern int strfeditor(char *editor, int linenum, char *filename, char *s, int maxsize, char *format); extern int strfpath(char *format, char *str, int maxsize, char *homedir, char *maildir, char *savedir, char *group); extern void get_cwd(char *buf); extern void make_group_path(char *name, char *path); extern void cleanup_tmp_files(void); extern void make_post_process_cmd(char *cmd, char *dir, char *file); /* newsrc.c */ extern int auto_subscribe_groups(void); extern void backup_newsrc(void); extern void read_newsrc(int sub_only); extern void write_newsrc(void); extern void rewrite_newsrc(void); extern void checknewsrc(int groupnum); extern void read_newsrc_line(char *group); extern void update_newsrc(char *group, int groupnum, int mark_unread); extern void subscribe(char *group, int ch, int num, int out_seq); extern void reset_newsrc(void); extern void delete_group(char *group); extern int undel_group(void); extern void mark_group_read(char *group, int groupnum); extern void parse_seq(char *s); extern int parse_unread(char *s, int groupnum); extern int get_line_unread(char *group, int groupnum); extern void print_newsrc_seq(FILE *fp, int groupnum); extern void print_seq(FILE *fp, int groupnum); extern int pos_group_in_newsrc(char *group, int pos); /* nntplib.c */ extern char *getserverbyfile(char *file); extern int server_init(char *machine, char *service, int port); extern int get_tcp_socket(char *machine, char *service, int port); extern int handle_server_response(int response, char *nntpserver); extern void put_server(char *string); extern int get_server(char *string, int size); extern void close_server(void); extern char *nntp_respcode(int respcode); /* open.c */ extern int nntp_open(void); extern void nntp_close(void); extern FILE *open_mail_active_fp(char *mode); extern FILE *open_news_active_fp(void); extern FILE *open_overview_fmt_fp(void); extern FILE *open_newgroups_fp(int index); extern FILE *open_motd_fp(char *motd_file_date); extern FILE *open_subscription_fp(void); extern FILE *open_mailgroups_fp(void); extern FILE *open_newsgroups_fp(void); extern FILE *open_xindex_fp(char *group_name); extern FILE *open_xover_fp(char *group_name, long min, long max); extern int stat_article(long art, char *group_path); extern char *open_art_header(long art); extern FILE *open_art_fp(char *group_path, long art); extern FILE *open_xhdr_fp(char *header, long min, long max); extern int base_comp(t_comptype *p1, t_comptype *p2); extern void setup_base(char *group, char *group_path); extern int get_respcode(void); extern int stuff_nntp(char *fnam); extern FILE *nntp_to_fp(void); extern void log_user(void); extern void authorization(char *server, char *authuser); /* os_2.c */ /* page.c */ extern int show_page(int respnum, int *threadnum, char *group, char *group_path); extern void redraw_page(int respnum, char *group); extern void show_note_page(int respnum, char *group); extern void show_mime_article(FILE *fp, struct t_article *art); extern void show_first_header(int respnum, char *group); extern void show_cont_header(int respnum); extern int art_open(long art, char *group_path); extern void art_close(void); extern int prompt_response(int ch, int respnum); extern void yank_to_addr(char *orig, char *addr); extern int show_last_page(void); extern int match_header(char *buf, char *pat, char *body, int len); /* parsdate.y */ extern int GetTimeInfo(TIMEINFO *Now); extern time_t parsedate(char *p, TIMEINFO *now); /* post.c */ extern int user_posted_messages(void); extern void update_art_posted_file(char *group, int action, char *subj); extern int check_article_to_be_posted(char *article); extern void setup_check_article_screen(int *init); extern void quick_post_article(void); extern int post_article(char *group, int *posted); extern int post_response(char *group, int respnum, int copy_text); extern int mail_to_someone(int respnum, char *address, int mail_to_poster, int confirm_to_mail, int *mailed_ok); extern int mail_bug_report(void); extern int mail_to_author(char *group, int respnum, int copy_text); extern void find_mail_header(int header, char *file, char *value); extern int delete_article(char *group, int respnum); extern int crosspost_article(char *group, int respnum); extern void insert_x_headers(char *infile); extern void find_reply_to_addr(int respnum, char *from_addr); extern void reread_active_after_posting(void); /* prompt.c */ extern int prompt_num(int ch, char *prompt); extern int prompt_string(char *prompt, char *buf); extern int prompt_menu_string(int line, int col, char *var); extern int prompt_yn(int line, char *prompt, int prompt_ch); extern void prompt_on_off(int row, int col, int *var, char *help_text, char *prompt_text); extern void continue_prompt(void); /* rcfile.c */ extern int read_rcfile(void); extern void write_rcfile(void); extern int change_rcfile(char *group, int kill_at_once); extern void show_rcfile_menu(void); extern void expand_rel_abs_pathname(int line, int col, char *str); extern void show_menu_help(char *help_message); extern int match_boolean(char *line, char *pat, int *dst); extern int match_number(char *line, char *pat, int *dst); extern int match_string(char *line, char *pat, char *dst, int dstlen); extern void quote_dash_to_space(char *s); extern char *quote_space_to_dash(char *s); /* save.c */ extern int check_start_save_any_news(int check_start_save); extern int save_art_to_file(int respnum, int index, int mailbox, char *filename); extern int save_thread_to_file(int is_mailbox, char *group_path); extern int save_regex_arts(int is_mailbox, char *group_path); extern int append_to_existing_file(int i); extern int create_path(char *path); extern int create_sub_dir(int i); extern void add_to_save_list(int index, struct t_article *article, int is_mailbox, int archive_save, char *path); extern void sort_save_list(void); extern int save_comp(t_comptype *p1, t_comptype *p2); extern char *save_filename(int i); extern char *get_first_savefile(void); extern char *get_last_savefile(void); extern int post_process_files(int proc_type_ch); extern void post_process_uud(int pp); extern void post_process_sh(void); extern char *get_archive_file(char *dir, char *ext); extern void delete_processed_files(void); extern void print_art_seperator_p5~ TIN-1_22.BCKd[SRC.TIN-1_22]PROTO.H;1?&}Bline(FILE *fp, int mailbox); /* screen.c */ extern void info_message(char *str); extern void wait_message(char *str); extern void error_message(char *template, char *str); extern void perror_message(char *template, char *str); extern void clear_message(void); extern void center_line(int line, int inverse, char *str); extern void draw_arrow(int line); extern void erase_arrow(int line); extern void show_title(char *title); extern void ring_bell(void); /* search.c */ extern int search_author(int index, int current_art, int forward); extern void search_group(int forward); extern void search_subject(int forward, char *group); extern int search_article(int forward); extern void make_lower(char *s, char *t); extern int search_body(char *group_path, int current_art); extern int search_art_body(char *group_path, struct t_article *art, char *pat, int len); extern void lcase(char *s); /* select.c */ extern void selection_index(int start_groupnum); extern void group_selection_page(void); extern int prompt_group_num(int ch); extern void erase_group_arrow(void); extern void draw_group_arrow(void); extern void yank_active_file(void); extern int choose_new_group(void); extern int add_group(char *s, int get_unread); extern int reposition_group(char *group, int default_num); extern void catchup_group(int goto_next_unread_group); extern void next_unread_group(int enter_group); extern void set_groupname_len(int all_groups); extern void toggle_my_groups(int only_unread_groups, char *group); extern void goto_next_group_on_screen(void); extern void strip_line(char *line, int len); /* sigfile.c */ extern void add_signature(FILE *fp, int flag); extern FILE *open_random_sig(char *sigdir); extern int thrashdir(char *sigdir); /* signal.c */ extern t_sigtype (*sigdisp(int sig, t_sigtype (*func)()))(); extern void set_signal_handlers(void); extern void set_alarm_signal(void); extern void set_alarm_clock_on(void); extern void set_alarm_clock_off(void); extern void signal_handler(int sig); extern int set_win_size(int *num_lines, int *num_cols); extern void set_signals_art(void); extern void set_signals_group(void); extern void set_signals_help(void); extern void set_signals_page(void); extern void set_signals_select(void); extern void set_signals_spooldir(void); extern void set_signals_thread(void); extern void art_suspend(int sig); extern void main_suspend(int sig); extern void select_suspend(int sig); extern void spooldir_suspend(int sig); extern void group_suspend(int sig); extern void help_suspend(int sig); extern void page_suspend(int sig); extern void thread_suspend(int sig); extern void rcfile_suspend(int sig); extern void art_resize(int sig); extern void main_resize(int sig); extern void select_resize(int sig); extern void spooldir_resize(int sig); extern void group_resize(int sig); extern void help_resize(int sig); extern void page_resize(int sig); extern void thread_resize(int sig); /* spooldir.c */ extern int spooldir_index(void); extern void show_spooldir_page(void); extern int prompt_spooldir_num(int ch); extern void erase_spooldir_arrow(void); extern void draw_spooldir_arrow(void); extern int load_spooldirs(void); extern void get_spooldir(void); extern int set_spooldir(char *name); /* strftime.c */ extern size_t my_strftime(char *s, size_t maxsize, char *format, struct tm *timeptr); /* thread.c */ extern int show_thread(int respnum, char *group, char *group_path); extern void show_thread_page(void); extern void update_thread_page(void); extern void draw_thread_arrow(void); extern void erase_thread_arrow(void); extern int prompt_thread_num(int ch); extern int new_responses(int thread); extern int which_thread(int n); extern int which_response(int n); extern int num_of_responses(int n); extern int stat_thread(int n, struct t_art_stat *sbuf); extern int next_response(int n); extern int next_thread(int n); extern int prev_response(int n); extern int choose_response(int i, int n); extern int next_unread(int n); extern int prev_unread(int n); /* wildmat.c */ extern int wildmat(char *text, char *p); /* win32.c */ /* xref.c */ extern int overview_xref_support(void); extern void mark_all_xref_read(struct t_article *art, char *group_path); #else /* active.c */ extern int cmp_group_p(); extern int cmp_notify_p(); extern int get_active_num(); extern void resync_active_file(); extern int find_group_index(); extern int parse_active_line(); extern int parse_newsrc_active_line(); extern void read_news_active_file(); extern void backup_active(); extern void check_for_any_new_groups(); extern int prompt_subscribe_group(); extern int match_group_list(); extern void set_default_attributes(); extern void read_attributes_file(); extern void write_attributes_file(); extern void read_active_times_file(); extern void write_active_times_file(); extern void load_active_size_info(); extern int find_active_size_index(); extern void read_motd_file(); extern char *my_strpbrk(); /* amiga.c */ /* art.c */ extern void find_base(); extern int num_of_arts(); extern int purge_needed(); extern int index_group(); extern int read_group(); extern void make_threads(); extern int parse_headers(); extern void write_xindex_file(); extern void read_xindex_file(); extern int read_xover_file(); extern int find_index_file(); extern void do_update(); extern int artnum_comp(); extern int subj_comp(); extern int from_comp(); extern int date_comp(); extern void set_article(); extern int input_pending(); extern int valid_artnum(); /* curses.c */ extern void setup_screen(); extern int InitScreen(); extern void ScreenSize(); extern void InitWin(); extern void EndWin(); extern void set_keypad_on(); extern void set_keypad_off(); extern void ClearScreen(); extern void MoveCursor(); extern void CleartoEOLN(); extern void CleartoEOS(); extern void StartInverse(); extern void EndInverse(); extern void ToggleInverse(); extern int RawState(); extern void Raw(); extern int ReadCh(); extern int outchar(); extern void xclick(); extern void set_xclick_on(); extern void set_xclick_off(); /* debug.c */ extern void debug_nntp(); extern void debug_nntp_respcode(); extern void debug_print_arts(); extern void debug_print_header(); extern void debug_save_comp(); extern void debug_print_comment(); extern void debug_print_base(); extern void debug_print_active(); extern void debug_print_active_hash(); /* envarg.c */ extern int count_args(); extern void envargs(); /* feed.c */ extern void feed_articles(); extern int print_file(); extern int get_post_proc_type(); extern int does_article_exist(); /* getline.c */ extern char *getline(); /* group.c */ extern void decr_tagged(); extern void group_page(); extern void fix_new_highest(); extern void show_group_page(); extern void update_group_page(); extern void draw_subject_arrow(); extern void erase_subject_arrow(); extern int prompt_subject_num(); extern void clear_note_area(); extern int find_new_pos(); extern void mark_screen(); extern void set_subj_from_size(); extern void toggle_subject_from(); extern void show_group_title(); /* hashstr.c */ extern char *hash_str(); extern struct t_hashnode *add_string(); extern void hash_init(); extern void hash_reclaim(); /* help.c */ extern void show_info_page(); extern void display_info_page(); extern void show_mini_help(); extern void toggle_mini_help(); /* inews.c */ extern int submit_inews(); extern void get_host_name(); extern void get_user_info(); extern void get_from_name(); extern int submit_file(); /* init.c */ extern void init_selfinfo(); extern void set_tindir(); extern int create_mail_save_dirs(); extern char *GetFQDN(); extern char *GetConfigValue(); /* kill.c */ extern int read_kill_file(); extern void write_kill_file(); extern int kill_art_menu(); extern int unkill_all_articles(); extern int kill_any_articles(); extern int auto_select_articles(); /* lang.c */ /* mail.c */ extern void read_mail_active_file(); extern void write_mail_active_file(); extern void read_mailgroups_file(); extern void read_newsgroups_file(); extern void read_groups_descriptions(); /* main.c */ extern void main(); extern void read_cmd_line_options(); extern void usage(); extern int check_for_any_new_news(); extern void save_or_mail_new_news(); extern void update_index_files(); extern void show_intro_page(); extern int read_cmd_line_groups(); /* memory.c */ extern void init_alloc(); extern void expand_art(); extern void expand_active(); extern void expand_kill(); extern void expand_save(); extern void expand_spooldirs(); extern void expand_active_size(); extern void init_screen_array(); extern void free_all_arrays(); extern void free_art_array(); extern void free_attributes_array(); extern void free_active_arrays(); extern void free_kill_array(); extern void free_save_array(); extern void free_spooldirs_array(); extern void free_active_size_array(); extern char *my_malloc(); extern char *my_realloc(); /* misc.c */ extern void asfail(); extern void copy_fp(); extern char *get_val(); extern int invoke_editor(); extern int invoke_ispell(); extern void shell_escape(); extern void tin_done(); extern int my_mkdir(); extern int my_chdir(); extern unsigned long hash_groupname(); extern void rename_file(); extern char *str_dup(); extern int invoke_cmd(); extern void draw_percent_mark(); extern void set_real_uid_gid(); extern void set_tin_uid_gid(); extern void base_name(); extern void mail_setup(); extern int mail_check(); extern void parse_from(); extern long my_atol(); extern int my_stricmp(); extern char *eat_re(); extern long hash_s(); extern void my_strncpy(); extern int untag_all_articles(); extern char *str_str(); extern void get_author(); extern void toggle_inverse_video(); extern int get_arrow_key(); extern void create_index_lock_file(); extern int strfquote(); extern int strfeditor(); extern int strfpath(); extern void get_cwd(); extern void make_group_path(); extern void cleanup_tmp_files(); extern void make_post_process_cmd(); /* newsrc.c */ extern int auto_subscribe_groups(); extern void backup_newsrc(); extern void read_newsrc(); extern void write_newsrc(); extern void rewrite_newsrc(); extern void checknewsrc(); extern void read_newsrc_line(); extern void update_newsrc(); extern void subscribe(); extern void reset_newsrc(); extern void delete_group(); extern int undel_group(); extern void mark_group_read(); extern void parse_seq(); extern int parse_unread(); extern int get_line_unread(); extern void print_newsrc_seq(); extern void print_seq(); extern int pos_group_in_newsrc(); /* nntplib.c */ extern char *getserverbyfile(); extern int server_init(); extern int get_tcp_socket(); extern int handle_server_response(); extern void put_server(); extern int get_server(); extern void close_server(); extern char *nntp_respcode(); /* open.c */ extern int nntp_open(); extern void nntp_close(); extern FILE *open_mail_active_fp(); extern FILE *open_news_active_fp(); extern FILE *open_overview_fmt_fp(); extern FILE *open_newgroups_fp(); extern FILE *open_motd_fp(); extern FILE *open_subscription_fp(); extern FILE *open_mailgroups_fp(); extern FILE *open_newsgroups_fp(); extern FILE *open_xindex_fp(); extern FILE *open_xover_fp(); extern int stat_article(); extern char *open_art_header(); extern FILE *open_art_fp(); extern FILE *open_xhdr_fp(); extern int base_comp(); extern void setup_base(); extern int get_respcode(); extern int stuff_nntp(); extern FILE *nntp_to_fp(); extern void log_user(); extern void authorization(); /* os_2.c */ /* page.c */ extern int show_page(); extern void redraw_page(); extern void show_note_page(); extern void show_mime_article(); extern void show_first_header(); extern void show_cont_header(); extern int art_open(); extern void art_close(); extern int prompt_response(); extern void yank_to_addr(); extern int show_last_page(); extern int match_header(); /* parsdate.y */ extern int GetTimeInfo(); extern time_t parsedate(); /* post.c */ extern int user_posted_messages(); extern void update_art_posted_file(); extern int check_article_to_be_posted(); extern void setup_check_article_screen(); extern void quick_post_article(); extern int post_article(); extern int post_response(); extern int mail_to_someone(); extern int mail_bug_report(); extern int mail_to_author(); extern void find_mail_header(); extern int delete_article(); extern int crosspost_article(); extern void insert_x_headers(); extern void find_reply_to_addr(); extern void reread_active_after_posting(); /* prompt.c */ extern int prompt_num(); extern int prompt_string(); extern int prompt_menu_string(); extern int prompt_yn(); extern void prompt_on_off(); extern void continue_prompt(); /* rcfile.c */ extern int read_rcfile(); extern void write_rcfile(); extern int change_rcfile(); extern void show_rcfile_menu(); extern void expand_rel_abs_pathname(); extern void show_menu_help(); extern int match_boolean(); extern int match_number(); extern int match_string(); extern void quote_dash_to_space(); extern char *quote_space_to_dash(); /* save.c */ extern int check_start_save_any_news(); extern int save_art_to_file(); extern int save_thread_to_file(); extern int save_regex_arts(); extern int append_to_existing_file(); extern int create_path(); extern int create_sub_dir(); extern void add_to_save_list(); extern void sort_save_list(); extern int save_comp(); extern char *save_filename(); extern char *get_first_savefile(); extern char *get_last_savefile(); extern int post_process_files(); extern void post_process_uud(); extern void post_process_sh(); extern char *get_archive_file(); extern void delete_processed_files(); extern void print_art_seperator_line(); /* screen.c */ extern void info_message(); extern void wait_message(); extern void error_message(); extern void perror_message(); extern void clear_message(); extern void center_line(); extern void draw_arrow(); extern void erase_arrow(); extern void show_title(); extern void ring_bell(); /* search.c */ extern int search_author(); extern void search_group(); extern void search_subject(); extern int search_article(); extern void make_lower(); extern int search_body(); extern int search_art_body(); extern void lcase(); /* select.c */ extern void selection_index(); extern void group_selection_page(); extern int prompt_group_num(); extern void erase_group_arrow(); extern void draw_group_arrow(); extern void yank_active_file(); extern int choose_new_group(); extern int add_group(); extern int reposition_group(); extern void catchup_group(); extern void next_unread_group(); extern void set_groupname_len(); extern void toggle_my_groups(); extern void goto_next_group_on_screen(); extern void strip_line(); /* sigfile.c */ extern void add_signature(); extern FILE *open_random_sig(); extern int thrashdir(); /* signal.c */ extern t_sigtype (*sigdisp())(); extern void set_signal_handlers(); extern void set_alarm_signal(); extern void set_alarm_clock_on(); extern void set_alarm_clock_off(); extern void signal_handler(); extern int set_win_size(); extern void set_signals_art(); extern void set_signals_group(); extern void set_signals_help(); extern void set_signals_page(); extern void set_signals_select(); extern void set_signals_spooldir(); extern void set_signals_thread(); extern void art_suspend(); extern void main_suspend(); extern void select_suspend(); extern void spooldir_suspend(); extern void group_suspend(); extern void help_suspend(); extern void page_suspend(); extern void thread_suspend(); extern void rcfile_suspend(); extern void art_resize(); extern void main_resize(); extern void select_resize(); extern void spooldir_resize(); extern void group_resize(); extern void help_resize(); extern void page_resize(); extern void thread_resize(); /* spooldir.c */ extern int spooldir_index(); extern void show_spooldir_page(); extern int prompt_spooldir_num(); extern void erase_spooldir_arrow(); extern void draw_spooldir_arrow(); extern int load_spooldirs(); extern void get_spooldir(); extern int set_spooldir(); /* strftime.c */ extern size_t my_strftime(); /* thread.c */ extern int show_thread(); extern void show_thread_page(); extern void update_thread_page(); extern void draw_thread_arrow(); extern void erase_thread_arrow(); extern int prompt_thread_num(); extern int new_responses(); extern int which_thread(); extern int which_response(); extern int num_of_responses(); extern int stat_thread(); extern int next_response(); extern int next_thread(); extern int prev_response(); extern int choose_response(); extern int next_unread(); extern int prev_unread(); /* wildmat.c */ extern int wildmat(); /* win32.c */ /* xref.c */ extern int overview_xref_support(); extern void mark_all_xref_read(); #endif *[SRC.TIN-1_22]RCFILE.C;2+,.B// 4BBy-d0@123KPWOC56A@7`'A89]VG/HJ/* * Project : tin - a Usenet reader * Module : rcfile.c * Author : I.Lea * Created : 01-04-91 * Updated : 22-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" extern int index_point; static int COL1; static int COL2; static int COL3; /* * read_rcfile - read defaults from ~/.tin/tinrc */ int read_rcfile () { char active_size_info[PATH_LEN]; char buf[LEN]; FILE *fp; if ((fp = fopen (rcfile, "r")) == NULL) { return (FALSE); } while (fgets (buf, sizeof (buf), fp) != NULL) { if (buf[0] == '#' || buf[0] == '\n') { continue; } if (match_boolean (buf, "auto_save=", &default_auto_save)) { continue; } if (match_boolean (buf, "batch_save=", &default_batch_save)) { continue; } if (match_boolean (buf, "start_editor_offset=", &start_editor_offset)) { continue; } if (match_string (buf, "default_editor_format=", default_editor_format, sizeof (default_editor_format))) { continue; } if (match_boolean (buf, "mark_saved_read=", &mark_saved_read)) { continue; } if (match_boolean (buf, "inverse_okay=", &inverse_okay)) { continue; } if (match_boolean (buf, "draw_arrow=", &draw_arrow_mark)) { continue; } if (match_boolean (buf, "print_header=", &print_header)) { continue; } if (match_number (buf, "kill_level=", &kill_level)) { continue; } if (match_boolean (buf, "pos_first_unread=", &pos_first_unread)) { continue; } if (match_boolean (buf, "full_page_scroll=", &full_page_scroll)) { continue; } if (match_boolean (buf, "catchup_read_groups=", &catchup_read_groups)) { continue; } if (match_boolean (buf, "thread_articles=", &default_thread_arts)) { continue; } if (match_boolean (buf, "unlink_article=", &unlink_article)) { continue; } if (match_boolean (buf, "show_only_unread_groups=", &show_only_unread_groups)) { continue; } if (match_boolean (buf, "show_only_unread=", &default_show_only_unread)) { continue; } if (match_boolean (buf, "confirm_action=", &confirm_action)) { continue; } if (match_boolean (buf, "show_description=", &show_description)) { continue; } if (match_number (buf, "show_author=", &default_show_author)) { continue; } if (match_number (buf, "post_process_type=", &default_post_proc_type)) { proc_ch_default = get_post_proc_type (default_post_proc_type); continue; } if (match_number (buf, "sort_article_type=", &default_sort_art_type)) { continue; } if (match_string (buf, "default_savedir=", default_savedir, sizeof (default_savedir))) { if (default_savedir[0] == '.' && strlen (default_savedir) == 1) { get_cwd (buf); my_strncpy (default_savedir, buf, sizeof (default_savedir)); } continue; } if (match_string (buf, "default_maildir=", default_maildir, sizeof (default_maildir))) { continue; } if (match_string (buf, "default_printer=", default_printer, sizeof (default_printer))) { continue; } if (match_string (buf, "default_sigfile=", default_sigfile, sizeof (default_sigfile))) { continue; } if (match_string (buf, "quote_chars=", quote_chars, sizeof (quote_chars))) { quote_dash_to_space (quote_chars); continue; } if (match_string (buf, "unread_art_mark=", buf, sizeof (buf))) { unread_art_mark = buf[0]; continue; } if (match_string (buf, "hot_art_mark=", buf, sizeof (buf))) { hot_art_mark = buf[0]; continue; } if (match_string (buf, "return_art_mark=", buf, sizeof (buf))) { return_art_mark = buf[0]; continue; } if (match_number (buf, "reread_active_file_secs=", &reread_active_file_secs)) { continue; } if (match_boolean (buf, "show_last_line_prev_page=", &show_last_line_prev_page)) { continue; } if (match_boolean (buf, "tab_after_X_selection=", &tab_after_X_selection)) { continue; } if (match_boolean (buf, "tab_goto_next_unread=", &tab_goto_next_unread)) { continue; } if (match_boolean (buf, "force_screen_redraw=", &force_screen_redraw)) { continue; } if (match_boolean (buf, "display_reading_prompt=", &display_reading_prompt)) { continue; } if (match_boolean (buf, "save_to_mmdf_mailbox=", &save_to_mmdf_mailbox)) { continue; } if (match_boolean (buf, "use_builtin_inews=", &use_builtin_inews)) { continue; } if (match_string (buf, "default_spooldir_alias=", spooldir_alias, sizeof (spooldir_alias))) { continue; } if (match_string (buf, "news_quote_format=", news_quote_format, sizeof (news_quote_format))) { continue; } if (match_string (buf, "mail_quote_format=", mail_quote_format, sizeof (mail_quote_format))) { continue; } #ifdef HAVE_KEYPAD if (match_boolean (buf, "use_keypad=", &use_keypad)) { continue; } #endif if (match_boolean (buf, "slow_speed_terminal=", &slow_speed_terminal)) { continue; } if (match_number (buf, "groupname_max_length=", &groupname_max_length)) { continue; } if (match_boolean (buf, "beginner_level=", &beginner_level)) { continue; } if (match_boolean (buf, "auto_cc=", &auto_cc)) { continue; } if (match_string (buf, "default_author_search=", default_author_search, sizeof (default_author_search))) { continue; } if (match_string (buf, "default_goto_group=", default_goto_group, sizeof (default_goto_group))) { continue; } if (match_string (buf, "default_group_search=", default_group_search, sizeof (default_group_search))) { continue; } if (match_string (buf, "default_subject_search=", default_subject_search, sizeof (default_subject_search))) { continue; } if (match_string (buf, "default_art_search=", default_art_search, sizeof (default_art_search))) { continue; } if (match_string (buf, "default_crosspost_group=", default_crosspost_group, sizeof (default_crosspost_group))) { continue; } if (match_string (buf, "default_mail_address=", default_mail_address, sizeof (default_mail_address))) { continue; } if (match_number (buf, "default_move_group=", &default_move_group)) { continue; } if (match_string (buf, "default_pipe_command=", default_pipe_command, sizeof (default_pipe_command))) { continue; } if (match_string (buf, "default_post_newsgroups=", default_post_newsgroups, sizeof (default_post_newsgroups))) { continue; } if (match_string (buf, "default_post_subject=", default_post_subject, sizeof (default_post_subject))) { continue; } if (match_string (buf, "default_regex_pattern=", default_regex_pattern, sizeof (default_regex_pattern))) { continue; } if (match_string (buf, "default_save_file=", default_save_file, sizeof (default_save_file))) { continue; } if (match_string (buf, "default_select_pattern=", default_select_pattern, sizeof (default_select_pattern))) { continue; } if (match_string (buf, "default_shell_command=", default_shell_command, sizeof (default_shell_command))) { continue; } if (match_string (buf, "motd_file_info=", motd_file_info, sizeof (motd_file_info))) { continue; } if (match_string (buf, "active_file_info=", active_size_info, sizeof (active_size_info))) { load_active_size_info (active_size_info); continue; } } fclose (fp); return TRUE; } /* * write_rcfile - write defaults to ~/.tin/tinrc */ void write_rcfile () { FILE *fp; int i; #ifdef VMS if ((fp = fopen (rcfile, "w", "fop=cif")) == NULL) { #else if ((fp = fopen (rcfile, "w")) == NULL) { #endif return; } if (! cmd_line) { wait_message (txt_saving); } if (! default_editor_format[0]) { strcpy (default_editor_format, EDITOR_FORMAT_ON); } fprintf (fp, "# if ON articles/threads with Archive-name: in mail header will\n"); fprintf (fp, "# be automatically saved with the Archive-name & part/patch no.\n"); fprintf (fp, "auto_save=%s\n\n", (default_auto_save ? "ON" : "OFF")); fprintf (fp, "# if ON articles/threads will be saved in batch mode when save -S\n"); fprintf (fp, "# or mail -M is specified on the command line\n"); fprintf (fp, "batch_save=%s\n\n", (default_batch_save ? "ON" : "OFF")); fprintf (fp, "# if ON editor will be started with cursor offset into the file\n"); fprintf (fp, "# otherwise the cursor will be positioned at the first line\n"); fprintf (fp, "start_editor_offset=%s\n\n", (start_editor_offset ? "ON" : "OFF")); fprintf (fp, "# Format of editor line including parameters\n"); fprintf (fp, "# %%E Editor %%F Filename %%N Linenumber\n"); fprintf (fp, "default_editor_format=%s\n\n", default_editor_format); fprintf (fp, "# if ON mark articles that are saved as read\n"); fprintf (fp, "mark_saved_read=%s\n\n", (mark_saved_read ? "ON" : "OFF")); fprintf (fp, "# if 0 killed articles are simply marked as being read\n"); fprintf (fp, "# if 1 killed articles are removed and never seen\n"); fprintf (fp, "kill_level=%d\n\n", kill_level); fprintf (fp, "# if ON use inverse video for page headers at different levels\n"); fprintf (fp, "inverse_okay=%s\n\n", (inverse_okay ? "ON" : "OFF")); fprintf (fp, "# if ON use -> otherwise highlighted bar for selection\n"); fprintf (fp, "draw_arrow=%s\n\n", (draw_arrow_mark ? "ON" : "OFF")); fprintf (fp, "# if ON print all of mail header otherwise Subject: & From: lines\n"); fprintf (fp, "print_header=%s\n\n", (print_header ? "ON" : "OFF")); fprintf (fp, "# if ON put cursor at first unread art in group otherwise last art\n"); fprintf (fp, "pos_first_unread=%s\n\n", (pos_first_unread ? "ON" : "OFF")); fprintf (fp, "# if ON scroll full page of groups/articles otherwise half a page\n"); fprintf (fp, "full_page_scroll=%s\n\n", (full_page_scroll ? "ON" : "OFF")); fprintf (fp, "# if ON ask user if read groups should all be marked read\n"); fprintf (fp, "catchup_read_groups=%s\n\n", (catchup_read_groups ? "ON" : "OFF")); fprintf (fp, "# if ON confirm certain commands with y/n before executing\n"); fprintf (fp, "confirm_action=%s\n\n", (confirm_action ? "ON" : "OFF")); fprintf (fp, "# if ON show group description text after newsgroup name at\n"); fprintf (fp, "# group selection level\n"); fprintf (fp, "show_description=%s\n\n", (show_description ? "ON" : "OFF")); fprintf (fp, "# part of from field to display 0) none 1) address 2) full name 3) both\n"); fprintf (fp, "show_author=%d\n\n", default_show_author); fprintf (fp, "# type of post processing to perform after saving articles.\n"); #ifdef M_AMIGA fprintf (fp, "# 0=(none) 1=(unshar) 2=(uudecode) 3=(uudecode & list lha)\n"); fprintf (fp, "# 4=(uud & extract lha) 5=(uud & list zip) 6=(uud & extract zip)\n"); #else fprintf (fp, "# 0=(none) 1=(unshar) 2=(uudecode) 3=(uudecode & list zoo)\n"); fprintf (fp, "# 4=(uud & extract zoo) 5=(uud & list zip) 6=(uud & extract zip)\n"); #endif fprintf (fp, "post_process_type=%d\n\n", default_post_proc_type); fprintf (fp, "# if ON all group will be threaded as default.\n"); fprintf (fp, "thread_articles=%s\n\n", (default_thread_arts ? "ON" : "OFF")); fprintf (fp, "# if ON remove ~/.article after posting.\n"); fprintf (fp, "unlink_article=%s\n\n", (unlink_article ? "ON" : "OFF")); fprintf (fp, "# if ON show only subscribed to groups that contain unread articles.\n"); fprintf (fp, "show_only_unread_groups=%s\n\n", (show_only_unread_groups ? "ON" : "OFF")); fprintf (fp, "# if ON show only new/unread articles otherwise show all.\n"); fprintf (fp, "show_only_unread=%s\n\n", (default_show_only_unread ? "ON" : "OFF")); fprintf (fp, "# sort articles by 0=(nothing) 1=(Subject descend) 2=(Subject ascend)\n"); fprintf (fp, "# 3=(From descend) 4=(From ascend) 5=(Date descend) 6=(Date ascend).\n"); fprintf (fp, "sort_article_type=%d\n\n", default_sort_art_type); fprintf (fp, "# directory where articles/threads are saved\n"); fprintf (fp, "default_savedir=%s\n\n", default_savedir); fprintf (fp, "# (-m) directory where articles/threads are saved in mailbox format\n"); fprintf (fp, "default_maildir=%s\n\n", default_maildir); fprintf (fp, "# print program with parameters used to print articles/threads\n"); fprintf (fp, "default_printer=%s\n\n", default_printer); fprintf (fp, "# Signature path (random sigs)/file to be used when posting/replying to messages\n"); fprintf (fp, "default_sigfile=%s\n\n", default_sigfile); fprintf (fp, "# time interval in seconds between rereading the active file\n"); fprintf (fp, "reread_active_file_secs=%d\n\n", reread_active_file_secs); fprintf (fp, "# characters used in quoting to followups and replys. '_' replaced by ' '\n"); fprintf (fp, "quote_chars=%s\n\n", quote_space_to_dash (quote_chars)); fprintf (fp, "# character used to show that an art was unread (default '+')\n"); fprintf (fp, "unread_art_mark=%c\n\n", unread_art_mark); fprintf (fp, "# character used to show that an art was auto-selected (default '*')\n"); fprintf (fp, "hot_art_mark=%c\n\n", hot_art_mark); fprintf (fp, "# character used to show that an art will return (default '-')\n"); fprintf (fp, "return_art_mark=%c\n\n", return_art_mark); fprintf (fp, "# if ON show the last line of the previous page as first line of next page\n"); fprintf (fp, "show_last_line_prev_page=%s\n\n", (show_last_line_prev_page ? "ON" : "OFF")); fprintf (fp, "# if ON a TAB command will be automatically done after the X command\n"); fprintf (fp, "tab_after_X_selection=%s\n\n", (tab_after_X_selection ? "ON" : "OFF")); fprintf (fp, "# if ON the TAB command will goto next unread article at article viewer level\n"); fprintf (fp, "tab_goto_next_unread=%s\n\n", (tab_goto_next_unread ? "ON" : "OFF")); fprintf (fp, "# if ON a screen redraw will always be done after certain external commands\n"); fprintf (fp, "force_screen_redraw=%s\n\n", (force_screen_redraw ? "ON" : "OFF")); fprintf (fp, "# if ON 'Reading...' will be displayed when reading article from NNTP server\n"); fprintf (fp, "display_reading_prompt=%s\n\n", (display_reading_prompt ? "ON" : "OFF")); fprintf (fp, "# if ON save mail to a MMDF style mailbox (default is normal mbox format)\n"); fprintf (fp, "save_to_mmdf_mailbo;I$~ TIN-1_22.BCKd[SRC.TIN-1_22]RCFILE.C;2BLx=%s\n\n", (save_to_mmdf_mailbox ? "ON" : "OFF")); fprintf (fp, "# if ON use the builtin mini inews otherwise use an external inews program\n"); fprintf (fp, "use_builtin_inews=%s\n\n", (use_builtin_inews ? "ON" : "OFF")); fprintf (fp, "# Format of quote line when mailing/posting/followingup an article\n"); fprintf (fp, "# %%A Address %%D Date %%F Addr+Name %%G Groupname %%M MessageId %%N Name\n"); fprintf (fp, "news_quote_format=%s\n", news_quote_format); fprintf (fp, "mail_quote_format=%s\n\n", mail_quote_format); fprintf (fp, "# if ON automatically put your name in the Cc: field when mailing an article\n"); fprintf (fp, "auto_cc=%s\n\n", (auto_cc ? "ON" : "OFF")); #ifdef HAVE_KEYPAD fprintf (fp, "# If ON enable scroll keys on terminals that support it\n"); fprintf (fp, "use_keypad=%s\n\n", (use_keypad ? "ON" : "OFF")); #endif fprintf (fp, "# If ON strip blanks from end of lines to speedup display on slow terminals\n"); fprintf (fp, "slow_speed_terminal=%s\n\n", (slow_speed_terminal ? "ON" : "OFF")); fprintf (fp, "# Maximum length of the names of newsgroups displayed\n"); fprintf (fp, "groupname_max_length=%d\n\n", groupname_max_length); fprintf (fp, "# If ON show a mini menu of useful commands at each level\n"); fprintf (fp, "beginner_level=%s\n\n", (beginner_level ? "ON" : "OFF")); fprintf (fp, "# default action/prompt strings\n"); fprintf (fp, "default_spooldir_alias=%s\n", spooldir_alias); fprintf (fp, "default_author_search=%s\n", default_author_search); fprintf (fp, "default_goto_group=%s\n", default_goto_group); fprintf (fp, "default_group_search=%s\n", default_group_search); fprintf (fp, "default_subject_search=%s\n", default_subject_search); fprintf (fp, "default_art_search=%s\n", default_art_search); fprintf (fp, "default_crosspost_group=%s\n", default_crosspost_group); fprintf (fp, "default_mail_address=%s\n", default_mail_address); fprintf (fp, "default_move_group=%d\n", default_move_group); fprintf (fp, "default_pipe_command=%s\n", default_pipe_command); fprintf (fp, "default_post_newsgroups=%s\n", default_post_newsgroups); fprintf (fp, "default_post_subject=%s\n", default_post_subject); fprintf (fp, "default_regex_pattern=%s\n", default_regex_pattern); fprintf (fp, "default_save_file=%s\n", default_save_file); fprintf (fp, "default_select_pattern=%s\n", default_select_pattern); fprintf (fp, "default_shell_command=%s\n\n", default_shell_command); fprintf (fp, "# news motd file dates from server used for detecting new motd info\n"); fprintf (fp, "motd_file_info=%s\n\n", motd_file_info); fprintf (fp, "# active file sizes/dates from different servers used for detecting new groups\n"); if (! num_active_size) { fprintf (fp, "active_file_info=%s[%s]\n", new_active_file_server, new_active_file_attribute); } else { for (i = 0 ; i < num_active_size ; i++) { fprintf (fp, "active_file_info=%s[%s]\n", active_size[i].server, active_size[i].attribute); } } fclose (fp); chmod (rcfile, 0600); } /* * options menu so that the user can dynamically change parameters */ int change_rcfile (group, kill_at_once) char *group; int kill_at_once; { char *str = (char *) 0; int ch, i; int kill_changed = FALSE; int orig_show_only_unread; int orig_thread_arts; int index; int option; int ret_code = NO_KILLING; int var_orig; #ifdef SIGTSTP t_sigtype (*susp)(); susp = (t_sigtype (*)()) 0; if (do_sigtstp) { susp = sigdisp (SIGTSTP, SIG_DFL); } #endif COL1 = 0; COL2 = ((cCOLS / 3) * 1) + 1; COL3 = ((cCOLS / 3) * 2) + 2; show_rcfile_menu (); set_xclick_off (); while (1) { #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, rcfile_suspend); } #endif MoveCursor (cLINES, 0); ch = ReadCh (); if (ch >= '1' && ch <= '9') { option = prompt_num (ch, "Enter option number> "); } else { if (ch == 'q' || ch == ESC) { option = -1; } else { option = 0; } } #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, SIG_IGN); } #endif switch (option) { case 0: write_rcfile (); /* FALLTHRU */ case -1: if (kill_changed) { if (kill_at_once) { index = my_group[cur_groupnum]; killed_articles = read_kill_file (); if (killed_articles) { if (kill_any_articles (index)) { make_threads (FALSE); find_base (index); } } else { if (unkill_all_articles ()) { make_threads (FALSE); find_base (index); } } } ret_code = KILLING; } clear_note_area (); #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, susp); } #endif return ret_code; case 1: /* auto save */ prompt_on_off (INDEX_TOP, COL1, &default_auto_save, txt_help_autosave, txt_opt_autosave); break; case 2: /* start editor with line offset */ prompt_on_off (INDEX_TOP, COL2, &start_editor_offset, txt_help_start_editor_offset, txt_opt_start_editor_offset); break; case 3: /* mark saved articles read */ prompt_on_off (INDEX_TOP, COL3, &mark_saved_read, txt_help_mark_saved_read, txt_opt_mark_saved_read); break; case 4: /* confirm action */ prompt_on_off (INDEX_TOP+2, COL1, &confirm_action, txt_help_confirm_action, txt_opt_confirm_action); break; case 5: /* draw -> / highlighted bar */ prompt_on_off (INDEX_TOP+2, COL2, &draw_arrow_mark, txt_help_draw_arrow, txt_opt_draw_arrow); if (draw_arrow_mark == FALSE && inverse_okay == FALSE) { inverse_okay = TRUE; } break; case 6: /* print header */ prompt_on_off (INDEX_TOP+2, COL3, &print_header, txt_help_print_header, txt_opt_print_header); break; case 7: /* position cursor at first / last unread art */ prompt_on_off (INDEX_TOP+4, COL1, &pos_first_unread, txt_help_pos_first_unread, txt_opt_pos_first_unread); break; case 8: /* scroll half/full page of groups/articles */ prompt_on_off (INDEX_TOP+4, COL2, &full_page_scroll, txt_help_page_scroll, txt_opt_page_scroll); break; case 9: /* catchup read groups when quitting */ prompt_on_off (INDEX_TOP+4, COL3, &catchup_read_groups, txt_help_catchup_groups, txt_opt_catchup_groups); break; case 10: /* thread/unthread all groups except those in ~/.tin/unthreaded */ orig_thread_arts = default_thread_arts; prompt_on_off (INDEX_TOP+6, COL1, &default_thread_arts, txt_help_thread_arts, txt_opt_thread_arts); if (default_thread_arts != orig_thread_arts || group != (char *) 0) { make_threads (TRUE); find_base (my_group[cur_groupnum]); } clear_message (); break; case 11: /* show all arts or just new/unread arts */ orig_show_only_unread = default_show_only_unread; prompt_on_off (INDEX_TOP+6, COL2, &default_show_only_unread, txt_help_show_only_unread, txt_opt_show_only_unread); if (default_show_only_unread != orig_show_only_unread || group != (char *) 0) { make_threads (TRUE); find_base (my_group[cur_groupnum]); if (space_mode) { for (i = 0; i < top_base; i++) { if (new_responses (i)) { break; } } if (i < top_base) { index_point = i; } else { index_point = top_base - 1; } } else { index_point = top_base - 1; } } break; case 12: /* show newsgroup description text next to newsgroups */ prompt_on_off (INDEX_TOP+6, COL3, &show_description, txt_help_show_description, txt_opt_show_description); if (show_description) { /* force reread of newgroups file */ reread_active_file = TRUE; } else { set_groupname_len (FALSE); } break; case 13: /* show subject & author / subject only */ var_orig = default_show_author; show_menu_help (txt_help_show_author); do { MoveCursor (INDEX_TOP+8, COL1 + (int) strlen (txt_opt_show_author)); if ((ch = ReadCh()) == ' ') { if (default_show_author + 1 > SHOW_FROM_BOTH) { default_show_author = SHOW_FROM_NONE; } else { default_show_author++; } switch (default_show_author) { case SHOW_FROM_NONE: str = txt_show_from_none; break; case SHOW_FROM_ADDR: str = txt_show_from_addr; break; case SHOW_FROM_NAME: str = txt_show_from_name; break; case SHOW_FROM_BOTH: str = txt_show_from_both; break; } fputs (str, stdout); fflush (stdout); } } while (ch != CR && ch != ESC); if (ch == ESC) { /* restore original value */ default_show_author = var_orig; switch (default_show_author) { case SHOW_FROM_NONE: str = txt_show_from_none; break; case SHOW_FROM_ADDR: str = txt_show_from_addr; break; case SHOW_FROM_NAME: str = txt_show_from_name; break; case SHOW_FROM_BOTH: str = txt_show_from_both; break; } fputs (str, stdout); fflush (stdout); } #if 0 else { set_subj_from_size (cCOLS); } #endif break; case 14: var_orig = default_post_proc_type; show_menu_help (txt_help_post_proc_type); do { MoveCursor (INDEX_TOP+8, COL2 + (int) strlen (txt_opt_process_type)); if ((ch = ReadCh()) == ' ') { if (default_post_proc_type + 1 > POST_PROC_UUD_EXT_ZIP) { default_post_proc_type = POST_PROC_NONE; } else { default_post_proc_type++; } proc_ch_default = get_post_proc_type (default_post_proc_type); switch (default_post_proc_type) { case POST_PROC_NONE: str = txt_post_process_none; break; case POST_PROC_SHAR: str = txt_post_process_sh; break; case POST_PROC_UUDECODE: str = txt_post_process_uudecode; break; case POST_PROC_UUD_LST_ZOO: str = txt_post_process_uud_lst_zoo; break; case POST_PROC_UUD_EXT_ZOO: str = txt_post_process_uud_ext_zoo; break; case POST_PROC_UUD_LST_ZIP: str = txt_post_process_uud_lst_zip; break; case POST_PROC_UUD_EXT_ZIP: str = txt_post_process_uud_ext_zip; break; } CleartoEOLN (); fputs (str, stdout); fflush (stdout); } } while (ch != CR && ch != ESC); if (ch == ESC) { /* restore original value */ default_post_proc_type = var_orig; switch (default_post_proc_type) { case POST_PROC_NONE: str = txt_post_process_none; proc_ch_default = 'n'; break; case POST_PROC_SHAR: str = txt_post_process_sh; proc_ch_default = 's'; break; case POST_PROC_UUDECODE: str = txt_post_process_uudecode; proc_ch_default = 'u'; break; case POST_PROC_UUD_LST_ZOO: str = txt_post_process_uud_lst_zoo; proc_ch_default = '1'; break; case POST_PROC_UUD_EXT_ZOO: str = txt_post_process_uud_ext_zoo; proc_ch_default = '2'; break; case POST_PROC_UUD_LST_ZIP: str = txt_post_process_uud_lst_zip; proc_ch_default = '3'; break; case POST_PROC_UUD_EXT_ZIP: str = txt_post_process_uud_ext_zip; proc_ch_default = '4'; break; } CleartoEOLN (); fputs (str, stdout); fflush (stdout); } break; case 15: var_orig = default_sort_art_type; show_menu_help (txt_help_sort_type); do { MoveCursor (INDEX_TOP+10, COL1 + (int) strlen (txt_opt_sort_type)); if ((ch = ReadCh()) == ' ') { if (default_sort_art_type + 1 > SORT_BY_DATE_ASCEND) { default_sort_art_type = SORT_BY_NOTHING; } else { default_sort_art_type++; } switch (default_sort_art_type) { case SORT_BY_NOTHING: str = txt_sort_by_nothing; break; case SORT_BY_SUBJ_DESCEND: str = txt_sort_by_subj_descend; break; case SORT_BY_SUBJ_ASCEND: str = txt_sort_by_subj_ascend; break; case SORT_BY_FROM_DESCEND: str = txt_sort_by_from_descend; break; case SORT_BY_FROM_ASCEND: str = txt_sort_by_from_ascend; break; case SORT_BY_DATE_DESCEND: str = txt_sort_by_date_descend; break; case SORT_BY_DATE_ASCEND: str = txt_sort_by_date_ascend; break; } CleartoEOLN (); fputs (str, stdout); fflush (stdout); } } while (ch != CR && ch != ESC); if (ch == ESC) { /* restore original value */ default_sort_art_type = var_orig; switch (default_sort_art_type) { case SORT_BY_NOTHING: str = txt_sort_by_nothing; break; case SORT_BY_SUBJ_DESCEND: str = txt_sort_by_subj_descend; break; case SORT_BY_SUBJ_ASCEND: str = txt_sort_by_subj_ascend; break; case SORT_BY_FROM_DESCEND: str = txt_sort_by_from_descend; break; case SORT_BY_FROM_ASCEND: str = txt_sort_by_from_ascend; break; case SORT_BY_DATE_DESCEND: str = txt_sort_by_date_descend; break; case SORT_BY_DATE_ASCEND: str = txt_sort_by_date_ascend; break; } CleartoEOLN (); fputs (str, stdout); fflush (stdout); } break; #ifndef AMIGA_BBS case 16: show_menu_help (txt_help_savedir); prompt_menu_string (INDEX_TOP+12, COL1 + (int) strlen (txt_opt_savedir), default_savedir); expand_rel_abs_pathname (INDEX_TOP+12, COL1 + (int) strlen (txt_opt_savedir), default_savedir); break; case 17: show_menu_help (txt_help_maildir); prompt_menu_string (INDEX_TOP+14, COL1 + (int) strlen (txt_opt_maildir), default_maildir); expand_rel_abs_pathname (INDEX_TOP+14, COL1 + (int) strlen (txt_opt_maildir), default_maildir); break; case 18: show_menu_help (txt_help_printer); prompt_menu_string (INDEX_TOP+16, COL1 + (int) strlen (txt_opt_printer), default_printer); expand_rel_abs_pathname (INDEX_TOP+16, COL1 + (int) strlen (txt_opt_printer), default_printer); break; #endif /* AMIGA_BBS */ } show_menu_help (txt_select_rcfile_option); } } void show_rcfile_menu () { char *str = (char *) 0; ClearScreen (); center_line (0, TRUE, txt_options_menu); MoveCursor (INDEX_TOP, 0); printf ("%s%s\r\n\r\n", txt_opt_autosave, (default_auto_save ? "ON " : "OFF")); printf ("%s%s\r\n\r\n", txt_opt_confirm_action, (confirm_action ? "ON " : "OFF")); printf ("%s%s\r\n\r\n", txt_opt_pos_first_unread, (pos_first_unread ? "ON " : "OFF")); printf ("%s%s", txt_opt_thread_arts, (default_thread_arts ? "ON " : "OFF")); MoveCursor(INDEX_TOP, COL2); printf ("%s%s", txt_opt_start_editor_offset, (start_editor_offset ? "ON " : "OFF")); MoveCursor(INDEX_TOP+2, COL2); printf ("%s%s", txt_opt_draw_arrow, (draw_arrow_mark ? "ON " : "OFF")); MoveCursor(INDEX_TOP+4, COL2); printf ("%s%s", txt_opt_page_scroll, (full_page_scroll ? "ON " : "OFF")); MoveCursor(INDEX_TOP+6, COL2); printf ("%s%s", txt_opt_show_only_unread, (default_show_only_unread ? "ON " : "OFF")); MoveCursor(INDEX_TOP, COL3); printf ("%s%s", txt_opt_mark_saved_read, (mark_saved_read ? "ON " : "OFF")); MoveCursor(INDEX_TOP+2, COL3); printf ("%s%s", txt_opt_print_header, (print_header ? "ON " : "OFF")); MoveCursor(INDEX_TOP+4, COL3); printf ("%s%s", txt_opt_catchup_groups, (catchup_read_groups ? "ON " : "OFF")); MoveCursor(INDEX_TOP+6, COL3); printf ("%s%s", txt_opt_show_description, (show_description ? "ON " : "OFF")); MoveCursor(INDEX_TOP+8, COL1); switch (default_show_author) { case SHOW_FROM_NONE: str = txt_show_from_none; break; case SHOW_FROM_ADDR: str = txt_show_from_addr; break; case SHOW_FROM_NAME: str = txt_show_from_name; break; case SHOW_FROM_BOTH: str = txt_show_from_both; break; } printf ("%s%s", txt_opt_show_author, str); MoveCursor(INDEX_TOP+8, COL2); switch (default_post_proc_type) { case POST_PROC_NONE: str = txt_post_process_none; break; case POST_PROC_SHAR: str = txt_post_process_sh; break; case POST_PROC_UUDECODE: str = txt_post_process_uudecode; break; case POST_PROC_UUD_LST_ZOO: str = txt_post_process_uud_lst_zoo; break; case POST_PROC_UUD_EXT_ZOO: str = txt_post_process_uud_ext_zoo; break; case POST_PROC_UUD_LST_ZIP: str = txt_post_process_uud_lst_zip; break; case POST_PROC_UUD_EXT_ZIP: str = txt_post_process_uud_ext_zip; break; } printf ("%s%s\r\n\r\n", txt_opt_process_type, str); MoveCursor(INDEX_TOP+10, COL1); switch (default_sort_art_type) { case SORT_BY_NOTHING: str = txt_sort_by_nothing; break; case SORT_BY_SUBJ_DESCEND: str = txt_sort_by_subj_descend; break; case SORT_BY_SUBJ_ASCEND: str = txt_sort_by_subj_ascend; break; case SORT_BY_FROM_DESCEND: str = txt_sort_by_from_descend; break; case SORT_BY_FROM_ASCEND: str = txt_sort_by_from_ascend; break; case SORT_BY_DATE_DESCEND: str = txt_sort_by_date_descend; break; case SORT_BY_DATE_ASCEND: str = txt_sort_by_date_ascend; break; } printf ("%s%s\r\n\r\n", txt_opt_sort_type, str); #ifndef AMIGA_BBS printf ("%s%s\r\n\r\n", txt_opt_savedir, default_savedir); printf ("%s%s\r\n\r\n", txt_opt_maildir, default_maildir); printf ("%s%s\r\n\r\n", txt_opt_printer, default_printer); #endif /* AMIGA_BBS */ fflush(stdout); show_menu_help (txt_select_rcfile_option); MoveCursor (cLINES, 0); } /* * expand ~/News to /usr/username/News and print to screen */ void expand_rel_abs_pathname (line, col, str) int line; int col; char *str; { char buf[LEN]; if (str[0] == '~') { if (strlen (str) == 1) { strcpy (str, homedir); } else { joinpath (buf, homedir, str+2); strcpy (str, buf); } } MoveCursor (line, col); CleartoEOLN (); puts (str); fflush (stdout); } /* * show_menu_help */ void show_menu_help (help_message) char *help_message; { MoveCursor (cLINES-2, 0); CleartoEOLN (); center_line (cLINES-2, FALSE, help_message); } int match_boolean (line, pat, dst) char *line; char *pat; int *dst; { int patlen = strlen (pat); if (strncmp (line, pat, patlen) == 0) { *dst = (strncmp (&line[patlen], "ON", 2) == 0 ? TRUE : FALSE); return TRUE; } return FALSE; } int match_number (line, pat, dst) char *line; char *pat; int *dst; { int patlen = strlen (pat); if (strncmp (line, pat, patlen) == 0) { *dst = atoi (&line[patlen]); return TRUE; } return FALSE; } int match_string (line, pat, dst, dstlen) char *line; char *pat; char *dst; int dstlen; { char *ptr; int patlen = strlen (pat); if (strncmp (line, pat, patlen) == 0) { strncpy (dst, &line[patlen], dstlen); ptr = strchr (dst, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } return TRUE; } return FALSE; } /* * convert underlines to spaces in a string */ void quote_dash_to_space (s) char *s; { int i; for (i=0 ; i < strlen (s) ; i++) { if (s[i] == '_') { s[i] = ' '; } } } /* * convert spaces to underlines in a string */ char * quote_space_to_dash (s) char *s; { int i; static char ds[PATH_LEN]; for (i=0 ; i < strlen (s) ; i++) { (s[i] == ' ') ? (ds[i] = '_') : (ds[i] = s[i]); } ds[i] = '\0'; return ds; } *[SRC.TIN-1_22]README.;1+,j. // 4 -d0@123KPWO 56Y<t7Jꑗ89 G/HJThis is version 1.2 PL2 (patchlevel 2) of the tin newsreader. o Compiles & runs on Unix, AmigaDOS & OS/2 (Not finished). o Based more on Notes and tass than rn type newreaders. o Full screen, easy to use with on-line help at all levels. o Reads news locally (ie. /usr/spool/news) and/or via NNTP/INN/CD-ROM. o Supports the XOVER news overview index file standard. o Threads on Subject: and/or Archive-name: mail headers. o Five different operating levels: - Spooldir selection level (CD-ROM only) - Group selection level - Thread selection level - Article selection level - Article viewer o Same interface to mail, pipe, print and save articles. o Auto unpacking of multi-part shar & uuencoded articles. o Killing and auto-selection (hot) of articles. o History of user posted articles. o Reposting of articles from one newsgroup to another. o Batch mode to mail/save new news when user is on holiday. o Random signature generator on a per newsgroup basis. o Builtin NNTP mini-inews & clientlib.c o NNTP extensions XINDEX, XMOTD & XUSER to allow better admin. ------------------------------------------------------------------------------- Major improvements over tin 1.2 PL1 are the following: o Added more support for 386BSD machines. o Added more support for AmigaDOS machines. o Added more support for HPUX machines. o Added more support for Mips machines. o Added more support for OS/2 machines. o Added more support for Pyramid machines. o Added more support for u3b2 machines. o Added reconnection to timed out nntp server. o Added group description text to groupname when searching at top level. o Added auto-subscribe/unsubscribe to/from new newsgroups. o Added basic navigation with mouse when running in a xterm window. o Added support for INN nntplib functions (GetFQDN() & GetConfigValue()). o Added -n cmd line option to read only subscribed to groups from active. o Added automatic entry into group when it was selected via a number. o Added basic MIME support (by calling Metamail). o Added more checking for article forging. o Changed article checking routine to be less facist when checking groups. o Fixed slowdown when entering a group compared to earlier releases. o Fixed -I cmd line option to specfiy different index directory. o Fixed direct use of getcwd() instead of get_cwd() in misc.c o Fixed 'Article rejected' error message from external inews. o Fixed reading of Xref: lines in xover data that could cause SIGSEGV. o Fixed connecting to ANU news server that was causing SIGSEGV. o Fixed poll() routine that was being initialized with wrong parameters. o Fixed valid_artnum() routine to use a binary instead of linear search. o Fixed strfpath() routine that was returning bogus filenames from env vars. o Fixed Makefile 'make install' rule. ------------------------------------------------------------------------------- For info. about the AmigaDOS port read the README.AMI file. For info. about the OS/2 port read the README.OS2 file. For compilation and installation information read the INSTALL file. For more bug fixes, changes & additions read the CHANGES & TODO files. For info. about my NNTP patches read the README.NNTP & INSTALL.NNTP files. For ftp & non-ftp source & binary availability read the FTP file. For an internal overview of tin read the HACKERS file. ------------------------------------------------------------------------------- I wish to thank all the people that sent me bug fixes and comments (especially Bryan Dongray, Clifton Royston, John Schmitz & Mark Tomlinson). I still want to hear of any bug reports, gripes and comments but *PLEASE* read the INSTALL, TODO and tin.1 manual page before doing anything :-)!. Also please make sure you have the newest release of tin before reporting any problems to me. My time is limited and I don't want to have to backtrack to assist with an old problem when it may have already been fixed in the newest release. I read & reply to questions that are posted to news.software.readers Enjoy Iain (iain.lea@erlm.siemens.de) *[SRC.TIN-1_22]README.AMI;1+,k. // 4 {-d0@123KPWO 56`kt7Nꑗ89 G/HJThis is the Amiga port of TIN. To use it you will need to do the following: Have appropriate directories assigned (UULIB: UUNEWS:). Have these environment variables set: NODENAME USERNAME REALNAME HOME EDITOR TIN only works with a hierarchical news directory. This means you will need Dillon's UUCP1.16 or higher. TIN has been modified to work with Dillon's Sendmail & Postnews programs. These take different arguments from standard UNIX mail and news programs. With release 1.1 PL 9 of TIN two further environment variables can been defined: TIN_POST (default "uucp:c/postnews %s") and TIN_MAIL (default "uucp:c/sendmail <%s -f %s"). If you set these variables, you can configure TIN for any other news/mail system. The %s in TIN_POST gets replaced with a filename containing the news article. TIN_MAIL's first %s is a filename, the second is the userid of the person sending the mail, and the third (if present), is the userid of the recipient of the mail. Printing is now available. You will have to edit the .tin/tinrc file to enable it. The "default_printer" string should be set to "copy PIPE: PRT:" instead of its default setting. This option has not been added to the internal menu of configurable options. The editor you use with TIN should not return instantly, so if you are using CED or TTX (or probably some others too) you will need to also add in the appropriate options in the environment variable EDITOR (see script file below) which will force your editor to wait until you've quit the editor. Unless you have an editor which understands the argument +7 to mean "start editing at line 7", you should set "Editor Offset" in the configuration menu (type shift-M) to OFF. An example startup script is given here (I've renamed tin to tin.exe so you can call this script TIN): ---------------------------------- ; For WB2.0 users, the setenv's can be set's instead. This allows multiple ; users to run with their own names etc. WB1.3 users have to use setenv. setenv USERNAME fred setenv REALNAME "Fred Flinstone" setenv NODENAME bedrock ; This should be just your node name, not the ; entire domain. setenv HOME dh0:news ; wherever you want your news & index files ; stored setenv EDITOR c:ed stack 40000 ; TIN requires a large stack! actived ; create a new active file delete uulib:active rename uulib:newsactive uulib:active tin.exe ; start tin itself --------------------------------- Actived creates an "active" file. This is a standard file in Unix environments, but missing from DUUCP. Actived also sets the environment variable "TIN_GROUPS" to equal the number of newsgroups. This variable isn't necessary to run TIN, but allows TIN to save some memory. If you don't wish to run actived before TIN, you may wish to copy TIN_GROUPS to ENVARC: (WB 2.0+ !!). More options, and use of tin is explained in the file tin.nrf. (The standard manual page that comes with TIN). The following options have been disabled from the TIN source for the Amiga version: - Archive extraction. Note that 1.1 PL 9 has now enabled uudecoding and un-shar'ing of posted archives. This requires "uudecode" and "unshar" to be in the path. - Shell escape and piping to any shell command. - Ability to change News and Mail directories. - Re-reading of the active file while reading news has been disabled. So has updating index files in the background (-U option). These options were disabled partly to make TIN easier to port, but also to make it secure enough to run as a newsreader for a Bulletin Board. A TIND program (for creating/updating index files) exists, and is only any use on a BBS. To make TIN aware that you are using TIND to update the index, you must set the environment variable TIND (it doesn't matter what you set it to!). The index files are by default stored in the UUNEWS: directory, in a subdirectory called .index (as in UNIX). For performance reasons, you may wish to change the directory to a different drive. By setting the environment variable TIN_INDEX, you can force the index files to be stored in $TIN_INDEX/.index. Another two environment variables LINES and COLUMNS can be set to specify the size of your screen. Inside an Amiga window this isn't required, as TIN can query the window size, but once again, if you're running on anything other than the Amiga's console, this query string won't work. If TIN finds the LINES and COLUMNS environment variables set, it will not try to query the window size. If you are planning on using TIN in a multi-user environment, such as with a BBS, you should take a look at the companion archive to this one, which is called DLGTIN.LHA. Even though this has been set up explicitly for DLG, it contains executables and documentation which will be useful for any setup of this type. - Mark Tomlinson (mark@garden.equinox.gen.nz) Geoff McCaughan (geoff@satori.equinox.gen.nz) *[SRC.TIN-1_22]README.NNTP;1+,x.// 4-d0@123KPWO56 ut7Qꑗ89 G/HJThis patch enables the TIN newsreader index files to be fetched from a NNTP server. The patch adds the following to the NNTP 1.5.11 server: o XINDEX command to retrieve tin style style group index files from the server. Syntax: XINDEX alt.sources o XOVERVIEW command to retrieve .overview style (a la Cnews) group index files from the server. Note: Tin will eventually be modified to use this format. Syntax: XOVERVIEW alt.sources o XMOTD command to display a news message of the day file from server. Syntax: XMOTD 921231 235959 o XUSER command to log tin clients username to nntp logfile. Syntax: XUSER Iain Lea o LIST SUBSCRIPTIONS command to retrieve list of newsgroups that first time user will be automatically subscribed to. Our users are auto subscribed to 120 groups from the 450 that we take. Groups are written 1 group per line in the file /usr/lib/news/subscriptions. Syntax: LIST SUBSCRIPTIONS o HELP command changed to list all the extensions. o Added comments /* */ around '#endif /* text */' to stop some compilers producing warnings about text after '#endif text'. For compile & install instructions read the file INSTALL.NNTP Enjoy Iain *[SRC.TIN-1_22]README.OS2;1+,l. // 4 -d0@123KPWO 56t7fꑗ89 G/HJhT~ TIN-1_22.BCKld[SRC.TIN-1_22]README.OS2;1 This is the OS/2 port of TIN. An executable version can be obtained from ftp.cdrom.com. To build TIN, you need the following: - IBM C Set/2 or Borland C++ for OS/2 - PDCurses 2.0 build for the compiler of your choice For the NNTP version of TIN: - IBM TCP/IP 1.2.1 CSD UN37938 or higher, - IBM TCP/IP 1.2.1 Programmers's Kit CSD UN37942 of higher, with modifications as outlined below. To run it you need: - set environment variables To read news via NNTP - TCP/IP access to an nntp server, - some free space on a HPFS or NFS volume (FAT will not work, due to 8.3 file name restriction) optional: - uudecode and uushar utilities Note: uudecode is posted regularly to comp.binaries.os2 in the "OS/2 Binaries Starter's Kit" by mykek@miller.cs.uwm.edu (OS/2 binaries moderator) Building TIN for OS/2 1. Make sure that all source files (*.c and *.h) have as their line-ends. If not, use 'unix2os2 < original >newfile' to correct this. 2. Select the makefile appropriate for your compiler: Makefile.bcc for Borland C++ for OS/2 Makefile.icc for IBM's C Set/2 Compiler Note: Borland C is prefered. You may have to make some adjustments when you use IBM's C. 3. Edit the makefile, changing location of include and library files to match your installation 4. Correct bugs in \tcpip\include\netdb.h find all occurrences of 'char *' that are not followed by '_Seg16' and add '_Seg16'. For example change SERVENT * _Seg16 _Far16 _Cdecl getservbyname( char *, char * ); to SERVENT * _Seg16 _Far16 _Cdecl getservbyname( char * _Seg16, char * _Seg16 ); You will also correct the prototype for getservbyport and getprotobyname. 5. If you are using Borland C++, edit the TCP/IP header files: in netdb.h, netlib.h sys/socket.h do the following: - find the #ifndef __32BIT__ near the top of each file - change the conditional section as follows: #ifndef __32BIT__ #ifdef __TURBOC__ #define _Packed #define _Seg16 _far16 #define _Far16 _far16 #define _Cdecl _cdecl #define __32BIT__ #else #define _Packed #define _Seg16 #define _Far16 #define _Cdecl #endif #endif - add the following below the second #endif #ifdef __TURBOC__ #define FAR16P _far16 * #else #define FAR16P * _Seg16 #endif - throughout the files change '* _Seg16' and '* _Seg16' to 'FAR16P' After these changes, the TCP/IP package can be used with the Borland C compiler. 6. If you are using Borland C++ type 'make -f makefile.bcc' If you are using IBM's C Set/2 type 'nmake -f makefile.icc' Ignore warnings, especially re _Cdecl and _Seg16 re-definition. Using TIN under OS/2 To run the local newsspool version, start TIN. To run the NNTP version, start TIN -r, or rename TIN.EXE to RTIN.EXE and run RTIN. TIN runs in an OS/2 windowed session. It is not a PM application. TIN needs to know who you are, in order to create headers for posts and mail correctly. There are two ways in which you can specify this information: - through environment variables - through a Unix-style passwd file Environment Variables If only one person is using TIN to post articles, set the following variables in your config.sys file: USER userid set USER=andreas REALNAME Your Full Name set REALNAME=Andreas Wrede HOME \your\home\directory set HOME=c:\home\andreas Note that you HOME directory must be on a HPFS or NFS disk. FAT will probably not work, due to the 8.3 file name restriction. Unix-style passwd file If a number of users use TIN to post articles, for example via telnet to the OS/2 machine, it might be easier to use the Unix-style passwd file. The passwd file is located in the directory set by the environment variable ETC, usually c:\tcpip\etc. The same passwd file is used by ftp and telnet (via loginunx.exe). See your TCP/IP User's Guide for more information. TIN does no use the password field, but the full user name, the home directory and the shell are taken from the passwd file entry. The environment variable USER is used to locate the correct entry in the passwd file. If it is not set, an error message is issued by TIN. Two sample passwd entries follow: andreas:x:1:1:Andreas Wrede:c:/home/andreas:c:/os2/cmd.exe guest:x:2:2:Guest User:c:/home/guest:d:/ksh/ksh Other Environment Variables Required for NNTP version: NNTPSERVER - either the fully qualified domain name of the nntp server or the dotted internet address of the nntp server. HOSTNAME - specifies the name of your machine. It is used to identify your machine to the nntp server. Required for local newspool version: TIN_SPOOLDIR - path to the root of the news spool TIN_LIBDIR - path to news control: active file, newsgroups file etc. TIN_NOVROOTDIR - path to overview files Optional: LINES - specify how many lines you display window has. If LINES is not set, TIN will use the actual height of the display window. You might need to set this if you run TIN from a telnet session. EDITOR - VISUAL - specify the editor to use for composing articles and replies. Defaults to 'epm'. SHELL - specify command process to use for shell escapes - defaults to whatever is set by COMSPEC For other environment variables, see ENVIRONMENT VARIABLES in tin.man. Enjoy! Andreas Wrede (andreas@scilink.org) *[SRC.TIN-1_22]SAVE.C;11+,.?// 4?>-d0@123KPWO@56h7 nj89]VG/HJb/* * Project : tin - a Usenet reader * Module : save.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 22-08-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #define INITIAL 1 #define MIDDLE 2 #define OFF 3 #define END 4 #ifndef VMS extern int errno; #endif int create_subdir = TRUE; /* * types of archive programs */ struct archiver_t { char *name; char *ext; char *test; char *list; char *extract; } archiver[] = { { "", "", "", "", "" }, { "", "", "", "", "" }, { "", "", "", "", "" }, #ifdef M_AMIGA { "lha", "lha", "t", "l", "x" }, #else { "zoo", "zoo", "-test", "-list", "-extract" }, #endif { "unzip", "zip", "-t", "-l", "-o" }, { (char *) 0, (char *) 0, (char *) 0, (char *) 0, (char *) 0 } }; extern char *glob_group; extern char note_h_path[LEN]; /* Path: */ extern char note_h_date[PATH_LEN]; /* Date: */ extern FILE *note_fp; /* the body of the current article */ extern int index_point; extern int note_end; extern int note_page; extern long note_mark[MAX_PAGES]; /* * Check for articles and say how many new/unread in each group. * or * Start if new/unread articles and return first group with new/unread. * or * Save any new articles to savedir and mark arts read and mail user * and inform how many arts in which groups were saved. * or * Mail any new articles to specified user and mark arts read and mail * user and inform how many arts in which groups were mailed. */ int check_start_save_any_news (check_start_save) int check_start_save; { #ifndef INDEX_DAEMON char buf[LEN], logfile[LEN]; char group_path[PATH_LEN]; char savefile[PATH_LEN]; char path[PATH_LEN]; extern FILE *note_fp; FILE *fp = (FILE *) 0; FILE *fp_log = (FILE *) 0; int i, j, k, print_group; int check_arts = 0; int log_opened = TRUE; int print_first = TRUE; int saved_arts = 0; int saved_groups = 0; int unread_news = FALSE; long epoch; switch (check_start_save) { case CHECK_ANY_NEWS: case START_ANY_NEWS: if (verbose) { wait_message (txt_checking_for_news); } break; case MAIL_ANY_NEWS: case SAVE_ANY_NEWS: joinpath (logfile, rcdir, "log"); #ifdef VMS if ((fp_log = fopen (logfile, "w", "fop=cif")) == NULL) { #else if ((fp_log = fopen (logfile, "w")) == NULL) { #endif perror_message (txt_cannot_open, logfile); fp_log = stdout; verbose = FALSE; log_opened = FALSE; } time (&epoch); fprintf (fp_log, "To: %s\n", userid); fprintf (fp_log, "Subject: NEWS LOG %s\n", ctime (&epoch)); break; } for (i = 0; i < group_top; i++) { make_group_path (active[my_group[i]].name, group_path); if (! index_group (active[my_group[i]].name, group_path)) { continue; } read_newsrc_line (active[my_group[i]].name); print_group = TRUE; check_arts = 0; for (j = 0; j < top; j++) { if (arts[j].unread == ART_UNREAD) { switch (check_start_save) { case CHECK_ANY_NEWS: if (print_first && verbose) { fputc ('\n', stdout); print_first = FALSE; } check_arts++; break; case START_ANY_NEWS: return i; /* return first group with unread news */ /* NOTREACHED */ case MAIL_ANY_NEWS: case SAVE_ANY_NEWS: if (print_group) { sprintf (buf, "Saved %s...\n", active[my_group[i]].name); fprintf (fp_log, "%s", buf); if (verbose) { wait_message (buf); } print_group = FALSE; saved_groups++; if (check_start_save == SAVE_ANY_NEWS) { joinpath (buf, group_path, "dummy"); create_path (buf); } } if (check_start_save == MAIL_ANY_NEWS) { sprintf (savefile, "%stin.%d", TMPDIR, process_id); } else { k = my_group[cur_groupnum]; if (! strfpath (active[k].attribute.savedir, path, sizeof (path), homedir, (char *) 0, (char *) 0, active[k].name)) { joinpath (path, homedir, DEFAULT_SAVEDIR); } sprintf (savefile, "%s/%s/%ld", path, group_path, arts[j].artnum); } note_page = art_open (arts[j].artnum, group_path); if (note_page == ART_UNAVAILABLE) { continue; } #ifdef VMS if ((fp = fopen (savefile, "w", "fop=cif")) == NULL) { #else if ((fp = fopen (savefile, "w")) == NULL) { #endif fprintf (fp_log, txt_cannot_open, savefile); if (verbose) { perror_message (txt_cannot_open, savefile); } continue; } if (check_start_save == MAIL_ANY_NEWS) { fprintf (fp, "To: %s\n", mail_news_user); } sprintf (buf, "[%5ld] %s\n", arts[j].artnum, arts[j].subject); fprintf (fp_log, "%s", buf); if (verbose) { wait_message (buf); } fseek (note_fp, 0L, 0); copy_fp (note_fp, fp, ""); art_close (); fclose (fp); saved_arts++; if (check_start_save == MAIL_ANY_NEWS) { #ifdef M_AMIGA sprintf (buf, mailer, savefile, userid, mail_news_user); #else sprintf (buf, "%s \"%s\" < %s", mailer, mail_news_user, savefile); #endif /* M_AMIGA */ if (! invoke_cmd (buf)) { error_message (txt_command_failed_s, buf); } unlink (savefile); } if (catchup) { arts[j].unread = ART_READ; } break; } } } if (check_start_save == MAIL_ANY_NEWS || check_start_save == SAVE_ANY_NEWS) { if (catchup) { update_newsrc (active[my_group[i]].name, my_group[i], FALSE); } } else { if (check_arts) { if (verbose) { sprintf (buf, "%4d unread articles in %s\n", check_arts, active[my_group[i]].name); wait_message (buf); } unread_news = TRUE; } } } switch (check_start_save) { case CHECK_ANY_NEWS: if (unread_news) { return 2; } else { if (verbose) { wait_message (txt_there_is_no_news); } return 0; } /* NOTREACHED */ case START_ANY_NEWS: wait_message (txt_there_is_no_news); return -1; /* NOTREACHED */ case MAIL_ANY_NEWS: case SAVE_ANY_NEWS: sprintf (buf, "\n%s %d article(s) from %d group(s)\n", (check_start_save == MAIL_ANY_NEWS ? "Mailed" : "Saved"), saved_arts, saved_groups); fprintf (fp_log, "%s", buf); if (verbose) { wait_message (buf); } if (log_opened) { fclose (fp_log); if (verbose) { sprintf (buf, "Mailing log to %s\n", (check_start_save == MAIL_ANY_NEWS ? mail_news_user : userid)); wait_message (buf); } #ifdef M_AMIGA sprintf (buf, mailer, logfile, userid, (check_start_save == MAIL_ANY_NEWS ? mail_news_user : userid)); #else sprintf (buf, "%s \"%s\" < %s", mailer, (check_start_save == MAIL_ANY_NEWS ? mail_news_user : userid), logfile); #endif /* M_AMIGA */ if (! invoke_cmd (buf)) { error_message (txt_command_failed_s, buf); } } break; } #endif /* INDEX_DAEMON */ return 0; } int save_art_to_file (respnum, index, mailbox, filename) int respnum; int index; int mailbox; char *filename; { #ifndef INDEX_DAEMON char file[PATH_LEN]; char save_art_info[LEN]; FILE *fp; int is_mailbox = FALSE; int i = 0, ret_code = FALSE; long epoch; if (debug == 2) { sprintf (msg, "Save respnum=[%d] index=[%d] mbox=[%d] file=[%s]", respnum, index, mailbox, filename); error_message (msg, ""); } if (strlen (filename)) { my_strncpy (file, filename, sizeof (file)); is_mailbox = mailbox; i = index; } else if (default_auto_save && arts[respnum].archive) { my_strncpy (file, arts[respnum].archive, sizeof (file)); } if (! append_to_existing_file (i)) { save[i].saved = FALSE; info_message (txt_art_not_saved); sleep (1); return (ret_code); } if (debug == 2) { error_message ("Save file=[%s]", save_filename (i)); sleep (3); } if ((fp = fopen (save_filename (i), "a+")) == NULL) { save[i].saved = FALSE; info_message (txt_art_not_saved); return (ret_code); } time (&epoch); fprintf (fp, "From %s %s", note_h_path, ctime (&epoch)); if (fseek (note_fp, 0L, 0) == -1) { perror_message ("fseek() error on [%s]", arts[respnum].subject); } copy_fp (note_fp, fp, ""); print_art_seperator_line (fp, mailbox); fclose (fp); fseek (note_fp, note_mark[note_page], 0); save[i].saved = TRUE; if (filename == (char *) 0) { if (is_mailbox) { sprintf (save_art_info, txt_saved_to_mailbox, get_first_savefile ()); } else { sprintf (save_art_info, txt_art_saved_to, get_first_savefile ()); } info_message (save_art_info); } #endif /* INDEX_DAEMON */ return TRUE; } int save_thread_to_file (is_mailbox, group_path) int is_mailbox; char *group_path; { #ifndef INDEX_DAEMON char file[PATH_LEN]; char save_thread_info[LEN]; char *first_savefile; int count = 0; int i, ret_code = FALSE; for (i=0 ; i < num_save ; i++) { sprintf (msg, "%s%d", txt_saving, ++count); wait_message (msg); if (is_mailbox) { file[0] = 0; }else { sprintf (file, "%s.%02d", save[i].file, i+1); } note_page = art_open (arts[save[i].index].artnum, group_path); if (note_page != ART_UNAVAILABLE) { ret_code = save_art_to_file (save[i].index, i, is_mailbox, file); art_close (); } } first_savefile = get_first_savefile (); if (first_savefile == (char *) 0) { info_message (txt_thread_not_saved); } else { if (is_mailbox) { sprintf (save_thread_info, txt_saved_to_mailbox, first_savefile); } else { if (num_save == 1) { sprintf (save_thread_info, txt_art_saved_to, first_savefile); } else { sprintf (save_thread_info, txt_thread_saved_to_many, first_savefile, get_last_savefile ()); } if (first_savefile != (char *) 0) { free (first_savefile); first_savefile = (char *) 0; } } info_message (save_thread_info); sleep (2); } #endif /* INDEX_DAEMON */ return TRUE; } int save_regex_arts (is_mailbox, group_path) int is_mailbox; char *group_path; { #ifndef INDEX_DAEMON char buf[PATH_LEN]; int i, ret_code = FALSE; for (i=0 ; i < num_save ; i++) { sprintf(msg, "%s%d", txt_saving, i+1); wait_message (msg); if (is_mailbox) { buf[0] = 0; }else { sprintf (buf, "%s.%02d", save[i].file, i+1); } note_page = art_open (arts[save[i].index].artnum, group_path); if (note_page != ART_UNAVAILABLE) { ret_code = save_art_to_file (save[i].index, i, is_mailbox, buf); art_close (); } } if (! num_save) { info_message (txt_no_match); } else { if (is_mailbox) { sprintf (buf, txt_saved_to_mailbox, get_first_savefile ()); } else { sprintf (buf,txt_saved_pattern_to, get_first_savefile (), get_last_savefile ()); } info_message (buf); } return (ret_code); #else return (FALSE); #endif /* INDEX_DAEMON */ } int append_to_existing_file (i) int i; { #ifndef INDEX_DAEMON char buf[LEN]; char *file; struct stat st; if (! save[i].is_mailbox) { file = save_filename (i); if (stat(file, &st) != -1) { sprintf (buf, txt_append_to_file, file); if (! prompt_yn (cLINES, buf, 'n')) { if (file != (char *) 0) { free (file); file = (char *) 0; } return FALSE; } } if (file != (char *) 0) { free (file); file = (char *) 0; } } #endif /* INDEX_DAEMON */ return TRUE; } int create_path (path) char *path; { int mbox_format = FALSE; #ifndef INDEX_DAEMON char tmp[PATH_LEN]; char buf[PATH_LEN]; int i, j, len = 0; struct stat st; i = my_group[cur_groupnum]; /* * expand "$var..." first, so variables starting with * '+', '$' or '=' will be processed correctly later */ #ifndef VMS if (path[0] == '$') { if (strfpath (path, buf, sizeof (buf), homedir, (char *) 0, (char *) 0, active[i].name)) { my_strncpy (path, buf, PATH_LEN); } } #endif /* * save in mailbox format to ~/Mail/ or * attribute.maildir for current group */ if (path[0] == '=') { mbox_format = TRUE; strcpy (tmp, path); if (! strfpath (active[i].attribute.maildir, buf, sizeof (buf), homedir, (char *) 0, (char *) 0, active[i].name)) { #ifdef VMS joindir (buf, homedir, DEFAULT_MAILDIR); #else joinpath (buf, homedir, DEFAULT_MAILDIR); #endif } joinpath (path, buf, "dummy"); } else { #ifdef VMS if (! strchr ("~=+/.", path[0])) { #else if (! strchr ("~$=+/.", path[0])) { #endif if (! strfpath (active[i].attribute.savedir, buf, sizeof (buf), homedir, (char *) 0, (char *) 0, active[i].name)) { #ifdef VMS joindir (buf, homedir, DEFAULT_SAVEDIR); #else joinpath (buf, homedir, DEFAULT_SAVEDIR); #endif } joinpath (tmp, buf, path); my_strncpy (path, tmp, PATH_LEN); } if (strfpath (path, buf, sizeof (buf), homedir, (char *) 0, active[i].attribute.savedir, active[i].name)) { my_strncpy (path, buf, PATH_LEN); } } #ifndef VMS /* no good answer to this yet XXX */ /* * create any directories, otherwise check * errno and give appropiate error message */ len = (int) strlen (path); for (i=0, j=0 ; i < len ; i++, j++) { buf[j] = path[i]; if (i+1 < len && path[i+1] == '/') { buf[j+1] = '\0'; if (stat (buf, &st) == -1) { if (my_mkdir (buf, 0755) == -1) { if (errno != EEXIST) { perror_message ("Cannot create %s", buf); return FALSE; } } } } } #endif if (mbox_format) { strcpy (path, tmp); } #endif /* INDEX_DAEMON */ return (mbox_format); } int create_sub_dir (i) int i; { #ifndef INDEX_DAEMON char dir[LEN]; struct stat st; if (! save[i].is_mailbox && save[i].archive) { joinpath (dir, save[i].dir, save[i].archive); if (stat (dir, &st) == -1) { my_mkdir (dir, 0755); return TRUE; } #ifdef M_AMIGA if (st.st_attr & ST_DIRECT) { #else # ifdef M_OS2 if (st.st_mode & S_IFDIR) { # else if ((st.st_mode & S_IFMT) == S_IFDIR) { # endif #endif return TRUE; } else { return FALSE; } } #endif /* INDEX_DAEMON */ return FALSE; } /* * add files to be saved to save array */ void add_to_save_list (index, article, is_mailbox, archive_save, path) int index; struct t_article *article; int is_mailbox; int archive_save; char *path; { #ifndef INDEX_DAEMON char tmp[PATH_LEN]; char dir[PATH_LEN]; char file[PATH_LEN]; int i; dir[0] = '\0'; file[0] = '\0'; if (num_save == max_save-1) { expand_save (); } save[num_save].index = index; save[num_save].saved = FALSE; save[num_save].is_mailbox = is_mailbox; save[num_save].dir = (char *) 0; save[num_save].file = (char *) 0; save[num_save].archive = (char *) 0; save[num_save].part = (char *) 0; save[num_save].patch = (char *) 0; save[num_save].subject = str_dup (article->subject); if (archive_save && article->archive) { save[num_save].archive = str_dup (article->archive); if (article->part) { save[num_save].part = str_dup (article->part); } if (article->patch) { save[num_save].patch = str_dup (article->patch); } } if (is_mailbox) { if ((int) strlen (path) > 1) { if (path[0] == '=') { my_strncpy (file, path+1, sizeof (file)); } else { my_strncpy (file, path, sizeof (file)); } } else { my_strncpy (file, glob_group, sizeof (file)); } i = my_group[cur_groupnum]; if (! strfpath (active[i].attribute.maildir, tmp, sizeof (tmp), homedir, (char *) 0, (char *) 0, active[i].name)) { #ifdef VMS joindir (tmp, homedir, DEFAULT_MAILDIR); #else joinpath (tmp, homedir, DEFAULT_MAILDIR); #endif } save[num_save].dir = str_dup (tmp); save[num_save].file = str_dup (file); } else { if (path[0]) { #ifdef VMS #include "parse.h" struct filespec *spec; spec = sysparse(path); sprintf(dir, "%s%s", spec->dev, spec->dir); strcpy(file, spec->filename); #else for (i=strlen (path) ; i ; i--) { if (path[i] == '/') { strncpy (dir, path, i); dir[i] = '\0'; strcpy (file, path+i+1); break; } } #endif } if (dir[0]) { save[num_save].dir = str_dup (dir); } else { i = my_group[cur_groupnum]; if (! strfpath (active[i].attribute.savedir, tmp, sizeof (tmp), homedir, (char *) 0, (char *) 0, active[i].name)) { joinpath (tmp, homedir, DEFAULT_SAVEDIR); } save[num_save].dir = str_dup (tmp); } if (file[0]) { save[num_save].file = str_dup (file); } else { if (path[0]) { save[num_save].file = str_dup (path); } else { save[num_save].file = str_dup (save[num_save].archive); } } } num_save++; #endif /* INDEX_DAEMON */ } /* * print save array of files to be saved */ void sort_save_list () { qsort ((char *) save, num_save, sizeof (struct t_save), save_comp); debug_save_comp (); } /* * string comparison routine for the qsort() * ie. qsort(array, 5, 32, save_comp); */ int save_comp (p1, p2) t_comptype *p1; t_comptype *p2; { struct t_save *s1 = (struct t_save *) p1; struct t_save *s2 = (struct t_save *) p2; /* * Sort on Archive-name: part & patch otherwise Subject: */ if (s1->archive != (char *) 0) { if (s1->part != (char *) 0) { if (s2->part != (char *) 0) { if (strcmp (s1->part, s2->part) < 0) { return -1; } if (strcmp (s1->part, s2->part) > 0) { return 1; } } else { return 0; } } else if (s1->patch != (char *) 0) { if (s2->patch != (char *) 0) { if (strcmp (s1->patch, s2->patch) < 0) { return -1; } if (strcmp (s1->patch, s2->patch) > 0) { return 1; } } else { return 0; } } } else { if (strcmp (s1->subject, s2->subject) < 0) { return -1; } if (strcmp (s1->subject, s2->subject) > 0) { return 1; } } return 0; } char * save_filename (i) int i; { char *filename; filename = (char *) my_malloc (PATH_LEN); if (save[i].is_mailbox) { joinpath(filename, save[i].dir, save[i].file); return (filename); } if (! default_auto_save || (! save[i].part && ! save[i].patch)) { if (num_save == 1) { joinpath (filename, save[i].dir, save[i].file); } else { #ifdef VMS char tbuf[256]; sprintf(tbuf, "%s.%02d", save[i].file, i+1); joinpath (filename, save[i].dir, tbuf); #else sprintf (filename, "%s/%s.%02d", save[i].dir, save[i].file, i+1); #endif } } else { if (save[i].part) { if (create_sub_dir (i)) { #ifdef VMS char fbuf[256], dbuf[256]; sprintf(fbuf, "%s.%s%s", save[i].archive, LONG_PATH_PART, save[i].part); joindir(dbuf, save[i].dir, save[i].archive); joinpath(filename, dbuf, fbuf); #else sprintf (filename, "%s/%s/%s.%s%s", save[i].dir, save[i].archive, save[i].archive, LONG_PATH_PART, save[i].part); #endif } else { #ifdef VMS char fbuf[256]; sprintf(fbuf, "%s.%s%s", save[i].archive, LONG_PATH_PART, save[i].part); joinpath(filename, save[i].dir, fbuf); #else sprintf (filename, "%s/%s.%s%s", save[i].dir, save[i].archive, LONG_PATH_PART, save[i].part); #endif } } else { if (save[i].patch) { if (create_sub_dir (i)) { #ifdef VMS char fbuf[256], dbuf[256]; sprintf(fbuf, "%s.%s%s", save[i].archive, LONG_PATH_PATCH, save[i].patch); joindir(dbuf, save[i].dir, save[i].archive); joinpath(filename, dbuf, fbuf); #else sprintf (filename, "%s/%s/%s.%s%s", save[i].dir, save[i].archive, save[i].archive, LONG_PATH_PATCH, save[i].patch); #endif } else { #ifdef VMS char fbuf[256]; sprintf(fbuf, "%s.%s%s", save[i].archive, LONG_PATH_PATCH, save[i].patch); joinpath(filename, save[i].dir, fbuf); #else sprintf (filename, "%s/%s.%s%s", save[i].dir, save[i].archive, LONG_PATH_PATCH, save[i].patch); #endif } } else { joinpath (filename, save[i].dir, save[i].file); } } } return (filename); } char * get_first_savefile () { char *file; int i; for (i=0 ; i < num_save ; i++) { if (save[i].saved) { file = (char *) my_malloc (PATH_LEN); if (save[i].is_mailbox) { joinpath (file, save[i].dir, save[i].file); return (file); } else { if (save[i].archive && default_auto_save) { if (save[i].part) { if (create_subdir) { #ifdef VMS char fbuf[256], dbuf[256]; sprintf(fbuf, "%s.%s%s", save[i].archive, LONG_PATH_PART, save[i].part); joinpath(file, save[i].archive, fbuf); #else sprintf (file, "%s/%s.%s%s", save[i].archive, save[i].archive, LONG_PATH_PART, save[i].part); #endif } else { sprintf (file, "%s.%s%s", save[i].archive, LONG_PATH_PART, save[i].part); } } else { if (create_subdir) { #ifdef VMS char fbuf[256]; sprintf(fbuf, "%s.%s%s", save[i].archive, LONG_PATH_PATCH, save[i].patch); joinpath(file, save[i].archive, fbuf); #else sprintf (file, "%s/%s.%s%s", save[i].archive, save[i].archive, LONG_PATH_PATCH, save[i].patch); #endif } else { sprintf (file, "%s.%s%s", save[i].archive, LONG_PATH_PATCH, save[i].patch); } } } else { if (num_save == 1) { sprintf (file, "%s", save[i].file); } else { sprintf (file, "%s.%02d", save[i].file, i+1); } } return (file); } } } return ((char *) 0); } char * get_last_savefile () { char *file; int i; for (i=num_save-1 ; i >= 0 ; i--) { if (save[i].saved) { file = (char *) my_malloc (PATH_LEN); if (save[i].is_mailbox) { joinpath(file, save[i].dir, save[i].file); return (file); } else { if (save[i].archive && default_auto_save) { if (save[i].part) { if (create_subdir) { #ifdef VMS char fbuf[256]; sprintf(fbuf, "%s.%s%s", save[i].archive, LONG_PATH_PART, save[i].part); joinpath(file, save[i].archive, fbuf); #else sprintf (file, "%s/%s.%s%s", save[i].archive, save[i].archive, LONG_PATH_PART, save[i].part); #endif } else { sprintf (file, "%s.%s%s", save[i].archive, LONG_PATH_PART, save[i].part); } } else { if (create_subdir) { #ifdef VMS char fbuf[256]; sprintf(fbuf, "%s.%s%s", save[i].archive, LONG_PATH_PATCH, save[i].patch); joinpath(file, save[i].archive, fbuf); #else sprintf (file, "%s/%s.%s%s", save[i].archive, save[i].archive, LONG_PATH_PATCH, save[i].patch); #endif } else { sprintf (file, "%s.%s%s", save[i].archive, LONG_PATH_PATCH, save[i].patch); } } } else { if (num_save == 1) { sprintf (file, "%s", save[i].file); } else { sprintf (file, "%s.%02d", save[i].file, i+1); } } return (file); } } } return ((char *) 0); } int post_process_files (proc_type_ch) int proc_type_ch; { if (num_save) { wait_message (txt_post_processing); switch (proc_type_ch) { case 's': post_process_sh (); break; case 'u': post_process_uud (POST_PROC_UUDECODE); break; case 'l': post_process_uud (POST_PROC_UUD_LST_ZOO); break; case 'e': post_process_uud (POST_PROC_UUD_EXT_ZOO); break; case 'L': post_process_uud (POST_PROC_UUD_LST_ZIP); break; case 'E': post_process_uud (POST_PROC_UUD_EXT_ZIP); break; } info_message (txt_post_processing_finished); sleep (1); return TRUE; } return FALSE; } void post_process_uud (pp) int pp; { #ifndef INDEX_DAEMON char s[LEN], t[LEN], u[LEN]; char buf[LEN], *file, *ptr; char file_out[PATH_LEN]; char file_out_dir[PATH_LEN]; FILE *fp_in, *fp_out; int i, state = INITIAL; int file_size = 0; struct stat st; t[0] = '\0'; u[0] = '\0'; my_strncpy (file_out_dir, save_filename (0), sizeof (file_out_dir)); for (i=strlen(file_out_dir) ; i > 0 ; i--) { if (file_out_dir[i] == '/') { file_out_dir[i] = '\0'; break; } } joinpath (file_out, file_out_dir, process_id); if ((fp_out = fopen (file_out, "a+")) == NULL) { perror_message (txt_cannot_open, file_out); } for (i=0 ; i < num_save ; i++) { my_strncpy (buf, save_filename (i), sizeof (buf)); if ((fp_in = fopen (buf, "r")) != NULL) { if (fgets (s, sizeof s, fp_in) == NULL) { fclose (fp_in); continue; } while (state != END) { switch (state) { case INITIAL: if (! strncmp ("begin", s, 5)) { state = MIDDLE; fprintf (fp_out, "%s", s); } break; case MIDDLE: if (s[0] == 'M') { fprintf (fp_out, "%s", s); } else if (strncmp("end", s, 3)) { state = OFF; } else { /* end */ state = END; if (u[0] != 'M') { fprintf (fp_out, "%s", u); } if (t[0] != 'M') { fprintf (fp_out, "%s", t); } fprintf (fp_out, "%s\n", s); } break; case OFF: if ((s[0] == 'M') && (t[0] == 'M') && (u[0] == 'M')) { fprintf (fp_out, "%s", u); fprintf (fp_out, "%s", t); fprintf (fp_out, "%s", s); state = MIDDLE; } else if (! strncmp ("end",s/~ TIN-1_22.BCKd[SRC.TIN-1_22]SAVE.C;11?׊2 s, 3)) { state = END; if (u[0] != 'M') { fprintf (fp_out, "%s", u); } if (t[0] != 'M') { fprintf (fp_out, "%s", t); } fprintf (fp_out, "%s\n", s); } break; case END: break; default: fprintf (stderr, "\r\nerror: ASSERT - default state\n"); fclose (fp_in); fclose (fp_out); unlink (file_out); return; } strcpy (u,t); strcpy (t,s); /* * read next line & if error goto next file in save array */ if (fgets (s, sizeof s, fp_in) == NULL) { break; } } fclose (fp_in); } } fclose (fp_out); /* * uudecode file */ wait_message (txt_uudecoding); #if !defined(M_UNIX) make_post_process_cmd (DEFAULT_UUDECODE, file_out_dir, file_out); #else sprintf (buf, "cd %s; uudecode %s", file_out_dir, file_out); if (invoke_cmd (buf)) { /* * Sum file */ if ((file = get_archive_file (file_out_dir, "*")) != (char *) 0) { sprintf (buf, "%s %s", DEFAULT_SUM, file); printf (txt_checksum_of_file, file); fflush (stdout); if ((fp_in = (FILE *) popen (buf, "r")) == NULL) { printf ("Cannot execute %s\r\n", buf); fflush (stdout); } else { if (stat (file, &st) != -1) { file_size = (int) st.st_size; } if (fgets (buf, sizeof buf, fp_in) != NULL) { ptr = strchr (buf, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } } pclose (fp_in); printf ("%s %8d bytes\r\n", buf, file_size); fflush (stdout); } if (pp > POST_PROC_UUDECODE) { /* * Test archive integrity */ if (pp > POST_PROC_UUDECODE && archiver[pp].test != (char *) 0) { i = (pp == POST_PROC_UUD_LST_ZOO || pp == POST_PROC_UUD_EXT_ZOO ? 3 : 4); sprintf (buf, "cd %s; %s %s %s", file_out_dir, archiver[i].name, archiver[i].test, file); printf (txt_testing_archive, file); fflush (stdout); if (! invoke_cmd (buf)) { error_message (txt_post_processing_failed, ""); } } /* * List archive */ if (pp == POST_PROC_UUD_LST_ZOO || pp == POST_PROC_UUD_LST_ZIP) { i = (pp == POST_PROC_UUD_LST_ZOO ? 3 : 4); sprintf (buf, "cd %s; %s %s %s", file_out_dir, archiver[i].name, archiver[i].list, file); printf (txt_listing_archive, file); fflush (stdout); if (! invoke_cmd (buf)) { error_message (txt_post_processing_failed, ""); } sleep (3); } /* * Extract archive */ if (pp == POST_PROC_UUD_EXT_ZOO || pp == POST_PROC_UUD_EXT_ZIP) { i = (pp == POST_PROC_UUD_EXT_ZOO ? 3 : 4); sprintf (buf, "cd %s; %s %s %s", file_out_dir, archiver[i].name, archiver[i].extract, file); printf (txt_extracting_archive, file); fflush (stdout); if (! invoke_cmd (buf)) { error_message (txt_post_processing_failed, ""); } sleep (3); } if (file != (char *) 0) { free (file); file = (char *) 0; } } } } #endif /* M_UNIX */ delete_processed_files (); unlink (file_out); #endif /* INDEX_DAEMON */ } /* * Unpack /bin/sh archives */ void post_process_sh () { #ifndef INDEX_DAEMON char buf[LEN]; char file_in[PATH_LEN]; char file_out[PATH_LEN]; char file_out_dir[PATH_LEN]; char *ptr1, *ptr2; char sh_pattern_1[16]; char sh_pattern_2[16]; FILE *fp_in, *fp_out; int found_header; int i, j; int patlen1, patlen2; strcpy (sh_pattern_1, "#! /bin/sh"); strcpy (sh_pattern_2, "#!/bin/sh"); my_strncpy (file_out_dir, save_filename (0), sizeof (file_out_dir)); for (i=strlen(file_out_dir) ; i > 0 ; i--) { if (file_out_dir[i] == '/') { file_out_dir[i] = '\0'; break; } } joinpath (file_out, file_out_dir, process_id); for (j=0 ; j < num_save ; j++) { my_strncpy (file_in, save_filename (j), sizeof (file_in)); printf (txt_extracting_shar, file_in); fflush (stdout); found_header = FALSE; #ifdef VMS if ((fp_out = fopen (file_out, "w", "fop=cif")) != NULL) { #else if ((fp_out = fopen (file_out, "w")) != NULL) { #endif if ((fp_in = fope :n (file_in, "r")) != NULL) { ptr1 = sh_pattern_1; ptr2 = sh_pattern_2; patlen1 = strlen (sh_pattern_1); patlen2 = strlen (sh_pattern_2); while (! feof (fp_in)) { if (fgets (buf, sizeof buf, fp_in)) { /* * find #!/bin/sh or #! /bin/sh pattern */ if (!found_header) { if (str_str (buf, ptr1, patlen1) != 0 || str_str (buf, ptr2, patlen2) != 0) { found_header = TRUE; } } /* * Write to temp file */ if (found_header) { fputs (buf, fp_out); } } } fclose (fp_in); } fclose (fp_out); #if !defined(M_UNIX) make_post_process_cmd (DEFAULT_UNSHAR, file_out_dir, file_out); #else sprintf (buf, "cd %s; sh %s", file_out_dir, file_out); fputs ("\r\n", stdout); fflush (stdout); Raw (FALSE); invoke_cmd (buf); Raw (TRUE); #endif unlink (file_out); } } delete_processed_files (); #endif /* INDEX_DAEMON */ } /* * Returns the most recently modified file in the specified drectory */ char * get_archive_file (dir, ext) char *dir; char *ext; { char buf[LEN]; char *file = (char *) 0; DIR *dirp; DIR_BUF *dp; struct stat sbuf; time_t last = 0; if ((file = (char *) my_malloc (LEN)) == (char *) 0) { return (char *) 0; } if ((dirp = opendir (dir)) == NULL) { free (file); return (char *) 0; } dp = (DIR_BUF *) readdir (dirp); while (dp != (DIR_BUF *) 0) { joinpath (buf, dir, dp->d_name); stat (buf, &sbuf); if ((sbuf.st_mtime > last) && S_ISREG(sbuf.st_mode)) { last = sbuf.st_mtime; strcpy (file, buf); } dp = (DIR_BUF *) readdir (dirp); } closedir (dirp); if (last == 0) { free (file); file = (char *) 0; } return (file); } void delete_processed_files () { #ifndef INDEX_DAEMON int delete = FALSE; int i; if (active[my_group[cur_groupnum]].attribute.delete_tmp_files) { delete = TRUE; } else if (prompt_yn (cLINES, txt_delete_processed_files, 'y')) { delete = TRUE; } if (delete) { wait_message ("\r\n"); wait_message (txt_deleting); for (i=0 ; i < num_save ; i++) { unlink (save_filename (i)); } } #endif /* INDEX_DAEMON */ } void print_art_seperator_line (fp, mailbox) FILE *fp; int mailbox; { int sep = 0x01; /* Ctrl-A */ if (debug == 2) { sprintf (msg, "Mailbox=[%d] MMDF=[%d]", mailbox, save_to_mmdf_mailbox); error_message (msg, ""); } if (mailbox && save_to_mmdf_mailbox) { fprintf (fp, "%c%c%c%c\n", sep, sep, sep, sep); } else { fputc ('\n', fp); } } *[SRC.TIN-1_22]SCREEN.C;4+,. // 4 -d0@123KPWO 56`ԯ7 o89]VG/HJ/* * Project : tin - a Usenet reader * Module : screen.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 11-07-92 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #ifndef VMS extern int errno; #endif char msg[LEN]; struct t_screen *screen; void info_message (str) char *str; { clear_message (); /* Clear any old messages hanging around */ center_line (cLINES, FALSE, str); /* center the message at screen bottom */ if (! cmd_line) { MoveCursor (cLINES, 0); } } void wait_message (str) char *str; { clear_message (); /* Clear any old messages hanging around */ fputs (str, stdout); fflush (stdout); } void error_message (template, str) char *template; char *str; { errno = 0; clear_message (); /* Clear any old messages hanging around */ fprintf (stderr, template, str); fflush (stderr); if (cmd_line) { fputc ('\n', stderr); fflush (stderr); } else { MoveCursor (cLINES, 0); sleep (3); } } void perror_message (template, str) char *template; char *str; { #ifdef HAVE_SYSERRLIST extern char *sys_errlist[]; #endif char str2[512]; int err = 0; err = errno; clear_message (); /* Clear any old messages hanging around */ sprintf (str2, template, str); err = errno; #ifdef HAVE_SYSERRLIST fprintf (stderr, "%s: %s", str2, sys_errlist[err]); #else # ifdef VMS fprintf (stderr, "%s: %s", str2, strerror(err)); # else fprintf (stderr, "%s: Error: %i", str2, err); # endif #endif errno = 0; if (cmd_line) { fputc ('\n', stderr); fflush (stderr); } else { MoveCursor (cLINES, 0); sleep (3); } } void clear_message () { if (! cmd_line) { MoveCursor (cLINES, 0); CleartoEOLN (); } } void center_line (line, inverse, str) int line; int inverse; char *str; { int pos; if (! cmd_line) { pos = (cCOLS - (int) strlen (str)) / 2; MoveCursor (line, pos); if (inverse) { StartInverse (); } } fputs (str, stdout); fflush (stdout); if (! cmd_line) { if (inverse) { EndInverse (); } } } void draw_arrow (line) int line; { MoveCursor (line, 0); if (draw_arrow_mark) { fputs ("->", stdout); fflush (stdout); } else { StartInverse (); fputs (screen[line-INDEX_TOP].col, stdout); fflush (stdout); EndInverse (); } MoveCursor (cLINES, 0); } void erase_arrow (line) int line; { MoveCursor (line, 0); if (draw_arrow_mark) { fputs (" ", stdout); } else { EndInverse (); fputs (screen[line-INDEX_TOP].col, stdout); } fflush (stdout); } void show_title (title) char *title; { int col; col = (cCOLS - (int) strlen (txt_type_h_for_help))+1; if (col) { MoveCursor (0, col); if (mail_check ()) { /* you have mail message in */ fputs (txt_you_have_mail, stdout); } else { fputs (txt_type_h_for_help, stdout); } } center_line (0, TRUE, title); } void ring_bell () { fputc ('\007', stdout); fflush (stdout); } *[SRC.TIN-1_22]SEARCH.C;1+,.// 4-d0@123KPWO56{ t7ct189]VG/HJ&/* * Project : tin - a Usenet reader * Module : search.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 20-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" extern FILE *note_fp; extern int first_group_on_screen; extern int last_group_on_screen; extern int first_subj_on_screen; extern int last_subj_on_screen; extern int index_point; extern int note_line; extern int note_page; extern int note_end; extern long note_mark[MAX_PAGES]; /* * last search patterns */ char default_author_search[LEN]; char default_group_search[LEN]; char default_subject_search[LEN]; char default_art_search[LEN]; /* * group.c & page.c */ int search_author (index, current_art, forward) int index; int current_art; int forward; { char buf[LEN]; char buf2[LEN]; char group_path[PATH_LEN]; int i, patlen; clear_message (); if (forward) { sprintf (buf2, txt_author_search_forwards, default_author_search); } else { sprintf (buf2, txt_author_search_backwards, default_author_search); } if (! prompt_string (buf2, buf)) { return -1; } if (strlen (buf)) { strcpy (default_author_search, buf); } else { if (default_author_search[0]) { strcpy (buf, default_author_search); } else { info_message (txt_no_search_string); return -1; } } wait_message (txt_searching); make_group_path (active[index].name, group_path); make_lower (default_author_search, buf); patlen = strlen (default_author_search); i = current_art; do { if (forward) { i = next_response (i); if (i < 0) i = base[0]; } else { i = prev_response (i); if (i < 0) i = base[top_base - 1] + num_of_responses (top_base - 1); } if (active[index].attribute.show_only_unread && arts[i].unread != ART_UNREAD) { continue; } if (arts[i].name == (char *) 0) { make_lower (arts[i].from, buf2); } else { sprintf (msg, "%s (%s)", arts[i].from, arts[i].name); make_lower (msg, buf2); } if (str_str (buf2, buf, patlen) != 0) { /* * check if article still exists */ if (stat_article (arts[i].artnum, group_path)) { clear_message (); return i; } } } while (i != current_art); info_message (txt_no_match); return -1; } /* * select.c */ void search_group (forward) int forward; { char buf[LEN]; char buf2[LEN]; int i, patlen; if (! group_top) { info_message (txt_no_groups); return; } clear_message (); if (forward) { sprintf (buf2, txt_search_forwards, default_group_search); } else { sprintf (buf2, txt_search_backwards, default_group_search); } if (! prompt_string (buf2, buf)) { return; } if (strlen (buf)) { strcpy (default_group_search, buf); } else { if (default_group_search[0]) { strcpy (buf, default_group_search); } else { info_message (txt_no_search_string); return; } } wait_message (txt_searching); make_lower (default_group_search, buf); patlen = strlen (default_group_search); i = cur_groupnum; do { if (forward) i++; else i--; if (i >= group_top) i = 0; if (i < 0) i = group_top - 1; if (show_description && active[my_group[i]].description) { sprintf (buf2, "%s %s", active[my_group[i]].name, active[my_group[i]].description); } else { strcpy (buf2, active[my_group[i]].name); } lcase (buf2); if (str_str (buf2, buf, patlen) != 0) { if (_hp_glitch) { erase_group_arrow (); } if (i >= first_group_on_screen && i < last_group_on_screen) { clear_message (); erase_group_arrow (); cur_groupnum = i; draw_group_arrow (); } else { cur_groupnum = i; group_selection_page (); } return; } } while (i != cur_groupnum); info_message (txt_no_match); } /* * group.c */ void search_subject (forward, group) int forward; char *group; { char buf[LEN]; char buf2[LEN]; int i, j, patlen; if (index_point < 0) { info_message (txt_no_arts); return; } clear_message (); if (forward) { sprintf (buf2, txt_search_forwards, default_subject_search); } else { sprintf (buf2, txt_search_backwards, default_subject_search); } if (! prompt_string (buf2, buf)) { return; } if (strlen (buf)) { strcpy (default_subject_search, buf); } else { if (default_subject_search[0]) { strcpy (buf, default_subject_search); } else { info_message (txt_no_search_string); return; } } wait_message (txt_searching); make_lower (default_subject_search, buf); patlen = strlen (default_subject_search); i = index_point; do { if (forward) i++; else i--; if (i >= top_base) i = 0; if (i < 0) i = top_base - 1; j = (int) base[i]; make_lower (arts[j].subject, buf2); if (str_str (buf2, buf, patlen) != 0) { if (_hp_glitch) { erase_subject_arrow (); } if (i >= first_subj_on_screen && i < last_subj_on_screen) { clear_message (); erase_subject_arrow (); index_point = i; draw_subject_arrow (); } else { index_point = i; show_group_page (); } return; } } while (i != index_point); info_message (txt_no_match); } /* * page.c (search article body) */ int search_article (forward) int forward; { char buf[LEN]; char buf2[LEN]; char string[LEN]; char pattern[LEN]; char *p, *q; int ctrl_L; int i, j, patlen; int orig_note_end; int orig_note_page; clear_message (); if (forward) { sprintf (buf2, txt_search_forwards, default_art_search); } else { sprintf (buf2, txt_search_backwards, default_art_search); } if (! prompt_string (buf2, buf)) { return FALSE; } if (strlen (buf)) { strcpy (default_art_search, buf); } else { if (default_art_search[0]) { strcpy (buf, default_art_search); } else { info_message (txt_no_search_string); return FALSE; } } wait_message (txt_searching); make_lower (default_art_search, pattern); patlen = strlen (default_art_search); /* * save current position in article */ orig_note_end = note_end; orig_note_page = note_page; while (! note_end) { note_line = 1; ctrl_L = FALSE; if (note_page == 0) { note_line += 4; } else { note_line += 2; } while (note_line < cLINES) { if (fgets (buf, sizeof buf, note_fp) == NULL) { note_end = TRUE; break; } buf[LEN-1] = '\0'; for (p = buf, q = buf2; *p && *p != '\n' && q<&buf2[LEN]; p++) { if (*p == '\b' && q > buf2) { q--; } else if (*p == '\f') { /* ^L */ *q++ = '^'; *q++ = 'L'; ctrl_L = TRUE; } else if (*p == '\t') { i = q - buf2; j = (i|7) + 1; while (i++ < j) { *q++ = ' '; } } else if (((*p) & 0xFF) < ' ') { *q++ = '^'; *q++ = ((*p) & 0xFF) + '@'; } else { *q++ = *p; } } *q = '\0'; make_lower (buf2, string); if (str_str (string, pattern, patlen) != 0) { fseek (note_fp, note_mark[note_page], 0); return TRUE; } note_line += ((int) strlen(buf2) / cCOLS) + 1; if (ctrl_L) { break; } } if (! note_end) { note_mark[++note_page] = ftell (note_fp); } } note_end = orig_note_end; note_page = orig_note_page; fseek (note_fp, note_mark[note_page], 0); info_message (txt_no_match); return FALSE; } void make_lower (s, t) char *s; char *t; { while (*s) { if (isupper(*s)) *t = tolower(*s); else *t = *s; s++; t++; } *t = 0; } int search_body (group_path, current_art) char *group_path; int current_art; { char pat[LEN]; char buf2[LEN]; int aborted = FALSE; int i, len; int count = 0; clear_message (); sprintf (buf2, txt_search_body, default_art_search); if (! prompt_string (buf2, pat)) { return -1; } if (strlen (pat)) { strcpy (default_art_search, pat); } else { if (default_art_search[0]) { strcpy (pat, default_art_search); } else { info_message (txt_no_search_string); return -1; } } make_lower (default_art_search, pat); len = strlen (pat); i = current_art; do { i = next_response (i); if (i < 0) { i = 0; } if (search_art_body (group_path, &arts[i], pat, len)) { return i; } if (count % MODULO_COUNT_NUM == 0) { if (input_pending ()) { if (read (STDIN_FILENO, buf2, sizeof (buf2)-1)) { if (buf2[0] == ESC || buf2[0] == 'q' || buf2[0] == 'Q') { if (prompt_yn (cLINES, txt_abort_searching, 'y')) { aborted = TRUE; break; } else { printf (txt_searching_body); } } } } if (count == 0) { printf (txt_searching_body); } else { printf ("\b\b\b\b%4d", count); } fflush (stdout); } count++; } while (i != current_art); if (! aborted) { info_message (txt_no_match); } return -1; } int search_art_body (group_path, art, pat, len) char *group_path; struct t_article *art; char *pat; int len; { char buf[LEN]; FILE *fp; fp = open_art_fp (group_path, art->artnum); if (fp == (FILE *) 0) { return FALSE; } while (fgets (buf, sizeof (buf), fp) != NULL) { if (buf[0] == '\n') { break; } } while (fgets (buf, sizeof (buf), fp) != NULL) { lcase (buf); if (str_str (buf, pat, len)) { fclose (fp); return TRUE; } } fclose (fp); return FALSE; } /* * inline conversion of a string to lowercase letters */ void lcase (s) char *s; { while (*s) { if (isupper(*s)) { *s = tolower(*s); } s++; } } *[SRC.TIN-1_22]SELECT.C;1+,.<// 4<:<-d0@123KPWO=56 t7ct189]VG/HJ*/* * Project : tin - a Usenet reader * Module : select.c * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 22-09-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" extern char cvers[LEN]; extern int index_point; char default_goto_group[LEN]; int default_move_group; int cur_groupnum = 0; int first_group_on_screen; int last_group_on_screen; int space_mode; int yank_in_active_file = TRUE; void selection_index (start_groupnum) int start_groupnum; { #ifndef INDEX_DAEMON char buf[LEN]; char post_group[LEN]; int ch, i, n; int patlen; int posted; int scroll_lines; int subscribe_num; cur_groupnum = start_groupnum; mail_setup (); /* record mailbox size for "you have mail" */ #ifdef READ_CHAR_HACK setbuf (stdin, 0); #endif #ifndef USE_CLEARSCREEN ClearScreen(); #endif set_groupname_len (FALSE); /* find longest subscribedto groupname */ group_selection_page (); /* display group selection page */ set_alarm_signal (); /* set alarm signal for resync_active_file () */ while (TRUE) { reread_active_after_posting (); #ifndef DONT_REREAD_ACTIVE_FILE resync_active_file (); /* reread active file if alarm set */ #endif set_xclick_on (); ch = ReadCh (); #ifndef DONT_REREAD_ACTIVE_FILE if (ch != 'q' && ch != 'Q') { resync_active_file (); } #endif if (ch > '0' && ch <= '9') { if (prompt_group_num (ch)) { goto select_read_group; } continue; } switch (ch) { case ESC: /* (ESC) common arrow keys */ #ifdef HAVE_KEY_PREFIX case KEY_PREFIX: #endif switch (get_arrow_key ()) { case KEYMAP_UP: goto select_up; case KEYMAP_DOWN: goto select_down; case KEYMAP_LEFT: goto select_done; case KEYMAP_RIGHT: goto select_read_group; case KEYMAP_PAGE_UP: goto select_page_up; case KEYMAP_PAGE_DOWN: goto select_page_down; case KEYMAP_HOME: if (cur_groupnum != 0) { if (0 < first_group_on_screen) { #ifndef USE_CLEARSCREEN erase_group_arrow(); #endif cur_groupnum = 0; group_selection_page(); } else { erase_group_arrow(); cur_groupnum = 0; draw_group_arrow(); } } break; case KEYMAP_END: goto end_of_list; case KEYMAP_MOUSE: if (xrow < INDEX_TOP) { goto select_page_up; } if (xrow >= INDEX_TOP+last_group_on_screen-first_group_on_screen) { goto select_page_down; } erase_group_arrow (); cur_groupnum = xrow-INDEX_TOP+first_group_on_screen; draw_group_arrow (); if (xmouse > 0) { goto select_read_group; } break; } break; #ifndef NO_SHELL_ESCAPE case '!': shell_escape (); group_selection_page (); break; #endif case '$': /* show last page of groups */ end_of_list: if (cur_groupnum != group_top - 1) { if (group_top - 1 > last_group_on_screen) { #ifndef USE_CLEARSCREEN erase_group_arrow(); #endif cur_groupnum = group_top - 1; group_selection_page(); } else { erase_group_arrow(); cur_groupnum = group_top - 1; draw_group_arrow(); } } break; case '/': /* search forward */ case '?': /* search backward */ i = (ch == '/'); search_group (i); break; case '\r': /* go into group */ case '\n': select_read_group: if (group_top == 0) { info_message (txt_no_groups); break; } n = my_group[cur_groupnum]; if (active[n].min <= active[n].max) { space_mode = pos_first_unread; clear_message (); do { index_point = GRP_UNINDEXED; n = my_group[cur_groupnum]; group_page (active[n].name); } while (index_point == GRP_GOTONEXT || index_point == GRP_CONTINUE); #ifndef DONT_REREAD_ACTIVE_FILE if (! reread_active_file) #endif group_selection_page (); } else { info_message (txt_no_arts); } break; case '\t': /* enter next group containing unread articles */ case 'n': next_unread_group (TRUE); break; case ' ': /* page down */ case ctrl('D'): case ctrl('F'): /* vi style */ select_page_down: if (! group_top) { break; } if (cur_groupnum == group_top - 1) { if (0 < first_group_on_screen) { # ifndef USE_CLEARSCREEN erase_group_arrow(); # endif cur_groupnum = 0; group_selection_page(); } else { erase_group_arrow(); cur_groupnum = 0; draw_group_arrow(); } break; } erase_group_arrow (); scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2); cur_groupnum = ((cur_groupnum + scroll_lines) / scroll_lines) * scroll_lines; if (cur_groupnum >= group_top) { cur_groupnum = (group_top / scroll_lines) * scroll_lines; if (cur_groupnum < group_top - 1) { cur_groupnum = group_top - 1; } } if (cur_groupnum <= first_group_on_screen || cur_groupnum >= last_group_on_screen) group_selection_page (); else draw_group_arrow (); break; case ctrl('K'): /* delete group */ if (group_top <= 0) { info_message (txt_no_groups_to_delete); break; } sprintf (buf, active[my_group[cur_groupnum]].name); sprintf (msg, txt_del_group_in_newsrc, buf); if (prompt_yn (cLINES, msg, 'y')) { delete_group (active[my_group[cur_groupnum]].name); active[my_group[cur_groupnum]].my_group = UNSUBSCRIBED; group_top--; for (i = cur_groupnum; i < group_top; i++) { #if 0 active[my_group[i]].unread = active[my_group[i+1]].unread; #endif my_group[i] = my_group[i+1]; } if (cur_groupnum >= group_top) cur_groupnum = group_top - 1; set_groupname_len (FALSE); group_selection_page (); sprintf (msg, txt_group_deleted, buf); info_message (msg); } break; case ctrl('L'): /* redraw */ #ifndef USE_CLEARSCREEN ClearScreen (); #endif set_xclick_off (); group_selection_page (); break; case ctrl('N'): /* line down */ case 'j': select_down: if (! group_top) { break; } if (cur_groupnum + 1 >= group_top) { if (0 < first_group_on_screen) { # ifndef USE_CLEARSCREEN erase_group_arrow(); # endif cur_groupnum = 0; group_selection_page(); } else { erase_group_arrow(); cur_groupnum = 0; draw_group_arrow(); } break; } if (cur_groupnum + 1 >= last_group_on_screen) { #ifndef USE_CLEARSCREEN erase_group_arrow(); #endif cur_groupnum++; group_selection_page(); } else { erase_group_arrow(); cur_groupnum++; draw_group_arrow(); } break; case ctrl('P'): /* line up */ case 'k': select_up: if (! group_top) { break; } if (cur_groupnum == 0) { if (_hp_glitch) { erase_group_arrow (); } if (group_top > last_group_on_screen) { cur_groupnum = group_top - 1; group_selection_page (); } else { erase_group_arrow (); cur_groupnum = group_top - 1; draw_group_arrow (); } break; } if (_hp_glitch) { erase_group_arrow (); } if (cur_groupnum <= first_group_on_screen) { cur_groupnum--; group_selection_page (); } else { erase_group_arrow (); cur_groupnum--; draw_group_arrow (); } break; case ctrl('R'): /* reset .newsrc */ if (prompt_yn (cLINES, txt_reset_newsrc, 'n')) { reset_newsrc (); cur_groupnum = 0; group_selection_page (); } break; case 'b': /* page up */ case ctrl('U'): case ctrl('B'): /* vi style */ select_page_up: if (! group_top) { break; } if (cur_groupnum == 0) { if (_hp_glitch) { erase_group_arrow (); } if (group_top > last_group_on_screen) { cur_groupnum = group_top - 1; group_selection_page (); } else { erase_group_arrow (); cur_groupnum = group_top - 1; draw_group_arrow (); } break; } erase_group_arrow (); scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2); if ((n = cur_groupnum % scroll_lines) > 0) { cur_groupnum = cur_groupnum - n; } else { cur_groupnum = ((cur_groupnum - scroll_lines) / scroll_lines) * scroll_lines; } if (cur_groupnum < 0) { cur_groupnum = 0; } if (cur_groupnum < first_group_on_screen || cur_groupnum >= last_group_on_screen) group_selection_page (); else draw_group_arrow (); break; case 'c': /* catchup - mark all articles as read */ case 'C': /* and goto next unread group */ if (group_top == 0) { break; } catchup_group ((ch == 'C')); break; case 'd': /* toggle newsgroup descriptions */ show_description = !show_description; if (show_description) { read_newsgroups_file (); } set_groupname_len (FALSE); group_selection_page (); break; case 'g': /* prompt for a new group name */ if ((n = choose_new_group ()) >= 0) { erase_group_arrow (); if (active[my_group[n]].my_group != SUBSCRIBED) { subscribe (active[my_group[n]].name, ':', my_group[n], FALSE); cur_groupnum = reposition_group (active[my_group[n]].name, (n ? n : cur_groupnum)); } else { cur_groupnum = n; } set_groupname_len (FALSE); if (cur_groupnum < first_group_on_screen || cur_groupnum >= last_group_on_screen || cur_groupnum != n) { group_selection_page(); } else { clear_message (); draw_group_arrow(); } } break; case 'h': /* help */ show_info_page (HELP_INFO, help_select, txt_group_select_com); group_selection_page (); break; case 'H': /* toggle mini help menu */ toggle_mini_help (SELECT_LEVEL); group_selection_page (); break; case 'I': /* toggle inverse video */ erase_group_arrow (); toggle_inverse_video (); group_selection_page (); break; case 'l': /* list available spooldirs */ if (spooldir_index ()) { group_selection_page (); } break; case 'm': /* reposition group within group list */ if (active[my_group[cur_groupnum]].my_group == SUBSCRIBED) { n = cur_groupnum; cur_groupnum = reposition_group (active[my_group[n]].name, n); if [~ TIN-1_22.BCKd[SRC.TIN-1_22]SELECT.C;1<;J(_hp_glitch) { erase_group_arrow (); } if (cur_groupnum < first_group_on_screen || cur_groupnum >= last_group_on_screen || cur_groupnum != n) { group_selection_page (); } else { i = cur_groupnum; cur_groupnum = n; erase_group_arrow (); cur_groupnum = i; clear_message (); draw_group_arrow (); } } break; case 'M': /* options menu */ set_alarm_clock_off (); change_rcfile ("", TRUE); free_attributes_array (); read_attributes_file (); group_selection_page (); set_alarm_clock_on (); break; case 'N': /* goto next unread group */ next_unread_group (FALSE); break; case 'q': /* quit */ case 'Q': select_done: write_rcfile (); tin_done (0); break; case 'r': /* * If in show_only_unread_groups mode toggle * all subscribed to groups and only groups * that contain unread articles * * Disabled when started with cmdline groups */ if (! read_cmd_line_groups ()) { show_only_unread_groups = !show_only_unread_groups; if (show_only_unread_groups) { wait_message (txt_reading_new_groups); } else { wait_message (txt_reading_all_groups); } toggle_my_groups (show_only_unread_groups, ""); set_groupname_len (FALSE); group_selection_page (); } break; case 'R': /* bug/gripe/comment mailed to author */ mail_bug_report (); #ifndef USE_CLEARSCREEN ClearScreen (); #endif group_selection_page (); break; case 's': /* subscribe to current group */ if (group_top == 0) { break; } if (active[my_group[cur_groupnum]].my_group != SUBSCRIBED) { mark_screen (SELECT_LEVEL, cur_groupnum - first_group_on_screen, 2, " "); subscribe (active[my_group[cur_groupnum]].name, ':', my_group[cur_groupnum], FALSE); sprintf (buf, txt_subscribed_to, active[my_group[cur_groupnum]].name); info_message (buf); } break; case 'S': /* subscribe to groups matching pattern */ if (group_top == 0) { break; } if (prompt_string (txt_subscribe_pattern, buf) && buf[0]) { wait_message (txt_subscribing); patlen = strlen (buf); for (subscribe_num=0, i=0 ; i < group_top ; i++) { #ifdef NO_REGEX if (str_str (active[my_group[i]].name, buf, patlen)) { #else if (wildmat (active[my_group[i]].name, buf)) { #endif if (active[my_group[i]].my_group != SUBSCRIBED) { #ifndef SLOW_SCREEN_UPDATE sprintf (msg, txt_subscribing_to, active[my_group[i]].name); wait_message (msg); #endif subscribe (active[my_group[i]].name, ':', my_group[i], FALSE); } subscribe_num++; } } if (subscribe_num) { group_selection_page (); sprintf (buf, txt_subscribed_num_groups, subscribe_num); info_message (buf); } else { info_message (txt_no_match); } } else { clear_message (); } break; case 'u': /* unsubscribe to current group */ if (group_top == 0) { break; } if (active[my_group[cur_groupnum]].my_group == SUBSCRIBED) { mark_screen (SELECT_LEVEL, cur_groupnum - first_group_on_screen, 2, "u"); subscribe(active[my_group[cur_groupnum]].name, '!', my_group[cur_groupnum], FALSE); sprintf(buf, txt_unsubscribed_to,active[my_group[cur_groupnum]].name); info_message(buf); } goto_next_group_on_screen (); break; case 'U': /* unsubscribe to groups matching pattern */ if (group_top == 0) { break; } if (prompt_string (txt_unsubscribe_pattern, buf) && buf[0]) { wait_message (txt_unsubscribing); patlen = strlen (buf); for (subscribe_num=0, i=0 ; i < group_top ; i++) { #ifdef NO_REGEX if (str_str (active[my_group[i]].name, buf, patlen)) { #else if (wildmat (active[my_group[i]].name, buf)) { #endif if (active[my_group[i]].my_group == SUBSCRIBED) { #ifndef SLOW_SCREEN_UPDATE sprintf (msg, txt_unsubscribing_from, active[my_group[i]].name); wait_message (msg); #endif subscribe (active[my_group[i]].name, '!', my_group[i], FALSE); } subscribe_num++; } } if (subscribe_num) { group_selection_page (); sprintf (buf, txt_unsubscribed_num_groups, subscribe_num); info_message (buf); } else { info_message (txt_no_match); } } else { clear_message (); } break; case 'v': /* show tin version */ info_message (cvers); break; case 'w': /* post a basenote */ if (can_post) { if (group_top == 0) { if (! prompt_string (txt_post_newsgroup, buf)) break; if (buf[0] == '\0') break; strcpy (post_group, buf); } else { strcpy (post_group, active[my_group[cur_groupnum]].name); } if (post_article (post_group, &posted)) { group_selection_page (); } } break; case 'W': /* display messages posted by user */ if (user_posted_messages ()) { group_selection_page (); } break; case 'y': /* pull in rest of groups from active */ if (yank_in_active_file) { wait_message (txt_yanking_all_groups); set_alarm_clock_off (); n = group_top; for (i = 0; i < num_active; i++) { active[i].my_group = UNSUBSCRIBED; } read_newsrc (FALSE); for (i = 0; i < num_active; i++) { if (active[i].my_group & UNSUBSCRIBED) { active[i].my_group &= ~UNSUBSCRIBED; active[i].unread = -1; my_group[group_top] = i; group_top++; } } if (n < group_top) { sprintf (buf, txt_added_groups, group_top - n, group_top - n == 1 ? "" : txt_plural); set_groupname_len (yank_in_active_file); group_selection_page (); info_message (buf); } else { info_message (txt_no_groups_to_yank_in); } yank_in_active_file = FALSE; } else { wait_message (txt_yanking_sub_groups); read_newsrc (TRUE); if (_hp_glitch) { erase_group_arrow (); } cur_groupnum = group_top - 1; set_groupname_len (yank_in_active_file); group_selection_page (); yank_in_active_file = TRUE; set_alarm_clock_on (); } break; case 'Y': /* yank active file to see if any new news */ yank_active_file (); break; case 'z': /* mark group unread */ if (group_top == 0) { break; } n = cur_groupnum; update_newsrc (active[my_group[n]].name, my_group[n], TRUE); cur_groupnum = 0; group_top = 0; read_newsrc (TRUE); cur_groupnum = n; if (active[my_group[cur_groupnum]].unread) { sprintf (msg, "%5d", active[my_group[cur_groupnum]].unread); } else { strcpy (msg, " "); } mark_screen (SELECT_LEVEL, cur_groupnum - first_group_on_screen, 9, msg); break; case 'Z': /* undelete groups deleted by ctrl-K */ if (undel_group ()) { set_groupname_len (FALSE); group_selection_page (); info_message (txt_group_undeleted); } break; default: info_message(txt_bad_command); } } #endif /* INDEX_DAEMON */ } void group_selection_page () { #ifndef INDEX_DAEMON char buf[LEN]; char new[10]; char subs; int i, j, n; int blank_len; set_signals_select (); #ifdef USE_CLEARSCREEN ClearScreen (); #else MoveCursor (0, 0); /* top left corner */ CleartoEOLN (); #endif if (read_news_via_nntp || xspooldir_supported) { sprintf (buf, "%s (%s %d%s)", txt_group_selection, (xspooldir_supported ? spooldir_alias : nntp_server), group_top, (show_only_unread_groups ? " R" : "")); } else { sprintf (buf, "%s (%d%s)", txt_group_selection, group_top, (show_only_unread_groups ? " R" : "")); } show_title (buf); #ifndef USE_CLEARSCREEN MoveCursor (1, 0); CleartoEOLN (); #endif MoveCursor (INDEX_TOP, 0); if (cur_groupnum >= group_top) { cur_groupnum = group_top - 1; } if (cur_groupnum < 0) { cur_groupnum = 0; } if (NOTESLINES <= 0) { first_group_on_screen = 0; } else { first_group_on_screen = (cur_groupnum / NOTESLINES) * NOTESLINES; if (first_group_on_screen < 0) { first_group_on_screen = 0; } } last_group_on_screen = first_group_on_screen + NOTESLINES; if (last_group_on_screen >= group_top) { last_group_on_screen = group_top; first_group_on_screen = (cur_groupnum / NOTESLINES) * NOTESLINES; if (first_group_on_screen == last_group_on_screen || first_group_on_screen < 0) { if (first_group_on_screen < 0) { first_group_on_screen = 0; } else { first_group_on_screen = last_group_on_screen - NOTESLINES; } } } if (group_top == 0) { first_group_on_screen = 0; last_group_on_screen = 0; } if (show_description) { blank_len = (cCOLS - (groupname_len + SELECT_MISC_COLS)) + 2; } else { blank_len = (cCOLS - (groupname_len + SELECT_MISC_COLS)) + 4; } for (j=0, i=first_group_on_screen; i < last_group_on_screen; i++, j++) { switch (active[my_group[i]].unread) { case -2: strcpy (new, " ?"); break; case -1: strcpy (new, " -"); break; case 0: strcpy (new, " "); break; default: sprintf (new, "%5.d", active[my_group[i]].unread); } n = my_group[i]; if (active[n].my_group & SUBSCRIBED) { /* subscribed? */ subs = ' '; } else { subs = 'u'; /* u next to unsubscribed groups */ } if (show_description) { if (draw_arrow_mark) { sprintf (screen[j].col, " %c %4.d %s %-*.*s %-*.*s\r\n", subs, i+1, new, groupname_len, groupname_len, active[n].name, blank_len, blank_len, (active[n].description ? active[n].description : " ")); } else { sprintf (screen[j].col, " %c %4.d %s %-*.*s %-*.*s\r\n", subs, i+1, new, groupname_len, groupname_len, active[n].name, blank_len, blank_len, (active[n].description ? active[n].description : " ")); } } else { if (draw_arrow_mark) { sprintf (screen[j].col, " %c %4.d %s %-*.*s\r\n", subs, i+1, new, groupname_len, groupname_len, active[n].name); } else { sprintf (screen[j].col, " %c %4.d %s %-*.*s%*s\r\n", subs, i+1, new, groupname_len, groupname_len, active[n].name, blank_len, " "); } } if (slow_speed_terminal) { strip_line (screen[j].col, strlen (screen[j].col)); strcat (screen[j].col, "\r\n"); } CleartoEOLN (); fputs (screen[j].col, stdout); } #ifndef USE_CLEARSCREEN CleartoEOS (); #endif show_mini_help (SELECT_LEVEL); if (group_top <= 0) { info_message (txt_no_groups); return; } else if (last_group_on_screen == group_top) { info_message (txt_end_of_groups); } draw_group_arrow (); #endif /* INDEX_DAEMON */ } int prompt_group_num (ch) int ch; { int num; clear_message (); if ((num = prompt_num (ch, txt_select_group)) == -1) { clear_message (); return FALSE; } num--; /* index from 0 (internal) vs. 1 (user) */ if (num < 0) { num = 0; } if (num >= group_top) { num = group_top - 1; } if (num >= first_group_on_screen && num < last_group_on_screen) { erase_group_arrow (); cur_groupnum = num; draw_group_arrow (); } else { #ifndef USE_CLEARSCREEN erase_group_arrow (); #endif cur_groupnum = num; group_selection_page (); } return TRUE; } void erase_group_arrow () { erase_arrow (INDEX_TOP + (cur_groupnum-first_group_on_screen)); } void draw_group_arrow() { draw_arrow (INDEX_TOP + (cur_groupnum-first_group_on_screen)); } void yank_active_file () { reread_active_file = TRUE; resync_active_file (); } int choose_new_group () { char buf[LEN]; char *p; int ret; if (! group_top && show_only_unread_groups) { return -1; } sprintf (msg, txt_newsgroup, default_goto_group); if (! prompt_string (msg, buf)) { return -1; } if (strlen (buf)) { strcpy (default_goto_group, buf); } else { if (default_goto_group[0]) { strcpy (buf, default_goto_group); } else { return -1; } } for (p = buf; *p && (*p == ' ' || *p == '\t'); p++) continue; if (*p == '\0') return -1; clear_message (); if ((ret = add_group (p, TRUE)) < 0) { sprintf (msg, txt_not_in_active_file, p); info_message (msg); } return ret; } /* * Add a group to the users selection list (my_group[]) * Return the index of my_group[] if group is added or was already * there. Return -1 if named group is not in active[]. */ int add_group (s, get_unread) char *s; int get_unread; /* look in .newsrc for sequencer unread info? */ { long h; int i, j; h = hash_groupname (s); for (i = group_hash[h]; i >= 0; i = active[i].next) { if (strcmp (s, active[i].name) == 0) { for (j = 0; j < group_top; j++) { if (my_group[j] == i) { return j; } } active[i].my_group &= ~UNSUBSCRIBED; /* mark that we got it */ my_group[group_top] = i; if (get_unread) { active[my_group[group_top]].unread = get_line_unread (s, i); } else { active[my_group[group_top]].unread = -1; } group_top++; return (group_top - 1); } } return -1; } int reposition_group (group, default_num) char *group; int default_num; { char buf[LEN]; char pos[LEN]; int pos_num = 0; sprintf (buf, txt_newsgroup_position, group, (default_move_group ? default_move_group : default_num+1)); if (! prompt_string (buf, pos)) { return default_num; } if (strlen (pos)) { if (pos[0] == '$') { pos_num = group_top; } else { pos_num = atoi (pos); } } else { if (default_move_group) { pos_num = default_move_group; } else { return default_num; } } if (pos_num > group_top) { pos_num = group_top; } else if (pos_num <= 0) { pos_num = 1; } sprintf (buf, txt_moving, group); wait_message (buf); if (pos_group_in_newsrc (group, pos_num)) { read_newsrc (TRUE); default_move_group = pos_num; return (pos_num-1); } else { default_move_group = default_num + 1; return (default_num); } } void catchup_group (goto_next_unread_group) int goto_next_unread_group; { sprintf (msg, txt_mark_group_read, active[my_group[cur_groupnum]].name); if (! confirm_action || prompt_yn (cLINES, msg, 'y')) { active[my_group[cur_groupnum]].unread = 0; mark_group_read (active[my_group[cur_groupnum]].name, my_group[cur_groupnum]); mark_screen (SELECT_LEVEL, cur_groupnum - first_group_on_screen, 9, " "); goto_next_group_on_screen (); if (goto_next_unread_group) { next_unread_group (FALSE); } } } void next_unread_group (enter_group) int enter_group; { int i, all_groups_read = TRUE; for (i = cur_groupnum ; i < group_top ; i++) { if (active[my_group[i]].unread != 0) { all_groups_read = FALSE; erase_group_arrow (); break; } } if (all_groups_read) { for (i = 0 ; i < cur_groupnum ; i++) { if (active[my_group[i]].unread != 0) { all_groups_read = FALSE; break; } } } if (all_groups_read) { info_message (txt_no_groups_to_read); return; } if (i != cur_groupnum) { erase_group_arrow (); } cur_groupnum = i; if (cur_groupnum < first_group_on_screen || cur_groupnum >= last_group_on_screen) { group_selection_page (); } else { draw_group_arrow (); } space_mode = pos_first_unread; if (enter_group) { clear_message (); do { index_point = GRP_UNINDEXED; group_page (active[my_group[cur_groupnum]].name); } while (index_point == GRP_GOTONEXT || index_point == GRP_CONTINUE); group_selection_page (); } } /* * Calculate max length of groupname field for group selection level. * If all_group is TRUE check all groups in active file otherwise * just subscribed to groups. */ void set_groupname_len (all_groups) int all_groups; { int len; register int i; groupname_len = 0; if (all_groups) { for (i = 0 ; i < num_active ; i++) { len = strlen (active[i].name); if (len > groupname_len) { groupname_len = len; } } } else { for (i = 0 ; i < group_top ; i++) { len = strlen (active[my_group[i]].name); if (len > groupname_len) { groupname_len = len; } } } if (groupname_len >= (cCOLS - SELECT_MISC_COLS)) { groupname_len = cCOLS - SELECT_MISC_COLS - 1; if (groupname_len < 0) { groupname_len = 0; } } /* * If newsgroups descriptions are ON then cut off groupnames * to specified max. length otherwise display full length */ if (show_description && groupname_len > groupname_max_length) { groupname_len = groupname_max_length; } } void toggle_my_groups (only_unread_groups, group) int only_unread_groups; char *group; { #ifndef INDEX_DAEMON char buf[8192]; char old_curr_group[PATH_LEN]; char *ptr; FILE *fp; int active_idx = 0; int group_num = cur_groupnum; register int i = 0, j = 0; if ((fp = fopen (newsrc, "r")) != (FILE *) 0) { /* * Save current or next group with unread arts for later use */ old_curr_group[0] = '\0'; if (group_top) { if (! only_unread_groups || reread_active_file) { if (strlen (group)) { if ((i = find_group_index (group)) >= 0) { active_idx = i; } } else { active_idx = my_group[group_num]; } my_strncpy (old_curr_group, active[active_idx].name, sizeof (old_curr_group)); } else { for (i = group_num ; i < group_top ; i++) { if (active[my_group[i]].unread) { my_strncpy (old_curr_group, active[my_group[i]].name, sizeof (old_curr_group)); break; } } } } group_top = 0; while (fgets (buf, sizeof (buf), fp) != (char *) 0) { ptr = (char *) strchr (buf, ':'); if (ptr != (char *) 0) { *ptr = '\0'; if ((i = find_group_index (buf)) >= 0) { if (only_unread_groups) { if (active[i].unread) { my_group[group_top] = i; group_top++; } } else { my_group[group_top] = i; group_top++; } } } } fclose (fp); /* * Try and reposition on same or next group before toggling */ cur_groupnum = 0; if ((i = find_group_index (old_curr_group)) >= 0) { for (j = 0 ; j < group_top ; j++) { if (my_group[j] == i) { cur_groupnum = j; break; } } } } #endif /* INDEX_DAEMON */ } void goto_next_group_on_screen () { if (_hp_glitch) { erase_group_arrow (); } if (cur_groupnum+1 < last_group_on_screen) { erase_group_arrow (); cur_groupnum++; draw_group_arrow (); } else { cur_groupnum++; group_selection_page (); } } /* * Strip trailing blanks */ void strip_line (line, len) char *line; int len; { char *ptr = line + (len - 1); while (*ptr == ' ' || *ptr == '\r' || *ptr == '\n') { ptr--; } *++ptr = '\0'; } *[SRC.TIN-1_22]SERVER.PATCH;1+,{.?// 4?>-d0@123KPWO@568t7c189]VG/HJ0*** server/Makefile Wed Sep 30 14:34:44 1992 --- server/Makefile Wed Nov 18 19:02:45 1992 *************** *** 6,18 **** ahbs.o globals.o group.o help.o ihave.o list.o misc.o netaux.o \ newgroups.o newnews.o nextlast.o ngmatch.o post.o parsit.o scandir.o \ slave.o spawn.o strcasecmp.o subnet.o time.o xhdr.o fakesyslog.o \ ! batch.o auth.o timer.o ../common/version.o SRVRSRC = main.c serve.c access.c access_inet.c access_dnet.c active.c \ ahbs.c globals.c group.c help.c ihave.c list.c misc.c netaux.c \ newgroups.c newnews.c nextlast.c ngmatch.c post.c parsit.c scandir.c \ slave.c spawn.c strcasecmp.c subnet.c time.c xhdr.c fakesyslog.c \ ! batch.c auth.c timer.c ../common/version.c SRVRINC = common.h ../common/conf.h ../common/nntp.h timer.h --- 6,20 ---- ahbs.o globals.o group.o help.o ihave.o list.o misc.o netaux.o \ newgroups.o newnews.o nextlast.o ngmatch.o post.o parsit.o scandir.o \ slave.o spawn.o strcasecmp.o subnet.o time.o xhdr.o fakesyslog.o \ ! batch.o auth.o timer.o ../common/version.o xuser.o xindex.o \ ! xoverview.o xmotd.o SRVRSRC = main.c serve.c access.c access_inet.c access_dnet.c active.c \ ahbs.c globals.c group.c help.c ihave.c list.c misc.c netaux.c \ newgroups.c newnews.c nextlast.c ngmatch.c post.c parsit.c scandir.c \ slave.c spawn.c strcasecmp.c subnet.c time.c xhdr.c fakesyslog.c \ ! batch.c auth.c timer.c ../common/version.c xuser.c xindex.c \ ! xoverview.c xmotd.c SRVRINC = common.h ../common/conf.h ../common/nntp.h timer.h *** server/access.c Wed Sep 30 14:34:42 1992 --- server/access.c Tue Sep 15 20:09:54 1992 *************** *** 44,50 **** #ifdef AUTH extern int Needauth; ! #endif AUTH host_access(canread, canpost, canxfer, gdlist) int *canread, *canpost, *canxfer; --- 44,50 ---- #ifdef AUTH extern int Needauth; ! #endif /* AUTH */ host_access(canread, canpost, canxfer, gdlist) int *canread, *canpost, *canxfer; *************** *** 230,236 **** /* do we require a userid and password for this guy? */ if (isupper(readperm[0]) || isupper(postperm[0])) Needauth = 1; ! #endif AUTH } #ifdef DOMAINMATCH --- 230,236 ---- /* do we require a userid and password for this guy? */ if (isupper(readperm[0]) || isupper(postperm[0])) Needauth = 1; ! #endif /* AUTH */ } #ifdef DOMAINMATCH *************** *** 267,270 **** return (0); } ! #endif DOMAINMATCH --- 267,270 ---- return (0); } ! #endif /* DOMAINMATCH */ *** server/access_inet.c Wed Sep 30 14:34:45 1992 --- server/access_inet.c Tue Sep 15 20:09:56 1992 *************** *** 77,83 **** } #else subnet_name[0] = '\0'; ! #endif SUBNET hp = gethostbyaddr((char *) &sin->sin_addr.s_addr, sizeof (sin->sin_addr.s_addr), AF_INET); --- 77,83 ---- } #else subnet_name[0] = '\0'; ! #endif /* SUBNET */ hp = gethostbyaddr((char *) &sin->sin_addr.s_addr, sizeof (sin->sin_addr.s_addr), AF_INET); *** server/auth.c Wed Sep 30 14:34:49 1992 --- server/auth.c Tue Sep 15 20:10:00 1992 *************** *** 149,152 **** Needauth = 0; } ! #endif AUTH --- 149,152 ---- Needauth = 0; } ! #endif /* AUTH */ *** server/batch.c Wed Sep 30 14:34:45 1992 --- server/batch.c Tue Sep 15 20:09:58 1992 *************** *** 255,265 **** #ifdef SYSLOG #ifdef LOG syslog(LOG_ERR, "%s transfer_timeout", hostname); ! #endif LOG #endif (void) unlink(tempfile); exit(1); ! #endif XFER_TIMEOUT } /* --- 255,265 ---- #ifdef SYSLOG #ifdef LOG syslog(LOG_ERR, "%s transfer_timeout", hostname); ! #endif /* LOG */ #endif (void) unlink(tempfile); exit(1); ! #endif /* XFER_TIMEOUT */ } /* *** server/common.h Wed Sep 30 14:34:43 1992 --- server/common.h Tue Sep 15 20:09:55 1992 *************** *** 48,53 **** --- 48,56 ---- #endif #else /* not NDIR */ # include + # ifdef ISC + # include + # endif #endif /* not NDIR */ #ifdef FCNTL *************** *** 157,162 **** --- 160,166 ---- extern char spooldir[]; extern char activefile[]; extern char distributionsfile[]; + extern char subscriptionsfile[]; extern char newsgroupsfile[]; extern char accessfile[]; extern char historyfile[]; *** server/fakesyslog.c Wed Sep 30 14:34:46 1992 --- server/fakesyslog.c Tue Sep 15 20:09:58 1992 *************** *** 138,144 **** (void) strcpy(buf, ctime(&clock)+4); *(bp = buf + 16) = '\0'; ! (void) sprintf(bp, "localhost %s", ident ? ident : ""); bp += strlen(bp); if (opt&LOG_PID) { --- 138,145 ---- (void) strcpy(buf, ctime(&clock)+4); *(bp = buf + 16) = '\0'; ! /* (void) sprintf(bp, "localhost %s", ident ? ident : ""); */ ! (void) sprintf(bp, "local %s", ident ? ident : ""); bp += strlen(bp); if (opt&LOG_PID) { *** server/fakesyslog.h Wed Sep 30 14:34:50 1992 --- server/fakesyslog.h Tue Sep 15 20:10:02 1992 *************** *** 62,65 **** #define LOG_NDELAY 0 #define LOG_NOWAIT 0 ! #endif FAKESYSLOG --- 62,65 ---- #define LOG_NDELAY 0 #define LOG_NOWAIT 0 ! #endif /* FAKESYSLOG */ *** server/globals.c Wed Sep 30 14:34:46 1992 --- server/globals.c Tue Sep 15 20:09:58 1992 *************** *** 16,21 **** --- 16,22 ---- char activefile[] = ACTIVE_FILE; char accessfile[] = ACCESS_FILE; char distributionsfile[] = DISTRIBUTIONS_FILE; + char subscriptionsfile[] = SUBSCRIPTIONS_FILE; char newsgroupsfile[] = NEWSGROUPS_FILE; char historyfile[] = HISTORY_FILE; #ifdef ACTIVE_TIMES_FILE *************** *** 54,60 **** #ifdef AUTH int Needauth; /* 1 if we need to do authorization */ char User[10]; /* username for authentication */ ! #endif AUTH #ifdef LOG int arts_acsd; --- 55,61 ---- #ifdef AUTH int Needauth; /* 1 if we need to do authorization */ char User[10]; /* username for authentication */ ! #endif /* AUTH */ #ifdef LOG int arts_acsd; *** server/group.c Wed Sep 30 14:34:46 1992 --- server/group.c Tue Sep 15 20:09:58 1992 *************** *** 49,54 **** --- 49,55 ---- return; } + reqlist[0] = argv[1]; reqlist[1] = NULL; *************** *** 70,79 **** close_crnt(); (void) chdir(spooldir); - #ifdef LOG - syslog(LOG_INFO, "%s group %s", hostname, argv[1]); - #endif - while ((cp = index(argv[1], '.')) != (char *) NULL) *cp = '/'; --- 71,76 ---- *************** *** 96,101 **** --- 93,103 ---- art_ptr = 0; ingroup = 1; + + #ifdef LOG + syslog(LOG_INFO, "%s group=%s high=%d low=%d arts=%d", + hostname, argv[1], high_msg, low_msg, num_arts); + #endif while ((cp = index(argv[1], '/')) != (char *) NULL) *cp = '.'; *** server/help.c Wed Sep 30 14:34:46 1992 --- server/help.c Wed Nov 18 19:09:10 1992 *************** *** 21,28 **** printf("NEXT POST QUIT\r\n"); printf("STAT NEWGROUPS HELP\r\n"); printf("IHAVE NEWNEWS SLAVE\r\n"); ! printf("\r\nAdditionally, the following extention is supported:\r\n\r\n"); printf("XHDR Retrieve a single header line from a range of articles.\r\n"); printf("\r\n"); printf("Bugs to Stan Barber (Internet: nntp@tmc.edu; UUCP: ...!bcm!nntp)\r\n"); printf(".\r\n"); --- 21,45 ---- printf("NEXT POST QUIT\r\n"); printf("STAT NEWGROUPS HELP\r\n"); printf("IHAVE NEWNEWS SLAVE\r\n"); ! #if defined(XHDR) || defined(XINDEX) || defined(XMOTD) || \ ! defined(XOVERVIEW) || defined(XUSER) ! printf("\r\nAdditionally, the following extentions are supported:\r\n\r\n"); ! # ifdef XHDR printf("XHDR Retrieve a single header line from a range of articles.\r\n"); + # endif + # ifdef XINDEX + printf("XINDEX Retrieve a tin style index file.\r\n"); + # endif + # ifdef XMOTD + printf("XMOTD Display the news message of the day file.\r\n"); + # endif + # ifdef XOVERVIEW + printf("XOVERVIEW Retrieve a .overview style index file.\r\n"); + # endif + # ifdef XUSER + printf("XUSER Log a clients username to nntp logfile.\r\n"); + # endif + #endif printf("\r\n"); printf("Bugs to Stan Barber (Internet: nntp@tmc.edu; UUCP: ...!bcm!nntp)\r\n"); printf(".\r\n"); *** server/ihave.c Wed Sep 30 14:34:46 1992 --- server/ihave.c Tue Sep 15 20:09:59 1992 *************** *** 8,14 **** int ih_accepted; int ih_rejected; int ih_failed; ! #endif LOG /* * IHAVE --- 8,14 ---- int ih_accepted; int ih_rejected; int ih_failed; ! #endif /* LOG */ /* * IHAVE *************** *** 50,57 **** ih_rejected++; #ifdef IHAVE_DEBUG syslog(LOG_DEBUG, "%s ihave %s rejected", hostname, argv[1]); ! #endif IHAVE_DEBUG ! #endif LOG return; } --- 50,57 ---- ih_rejected++; #ifdef IHAVE_DEBUG syslog(LOG_DEBUG, "%s ihave %s rejected", hostname, argv[1]); ! #endif /* IHAVE_DEBUG */ ! #endif /* LOG */ return; } *************** *** 100,106 **** #ifdef IHAVE_DEBUG syslog(LOG_DEBUG, "%s ihave %s accepted %s", hostname, argv[1], retcode == 1 ? "succeeded" : "failed"); ! #endif IHAVE_DEBUG ! #endif LOG } --- 100,106 ---- #ifdef IHAVE_DEBUG syslog(LOG_DEBUG, "%s ihave %s accepted %s", hostname, argv[1], retcode == 1 ? "succeeded" : "failed"); ! #endif /* IHAVE_DEBUG */ ! #endif /* LOG */ } *** server/list.c Wed Sep 30 14:34:46 1992 --- server/list.c Tue Sep 15 20:09:59 1992 *************** *** 7,14 **** /* * LIST * ! * List active newsgroups, newsgroup descriptions, and distributions. * */ list(argc, argv) --- 7,17 ---- /* * LIST * ! * List active newsgroups, newsgroup descriptions, distributions ! * and subscriptions. * + * + * */ list(argc, argv) *************** *** 45,52 **** filename = newsgroupsfile; items = "newsgroup descriptions"; format = "Descriptions in form \"group description\"."; } else { ! printf("%d Usage: LIST [ACTIVE|NEWSGROUPS|DISTRIBUTIONS]\r\n", ERR_CMDSYN); (void) fflush(stdout); return; --- 48,59 ---- filename = newsgroupsfile; items = "newsgroup descriptions"; format = "Descriptions in form \"group description\"."; + } else if (argc == 2 && !strcasecmp(argv[1],"subscriptions")){ + filename = subscriptionsfile; + items = "automatic group subscriptions"; + format = "Subscriptions in form \"group\"."; } else { ! printf("%d Usage: LIST [ACTIVE|NEWSGROUPS|DISTRIBUTIONS|SUBSCRIPTIONS]\r\n", ERR_CMDSYN); (void) fflush(stdout); return; *** server/misc.c Wed Sep 30 14:34:43 1992 --- server/misc.c Tue Sep 15 20:09:56 1992 *************** *** 90,98 **** # ifndef DBM # ifndef USGHIST # define USGHIST ! # endif not USGHIST ! # endif not DBM ! #endif not DBM char * gethistent(msg_id, lookup) --- 90,98 ---- # ifndef DBM # ifndef USGHIST # define USGHIST ! # endif /* not USGHIST */ ! # endif /* not DBM */ ! #endif /* not DBM */ char * gethistent(msg_id, lookup) *************** *** 107,121 **** #ifdef USGHIST char *histfile(); register int len; ! #else not USGHIST #ifdef DBM static int dbopen = 0; datum fetch(); ! #else not DBM static DBM *db = NULL; /* History file, dbm version */ ! #endif DBM ! datum key, content; ! #endif USGHIST static FILE *hfp = NULL; /* history file, text version */ #ifdef CNEWS --- 107,121 ---- #ifdef USGHIST char *histfile(); register int len; ! #else /* not USGHIST */ #ifdef DBM static int dbopen = 0; datum fetch(); ! #else /* not DBM */ static DBM *db = NULL; /* History file, dbm version */ ! #endif /* DBM */ ! datum key, content; ! #endif /* USGHIST */ static FILE *hfp = NULL; /* history file, text version */ #ifdef CNEWS *************** *** 140,146 **** if (hfp == NULL) { #ifdef SYSLOG syslog(LOG_ERR, "gethistent: histfile: %m"); ! #endif SYSLOG return (NULL); } --- 140,146 ---- if (hfp == NULL) { #ifdef SYSLOG syslog(LOG_ERR, "gethistent: histfile: %m"); ! #endif /* SYSLOG */ return (NULL); } *************** *** 153,159 **** (void) fclose(hfp); return (NULL); } ! #else not USGHIST #ifdef DBM if (!dbopen) { if (dbminit(historyfile) < 0) { --- 153,159 ---- (void) fclose(hfp); return (NULL); WD~ TIN-1_22.BCK{d[SRC.TIN-1_22]SERVER.PATCH;1?L } ! #else /* not USGHIST */ #ifdef DBM if (!dbopen) { if (dbminit(historyfile) < 0) { *************** *** 160,166 **** #ifdef SYSLOG syslog(LOG_ERR, "openartbyid: dbminit %s: %m", historyfile); ! #endif SYSLOG return (NULL); } else dbopen = 1; --- 160,166 ---- #ifdef SYSLOG syslog(LOG_ERR, "openartbyid: dbminit %s: %m", historyfile); ! #endif /* SYSLOG */ return (NULL); } else dbopen = 1; *************** *** 172,182 **** #ifdef SYSLOG syslog(LOG_ERR, "openartbyid: dbm_open %s: %m", historyfile); ! #endif SYSLOG return (NULL); } } ! #endif DBM key.dptr = msg_id; key.dsize = strlen(msg_id) + 1; --- 172,182 ---- #ifdef SYSLOG syslog(LOG_ERR, "openartbyid: dbm_open %s: %m", historyfile); ! #endif /* SYSLOG */ return (NULL); } } ! #endif /* DBM */ key.dptr = msg_id; key.dsize = strlen(msg_id) + 1; *************** *** 185,191 **** content = fetch(key); #else /* ndbm */ content = dbm_fetch(db, key); ! #endif DBM if (content.dptr == NULL) return (NULL); --- 185,191 ---- content = fetch(key); #else /* ndbm */ content = dbm_fetch(db, key); ! #endif /* DBM */ if (content.dptr == NULL) return (NULL); *************** *** 202,208 **** #ifdef SYSLOG syslog(LOG_ERR, "message: fopen %s: %m", historyfile); ! #endif SYSLOG return (NULL); } } else { --- 202,208 ---- #ifdef SYSLOG syslog(LOG_ERR, "message: fopen %s: %m", historyfile); ! #endif /* SYSLOG */ return (NULL); } } else { *************** *** 215,226 **** #ifdef SYSLOG syslog(LOG_ERR, "message: %s: fseek to %ld on %d: %m", historyfile, ltmp, hfp); ! #endif SYSLOG return (NULL); } (void) fgets(line, sizeof(line), hfp); ! #endif USGHIST if ((cp = index(line, '\n')) != NULL) *cp = '\0'; --- 215,226 ---- #ifdef SYSLOG syslog(LOG_ERR, "message: %s: fseek to %ld on %d: %m", historyfile, ltmp, hfp); ! #endif /* SYSLOG */ return (NULL); } (void) fgets(line, sizeof(line), hfp); ! #endif /* USGHIST */ if ((cp = index(line, '\n')) != NULL) *cp = '\0'; *************** *** 232,238 **** syslog(LOG_ERR, "message: malformed line in history file at %ld bytes, id %s", ltmp, msg_id); ! #endif SYSLOG if (cp == NULL) return(NULL); /* this article has expired */ tmp = cp+1; --- 232,238 ---- syslog(LOG_ERR, "message: malformed line in history file at %ld bytes, id %s", ltmp, msg_id); ! #endif /* SYSLOG */ if (cp == NULL) return(NULL); /* this article has expired */ tmp = cp+1; *************** *** 613,619 **** chr = '0'; return chr; } ! #endif USGHIST #ifdef USG #ifndef GAZETTE bcopy(s, d, l) --- 613,619 ---- chr = '0'; return chr; } ! #endif /* USGHIST */ #ifdef USG #ifndef GAZETTE bcopy(s, d, l) *************** *** 775,781 **** #define blkavail(fs) ((fs).f_tfree) /* USG doesn't reserve blocks for root */ #define filfree(fs) ((fs).f_tinode) ! #endif USG #ifdef CMU_MACH /* This code supplied by Tom Lane */ --- 775,781 ---- #define blkavail(fs) ((fs).f_tfree) /* USG doesn't reserve blocks for root */ #define filfree(fs) ((fs).f_tinode) ! #endif /* USG */ #ifdef CMU_MACH /* This code supplied by Tom Lane */ *************** *** 799,805 **** #define bombed(call) ((call) < 0) #define blkfree(fs) ((fs).fsp_free-((fs).fsp_size*(fs).fsp_minfree+99)/100) #define blkavail(fs) (-1) ! #endif MACH dfree(spool,free_space) char *spool; --- 799,805 ---- #define bombed(call) ((call) < 0) #define blkfree(fs) ((fs).fsp_free-((fs).fsp_size*(fs).fsp_minfree+99)/100) #define blkavail(fs) (-1) ! #endif /* MACH */ dfree(spool,free_space) char *spool; *************** *** 825,831 **** return( DFREE_OK ); } ! #else READ_SUPER /* * This code is used if you've got to directly read the superblock * to determine how much space you've got left. It's copied from --- 825,831 ---- return( DFREE_OK ); } ! #else /* READ_SUPER */ /* * This code is used if you've got to directly read the superblock * to determine how much space you've got left. It's copied from *************** *** 919,925 **** return( DFREE_OK ); } ! #endif READ_SUPER #ifdef LOAD /* --- 919,925 ---- return( DFREE_OK ); } ! #endif /* READ_SUPER */ #ifdef LOAD /* *************** *** 987,990 **** # endif } #endif ! #endif LOAD --- 987,990 ---- # endif } #endif ! #endif /* LOAD */ *** server/netaux.c Wed Sep 30 14:34:48 1992 --- server/netaux.c Tue Sep 15 20:09:59 1992 *************** *** 12,25 **** #include #ifndef EXCELAN #include ! #endif not EXCELAN #include #include #ifdef USG #include ! #else not USG #include ! #endif USG #ifdef ALONE --- 12,25 ---- #include #ifndef EXCELAN #include ! #endif /* not EXCELAN */ #include #include #ifdef USG #include ! #else /* not USG */ #include ! #endif /* USG */ #ifdef ALONE *************** *** 211,217 **** if (setitimer(ITIMER_REAL, &new, &old) < 0) { #ifdef SYSLOG syslog(LOG_ERR, "set_timer: setitimer: %m\n"); ! #endif SYSLOG exit(1); } #endif /* not USG */ --- 211,217 ---- if (setitimer(ITIMER_REAL, &new, &old) < 0) { #ifdef SYSLOG syslog(LOG_ERR, "set_timer: setitimer: %m\n"); ! #endif /* SYSLOG */ exit(1); } #endif /* not USG */ *** server/newnews.c Wed Sep 30 14:34:52 1992 --- server/newnews.c Tue Sep 15 20:10:02 1992 *************** *** 39,45 **** FILE *tmplst; int i; char *tmpfile; ! #endif USGHIST if (argc < 4) { printf("%d Usage: NEWNEWS newsgroups yymmdd hhmmss [\"GMT\"] [].\r\n", --- 39,45 ---- FILE *tmplst; int i; char *tmpfile; ! #endif /* USGHIST */ if (argc < 4) { printf("%d Usage: NEWNEWS newsgroups yymmdd hhmmss [\"GMT\"] [].\r\n", *************** *** 131,137 **** for (i = 0; i < 9; i++) { sprintf(historyfile, "%s.d/%d", HISTORY_FILE, i); ! #endif USGHIST fp = fopen(historyfile, "r"); if (fp == NULL) { --- 131,137 ---- for (i = 0; i < 9; i++) { sprintf(historyfile, "%s.d/%d", HISTORY_FILE, i); ! #endif /* USGHIST */ fp = fopen(historyfile, "r"); if (fp == NULL) { *************** *** 142,167 **** printf("%d Cannot open history file.\r\n", ERR_FAULT); (void) fflush(stdout); return; ! #else USGHIST continue; ! #endif USGHIST } #ifndef USGHIST printf("%d New news by message id follows\r\n", OK_NEWNEWS); ! #endif not USGHIST if (seekuntil(fp, key, line, sizeof (line)) < 0) { #ifndef USGHIST printf(".\r\n"); (void) fflush(stdout); ! #endif not USGHIST (void) fclose(fp); #ifndef USGHIST return; ! #else USGHIST continue; ! #endif USGHIST } /* --- 142,167 ---- printf("%d Cannot open history file.\r\n", ERR_FAULT); (void) fflush(stdout); return; ! #else /* USGHIST */ continue; ! #endif /* USGHIST */ } #ifndef USGHIST printf("%d New news by message id follows\r\n", OK_NEWNEWS); ! #endif /* not USGHIST */ if (seekuntil(fp, key, line, sizeof (line)) < 0) { #ifndef USGHIST printf(".\r\n"); (void) fflush(stdout); ! #endif /* not USGHIST */ (void) fclose(fp); #ifndef USGHIST return; ! #else /* USGHIST */ continue; ! #endif /* USGHIST */ } /* *************** *** 208,216 **** #ifdef USGHIST fputs(line, tmplst); fputc('\n', tmplst); ! #else not USGHIST putline(line); ! #endif not USGHIST #ifdef LOG nn_told++; #endif --- 208,216 ---- #ifdef USGHIST fputs(line, tmplst); fputc('\n', tmplst); ! #else /* not USGHIST */ putline(line); ! #endif /* not USGHIST */ #ifdef LOG nn_told++; #endif *************** *** 235,241 **** (void) fflush(stdout); (void) fclose(tmplst); (void) unlink(tmpfile); ! #endif USGHIST } --- 235,241 ---- (void) fflush(stdout); (void) fclose(tmplst); (void) unlink(tmpfile); ! #endif /* USGHIST */ } *** server/scandir.c Wed Sep 30 14:34:49 1992 --- server/scandir.c Tue Sep 15 20:10:01 1992 *************** *** 4,9 **** --- 4,13 ---- #include "common.h" + #ifdef ISC + # include + #endif + /* * scan_dir -- scan the current directory for news articles, * loading the article numbers into art_array. Return *************** *** 26,32 **** --- 30,40 ---- scan_dir(low_msg, high_msg) int low_msg, high_msg; { + #ifdef ISC + register struct dirent *dirent; + #else register struct direct *dirent; + #endif register DIR *dirp; int artnum; *************** *** 34,43 **** dirp = opendir("."); ! if (dirp == NULL) return (0); while ((dirent = readdir(dirp)) != NULL) { artnum = atoi(dirent->d_name); #ifdef DYNAMIC_ART_ARRAY if (artnum == 0 || artnum < low_msg || artnum > high_msg) --- 42,73 ---- dirp = opendir("."); ! if (dirp == NULL) { ! #ifdef LOG ! syslog(LOG_ERR, "scan_dir(): opendir() failed. Returning num_arts=0"); ! #endif return (0); + } while ((dirent = readdir(dirp)) != NULL) { + + #ifdef LOG + /* + { + char pwd[256]; + + getcwd (pwd, 255); + #ifdef ISC + syslog(LOG_INFO, "%s: d->d_name=%s d->d_ino=%d d->d_reclen=%d", + pwd, dirent->d_name, dirent->d_ino, dirent->d_reclen); + #else + syslog(LOG_INFO, "%s: d->d_name=%s d->d_ino=%d", + pwd, dirent->d_name, dirent->d_ino); + #endif + } + */ + #endif + artnum = atoi(dirent->d_name); #ifdef DYNAMIC_ART_ARRAY if (artnum == 0 || artnum < low_msg || artnum > high_msg) *************** *** 70,75 **** --- 100,110 ---- } art_array[num_arts] = artnum; ++num_arts; + + #ifdef LOG + syslog(LOG_INFO, "scan_dir(): artnum=%d num_arts=%d", artnum, num_arts); + #endif + #else if (artnum != 0 && artnum >= low_msg && artnum <= high_msg) art_array[num_arts++] = artnum; *** server/serve.c Wed Sep 30 14:34:43 1992 --- server/serve.c Wed Nov 18 19:07:06 1992 *************** *** 17,23 **** #ifdef LOG # ifndef USG # include ! # endif not USG #endif #ifdef TIMERS --- 17,23 ---- #ifdef LOG # ifndef USG # include ! # endif /* not USG */ #endif #ifdef TIMERS *************** *** 27,38 **** extern int ahbs(), group(), help(), ihave(); extern int list(), newgroups(), newnews(), nextlast(), post(); extern int slave(), stat(), xhdr(); extern int errno; #ifdef AUTH extern int doauth(); ! #endif AUTH static struct cmdent { char *cmd_name; --- 27,50 ---- extern int ahbs(), group(), help(), ihave(); extern int list(), newgroups(), newnews(), nextlast(), post(); extern int slave(), stat(), xhdr(); + #ifdef XINDEX + extern int xindex(); + #endif + #ifdef XMOTD + extern int xmotd(); + #endif + #ifdef XOVERVIEW + extern int xoverview(); + #endif + #ifdef XUSER + extern int xuser(); + #endif extern int errno; #ifdef AUTH extern int doauth(); ! #endif /* AUTH */ static struct cmdent { char *cmd_name; *************** *** 43,49 **** "authcap", 0, doauth, "authinfo", 0, doauth, "authsys", 0, doauth, ! #endif AUTH "article", 0, ahbs, "body", 0, ahbs, "group", 0, group, --- 55,61 ---- "authcap", 0, doauth, "authinfo", 0, doauth, "authsys", 0, doauth, ! #endif /* AUTH */ "article", 0, ahbs, "body", 0, ahbs, "group", 0, group, *************** *** 60,66 **** "stat", 0, ahbs, #ifdef XHDR "xhdr", 0, xhdr, ! #endif XHDR }; #define NUMCMDS (sizeof(cmdtbl) / sizeof(struct cmdent)) --- 72,90 ---- "stat", 0, ahbs, #ifdef XHDR "xhdr", 0, xhdr, ! #endif /* XHDR */ ! #ifdef XINDEX ! "xindex", 0, xindex, ! #endif /* XINDEX */ ! #ifdef XMOTD ! "xmotd", 0, xmotd, ! #endif /* XMOTD */ ! #ifdef XOVERVIEW ! "xoverview", 0, xoverview, ! #endif /* XOVERVIEW */ ! #ifdef XUSER ! "xuser", 0, xuser, ! #endif /* XUSER */ }; #define NUMCMDS (sizeof(cmdtbl) / sizeof(struct cmdent)) *************** *** 98,104 **** #ifdef AUTH extern int Needauth; extern char User[]; ! #endif AUTH /* * serve -- given a connection on stdin/stdout, serve --- 122,128 ---- #ifdef AUTH extern int Needauth; extern char User[]; ! #endif /* AUTH */ /* * serve -- given a connection on stdin/stdout, serve *************** *** 152,158 **** #ifdef ALONE #ifndef USG (void) signal(SIGCHLD, SIG_IGN); ! #endif not USG #endif /* Ignore SIGPIPE, since we'll see closed connections with read */ --- 176,182 ---- #ifdef ALONE #ifndef USG (void) signal(SIGCHLD, SIG_IGN); ! #endif /* not USG */ #endif /* Ignore SIGPIPE, since we'll see closed connections with read */ *************** *** 163,169 **** #ifdef AUTH Needauth = 1; strcpy(User,""); ! #endif AUTH host_access(&canread, &canpost, &canxfer, gdbuf); if (gethostname(host, sizeof(host)) < 0) --- 187,193 ---- #ifdef AUTH Needauth = 1; strcpy(User,""); ! #endif /* AUTH */ host_access(&canread, &canpost, &canxfer, gdbuf); if (gethostname(host, sizeof(host)) < 0) *************** *** 302,308 **** (void) fflush(stdout); continue; } ! #endif AUTH (*cmdtbl[i].cmd_fctn)(argnum, argp); } else { #ifdef SYSLOG --- 326,332 ---- (void) fflush(stdout); continue; } ! #endif /* AUTH */ (*cmdtbl[i].cmd_fctn)(argnum, argp); } else { #ifdef SYSLOG *************** *** 490,493 **** user, sys, Tfinish - Tstart); syslog(LOG_INFO, "%s times %s", hostname, buf); } ! #endif LOG --- 514,517 ---- user, sys, Tfinish - Tstart); syslog(LOG_INFO, "%s times %s", hostname, buf); } ! #endif /* LOG */ *** server/spawn.c Wed Sep 30 14:34:46 1992 --- server/spawn.c Tue Sep 15 20:09:59 1992 *************** *** 65,73 **** #endif #ifdef USG int status; ! #else not USG union wait status; ! #endif not USG register FILE *fp; #ifdef CNEWS --- 65,73 ---- #endif #ifdef USG int status; ! #else /* not USG */ union wait status; ! #endif /* not USG */ register FILE *fp; #ifdef CNEWS *************** *** 91,97 **** */ if (cont_code == CONT_POST) fprintf(fp, "Nntp-Posting-Host: %s\n", hostname); ! #endif AUTH printf("%d Ok\r\n", cont_code); (void) fflush(stdout); --- 91,97 ---- */ if (cont_code == CONT_POST) fprintf(fp, "Nntp-Posting-Host: %s\n", hostname); ! #endif /* AUTH */ printf("%d Ok\r\n", cont_code); (void) fflush(stdout); *************** *** 332,338 **** #ifdef LOG syslog(LOG_ERR, "%s transfer_timeout", hostname); ! #endif LOG (void) unlink(tempfile); --- 332,338 ---- #ifdef LOG syslog(LOG_ERR, "%s transfer_timeout", hostname); ! #endif /* LOG */ (void) unlink(tempfile); *************** *** 339,343 **** exit(1); } ! #endif XFER_TIMEOUT --- 339,343 ---- exit(1); } ! #endif /* XFER_TIMEOUT */ *** server/time.c Wed Sep 30 14:34:50 1992 --- server/time.c Tue Sep 15 20:10:01 1992 *************** *** 10,18 **** #include "common.h" #ifdef USG #include ! #else not USG #include ! #endif not USG /* * dtol -- convert date to long integer. This is not implicitly --- 10,18 ---- #include "common.h" #ifdef USG #include ! #else /* not USG */ #include ! #endif /* not USG */ /* * dtol -- convert date to long integer. This is not implicitly *** server/timer.c Wed Sep 30 14:34:49 1992 --- server/timer.c Sat Oct 17 20:01:25 1992 *************** *** 6,17 **** #ifdef TIMERS #ifndef lint static char rcsid[] = ! "@(#) $Header: timer.c,v 1.3 91/03/19 03:02:41 sob Exp $ (NNTP with TIMERS)"; #endif #else #ifndef lint static char rcsid[] = ! "@(#) $Header: timer.c,v 1.3 91/03/19 03:02:41 sob Exp $ (NNTP without TIMERS)"; #endif #endif --- 6,17 ---- #ifdef TIMERS #ifndef lint static char rcsid[] = ! "@(#) $Header: timer.c,v 1.2 90/12/27 22:16:27 sob Exp $ (NNTP with TIMERS)"; #endif #else #ifndef lint static char rcsid[] = ! "@(#) $Header: timer.c,v 1.2 90/12/27 22:16:27 sob Exp $ (NNTP without TIMERS)"; #endif #endif *************** *** 18,29 **** #ifdef TIMERS #include #include "timer.h" ! #ifdef USG ! #ifdef LAI_TCP #include - #define BSDSELECT #endif ! #else #ifndef FD_SETSIZE /* Forward compatability */ #define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) --- 18,27 ---- #ifdef TIMERS #include #include "timer.h" ! #ifdef ISC #include #endif ! #ifndef USG #ifndef FD_SETSIZE /* Forward compatability */ #define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) *************** *** 30,36 **** #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) #define FD_ZERO(p) ((p)->fds_bits[0] = 0) - #define BSDSELECT #endif #endif /* non-portable */ --- 28,33 ---- *************** *** 75,81 **** register int i, n; register struct timer *tp; register long secs; ! #ifndef BSDSELECT long timeout; long readfds; #else --- 72,78 ---- register int i, n; register struct timer *tp; register long secs; ! #if defined(USG) && !defined(ISC) long timeout; long readfds; #else *************** *** 89,95 **** return(1); /* Length of next timeout is minimum of all "timers" */ ! #ifndef BSDSELECT timeout = -1; for (i = ntimer, tp = timers; i > 0; --i, ++tp) if (tp->left >= 0 && --- 86,92 ---- return(1); /* Length of next timeout is minimum of all "timers" */ ! #if defined(USG) && !defined(ISC) timeout = -1; for (i = ntimer, tp = timers; i > 0; --i, ++tp) if (tp->left >= 0 && *************** *** 120,128 **** /* Do select */ FD_ZERO(&readfds); FD_SET(fileno(stdin), &readfds); ! #endif /* BSDSELECT */ errno = 0; ! #if defined(EXCELAN) || defined(ULTRIX) n = select(fileno(stdin) + 1, &readfds, (long*)0, timeout); #else n = select(fileno(stdin) + 1, --- 117,125 ---- /* Do select */ FD_ZERO(&readfds); FD_SET(fileno(stdin), &readfds); ! #endif /* !USG */ errno = 0; ! #ifdef EXCELAN n = select(fileno(stdin) + 1, &readfds, (long*)0, timeout); #else n = select(fileno(stdin) + 1, *** server/xhdr.c Wed Sep 30 14:34:50 1992 --- server/xhdr.c Tue Sep 15 20:10:02 1992 *************** *** 158,164 **** } } ! #else not XHDR /* Kludge to get around Greenhills C compiler */ --- 158,164 ---- } } ! #else /* not XHDR */ /* Kludge to get around Greenhills C compiler */ *************** *** 166,169 **** { } ! #endif not XHDR --- 166,169 ---- { } ! #endif /* not XHDR */ *[SRC.TIN-1_22]SIGFILE.C;2+,. // 4 <-d0@123KPWO 56~I7~I89]VG/HJ/* * Project : tin - a Usenet reader * Module : sigfile.c * Author : M.Gleason & I.Lea * Created : 17-10-92 * Updated : 11-07-93 * Notes : Generate random signature for posting/mailing etc. * Copyright : (c) Copyright 1989-93 by Mike Gleason & Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #ifndef S_ISDIR # define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR) #endif #define MAXLOOPS 1000 #ifndef M_AMIGA # define CURRENTDIR "." #else # define CURRENTDIR "" #endif static char sigfile[PATH_LEN]; void add_signature (fp, flag) FILE *fp; int flag; { char path[PATH_LEN]; char cwd[PATH_LEN]; FILE *fixfp; FILE *sigfp; int i; #ifdef NNTP_INEWS if (read_news_via_nntp && use_builtin_inews) { flag = TRUE; } #endif i = my_group[cur_groupnum]; if (! strfpath (active[i].attribute.sigfile, path, sizeof (path), homedir, (char *) 0, (char *) 0, active[i].name)) { if (! strfpath (default_sigfile, path, sizeof (path), homedir, (char *) 0, (char *) 0, active[i].name)) { joinpath (path, homedir, ".Sig"); } } /* * Check to see if sigfile is a directory & if it is generate a * random signature from sigs in sigdir. If the file ~/.sigfixed * exists (fixed part of random sig) then read it in first and * append the random sig part onto the end. */ if ((sigfp = open_random_sig (path)) != (FILE *) 0) { if (debug == 2) { error_message ("USING random sig=[%s]", sigfile); } get_cwd (cwd); fprintf (fp, "\n--\n"); joinpath (path, homedir, ".sigfixed"); if ((fixfp = fopen (path, "r")) != (FILE *) 0) { copy_fp (fixfp, fp, ""); fclose (fixfp); } copy_fp (sigfp, fp, ""); fclose (sigfp); my_chdir (cwd); return; } /* * Use ~/.signature or ~/.Sig or custom .Sig files */ if ((sigfp = fopen (default_signature, "r")) != (FILE *) 0) { if (flag) { fprintf (fp, "\n--\n"); copy_fp (sigfp, fp, ""); } fclose (sigfp); return; } if ((sigfp = fopen (path, "r")) != (FILE *) 0) { fprintf (fp, "\n--\n"); copy_fp (sigfp, fp, ""); fclose (sigfp); } } FILE * open_random_sig (sigdir) char *sigdir; { long epoch; struct stat st; if (stat (sigdir, &st) != -1) { if (S_ISDIR(st.st_mode)) { time (&epoch); srand ((unsigned int) epoch); my_chdir (sigdir); if (thrashdir (sigdir) || ! sigfile[0]) { if (debug == 2) { error_message ("NO sigfile=[%s]", sigfile); } return (FILE *) 0; } else { if (debug == 2) { error_message ("sigfile=[%s]", sigfile); } return fopen (sigfile, "r"); } } } return (FILE *) 0; } int thrashdir (sigdir) char *sigdir; { char *cwd; int safeguard, recurse; register DIR *dirp; register DIR_BUF *dp; register int c, numentries, pick; struct stat st; sigfile[0] = '\0'; if ((dirp = opendir (CURRENTDIR)) == NULL) { return (1); } numentries = 0; while ((dp = readdir (dirp)) != NULL) { numentries++; } /* * consider "." and ".." non-entries */ cwd = (char *) my_malloc (PATH_LEN + 1); #ifndef M_AMIGA if (numentries < 3 || cwd == (char *) 0) { #else if (numentries == 0 || cwd == (char *) 0) { #endif closedir (dirp); return (-1); } get_cwd (cwd); recurse = strcmp (cwd, sigdir); /* If we are using the root sig directory, we don't want * to recurse, or else we might use a custom sig intended * for a specific newsgroup (and not this one). */ for (safeguard=0, dp=NULL; safeguard= 0) { if ((dp = readdir (dirp)) == NULL) { break; } } if (dp != NULL) { /* if we could open the dir entry */ if (! strcmp (dp->d_name, CURRENTDIR) || ! strcmp (dp->d_name, "..")) { dp = NULL; } else { /* if we have a non-dot entry */ if (stat (dp->d_name, &st) == -1) { gak: closedir (dirp); return (1); } if (S_ISDIR(st.st_mode)) { if (recurse) { /* * do subdirectories */ if (my_chdir (dp->d_name) < 0) { goto gak; } if ((c = thrashdir (sigdir)) == 1) { goto gak; } else if (c == -1) { /* * the one we picked was an * empty dir so try again. */ dp = NULL; my_chdir (cwd); } } else { dp = NULL; } } else { /* end dir; we have a file */ get_cwd (sigfile); strcat (sigfile, "/"); strcat (sigfile, dp->d_name); if (debug == 2) { error_message ("Found a file=[%s]", sigfile); } } } } } free (cwd); if (debug == 2) { error_message ("return 0: sigfile=[%s]", sigfile); } closedir (dirp); return (0); } *[SRC.TIN-1_22]SIGNAL.C;2+,.// 4-d0@123KPWO56`myD`7E`89]VG/HJ/* * Project : tin - a Usenet reader * Module : signal.c * Author : I.Lea * Created : 01-04-91 * Updated : 05-09-93 * Notes : signal handlers for different modes and window resizing * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" extern char *glob_art_group; extern char *glob_group; extern char *glob_page_group; extern char index_file[PATH_LEN]; extern int glob_respnum; static int time_remaining; #ifdef SIGTSTP int do_sigtstp = 0; #endif #ifdef HAVE_POSIX_JC /* * for POSIX systems we know SIGTYPE is void */ void (*sigdisp(sig, func))() int sig; void (*func)(); { struct sigaction sa, osa; sa.sa_handler = func; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; #ifdef SA_RESTART sa.sa_flags |= SA_RESTART; #endif if (sigaction (sig, &sa, &osa) < 0) { #ifdef DONT_PROTOTYPE_PTR_TO_FUNC return ((void (*) ()) (-1)); #else return ((void (*) (int)) (-1)); #endif } return (osa.sa_handler); } #else t_sigtype (*sigdisp(sig, func))() int sig; t_sigtype (_CDECL *func)(SIGTYPE); { return (signal (sig, func)); } #endif void set_signal_handlers () { #ifdef SIGINT signal (SIGINT, signal_handler); /* ctrl-C */ #endif #ifdef SIGQUIT signal (SIGQUIT, signal_handler); /* ctrl-\ */ #endif #ifdef SIGHUP signal (SIGHUP, signal_handler); /* hangup */ #endif #ifdef SIGILL signal (SIGILL, signal_handler); /* illegal instruction */ #endif #ifdef SIGFPE signal (SIGFPE, signal_handler); /* floating point exception */ #endif #if 0 #ifdef SIGBUS signal (SIGBUS, signal_handler); /* bus error */ #endif #endif #ifdef SIGSEGV signal (SIGSEGV, signal_handler); /* segmentation violation */ #endif #ifdef SIGPIPE signal (SIGPIPE, SIG_IGN); #endif #ifdef SIGCHLD signal (SIGCHLD, signal_handler); /* death of a child process */ #endif #ifdef SIGPWR signal (SIGPWR, signal_handler); /* powerfail */ #endif #ifdef SIGWINCH if (debug == 2) { wait_message ("SIGWINCH setting signal..."); sleep (2); } signal (SIGWINCH, main_resize); #endif #if defined(SIGTSTP) && ! defined(MINIX) { t_sigtype (*ptr)(); ptr = signal (SIGTSTP, SIG_DFL); signal (SIGTSTP, ptr); if (ptr != SIG_IGN) { /* * SIGTSTP is ignored when starting from shells * without job-control */ do_sigtstp = 1; signal (SIGTSTP, main_suspend); } } #endif } void set_alarm_signal () { #ifndef DONT_REREAD_ACTIVE_FILE /* * Only reread active file if news is not static (ie. CD-ROM) */ (void) alarm (0); if (strcmp (spooldir_alias, "news") == 0) { #ifndef M_OS2 signal (SIGALRM, signal_handler); #endif alarm (reread_active_file_secs); } reread_active_file = FALSE; #endif } void set_alarm_clock_on () { #ifndef DONT_REREAD_ACTIVE_FILE alarm (time_remaining); #endif } void set_alarm_clock_off () { #ifndef DONT_REREAD_ACTIVE_FILE time_remaining = alarm (0); #endif } void _CDECL signal_handler (sig) int sig; { char *sigtext; #ifdef SIGCHLD int wait_status = 1; #endif switch (sig) { #ifdef SIGINT case SIGINT: sigtext = "SIGINT "; # if !defined(M_AMIGA) && !defined(__SASC) if (! update) { signal (SIGINT, signal_handler); return; } # endif break; #endif #ifdef SIGQUIT case SIGQUIT: sigtext = "SIGQUIT "; break; #endif #ifdef SIGHUP case SIGHUP: sigtext = "SIGHUP "; break; #endif #ifdef SIGCHLD case SIGCHLD: wait (&wait_status); signal (SIGCHLD, signal_handler); /* death of a child */ # ifdef WEXITSTATUS system_status = WEXITSTATUS(wait_status); # endif return; #endif #ifdef SIGPWR case SIGPWR: sigtext = "SIGPWR "; break; #endif #ifdef SIGFPE case SIGFPE: sigtext = "SIGFPE "; break; #endif #ifdef SIGBUS case SIGBUS: sigtext = "SIGBUS "; break; #endif #ifdef SIGSEGV case SIGSEGV: sigtext = "SIGSEGV "; break; #endif #if defined(SIGALRM) && !defined(DONT_REREAD_ACTIVE_FILE) case SIGALRM: set_alarm_signal (); reread_active_file = TRUE; return; #endif default: sigtext = ""; break; } Raw (FALSE); EndWin (); fprintf (stderr, "\n%s: signal handler caught signal %s(%d).\n", progname, sigtext, sig); #if defined(SIGBUS) || defined(SIGSEGV) if ( # ifdef SIGBUS sig == SIGBUS # endif # if defined(SIGBUS) && defined(SIGSEGV) || # endif # ifdef SIGSEGV sig == SIGSEGV # endif ) { fprintf (stderr, "%s: send a bug report to %s%s\n", progname, BUG_REPORT_ADDRESS, add_addr); } #endif fflush (stderr); cleanup_tmp_files (); exit (1); } int set_win_size (num_lines, num_cols) int *num_lines; int *num_cols; { int old_lines; int old_cols; #ifdef TIOCGSIZE struct ttysize win; #else # ifdef TIOCGWINSZ struct winsize win; # endif #endif old_lines = *num_lines; old_cols = *num_cols; init_screen_array (FALSE); /* deallocate screen array */ #ifdef TIOCGSIZE if (ioctl (0, TIOCGSIZE, &win) == 0) { if (win.ts_lines != 0) { *num_lines = win.ts_lines - 1; } if (win.ts_cols != 0) { *num_cols = win.ts_cols; } } #else # ifdef TIOCGWINSZ if (ioctl (0, TIOCGWINSZ, &win) == 0) { if (win.ws_row != 0) { *num_lines = win.ws_row - 1; } if (win.ws_col != 0) { *num_cols = win.ws_col; } } # endif #endif init_screen_array (TRUE); /* allocate screen array for resize */ set_subj_from_size (*num_cols); RIGHT_POS = *num_cols - 20; MORE_POS = *num_cols - 15; if (beginner_level) { NOTESLINES = *num_lines - INDEX_TOP - MINI_HELP_LINES; } else { NOTESLINES = *num_lines - INDEX_TOP - 1; } if (NOTESLINES <= 0) { NOTESLINES = 1; } if (*num_lines != old_lines || *num_cols != old_cols) { return TRUE; } else { return FALSE; } } void set_signals_art () { #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, art_suspend); } #endif #ifdef SIGWINCH signal (SIGWINCH, art_resize); #endif } void set_signals_group () { #ifdef SIGTSTP iq“~ TIN-1_22.BCKd[SRC.TIN-1_22]SIGNAL.C;2 f (do_sigtstp) { sigdisp (SIGTSTP, group_suspend); } #endif #ifdef SIGWINCH signal (SIGWINCH, group_resize); #endif } void set_signals_help () { #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, help_suspend); } #endif #ifdef SIGWINCH signal (SIGWINCH, help_resize); #endif } void set_signals_page () { #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, page_suspend); } #endif #ifdef SIGWINCH signal (SIGWINCH, page_resize); #endif } void set_signals_select () { #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, select_suspend); } #endif #ifdef SIGWINCH signal (SIGWINCH, select_resize); #endif } void set_signals_spooldir () { #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, spooldir_suspend); } #endif #ifdef SIGWINCH signal (SIGWINCH, spooldir_resize); #endif } void set_signals_thread () { #ifdef SIGTSTP if (do_sigtstp) { sigdisp (SIGTSTP, thread_suspend); } #endif #ifdef SIGWINCH signal (SIGWINCH, thread_resize); #endif } #ifdef SIGTSTP /* ARGSUSED0 */ void art_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, art_suspend); if (! update) { Raw (TRUE); art_resize (0); } set_keypad_on (); } /* ARGSUSED0 */ void main_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, main_suspend); if (! update) { Raw (TRUE); main_resize (0); } set_keypad_on (); } /* ARGSUSED0 */ void select_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, select_suspend); if (! update) { Raw (TRUE); select_resize (0); } set_keypad_on (); } /* ARGSUSED0 */ void spooldir_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, spooldir_suspend); if (! update) { Raw (TRUE); spooldir_resize (0); } set_keypad_on (); } /* ARGSUSED0 */ void group_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, group_suspend); if (! update) { Raw (TRUE); group_resize (0); } set_keypad_on (); } /* ARGSUSED0 */ void help_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, help_suspend); if (! update) { Raw (TRUE); help_resize (0); } set_keypad_on (); } /* ARGSUSED0 */ void page_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, page_suspend); if (! update) { Raw (TRUE); page_resize (0); } set_keypad_on (); } /* ARGSUSED0 */ void thread_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, thread_suspend); if (! update) { Raw (TRUE); thread_resize (0); } set_keypad_on (); } /* ARGSUSED0 */ void rcfile_suspend (sig) int sig; { set_keypad_off (); Raw (FALSE); wait_message (txt_suspended_message); kill (0, SIGSTOP); sigdisp (SIGTSTP, rcfile_suspend); Raw (TRUE); set_keypad_on (); show_rcfile_menu (); } #endif /* SIGTSTP */ /* ARGSUSED0 */ void art_resize (sig) int sig; { char buf[LEN]; #ifdef SIGWINCH (void) set_win_size (&cLINES, &cCOLS); signal (SIGWINCH, art_resize); #endif mail_setup (); ClearScreen (); sprintf (buf, txt_group, glob_art_group); wait_message (buf); } /* ARGSUSED0 */ void main_resize (sig) int sig; { #ifdef SIGWINCH (void) set_win_size (&cLINES, &cCOLS); signal (SIGWINCH, main_resize); #endif mail_setup (); } /* ARGSUSED0 */ void select_resize (sig) int sig; { int resized = TRUE; #ifdef SIGWINCH resized = set_win_size (&cLINES, &cCOLS); signal (SIGWINCH, select_resize); #endif mail_setup (); if (resized || sig == 0) { #ifndef USE_CLEARSCREEN ClearScreen (); #endif group_selection_page (); } } /* ARGSUSED0 */ void spooldir_resize (sig) int sig; { int resized = TRUE; #ifdef SIGWINCH resized = set_win_size (&cLINES, &cCOLS); signal (SIGWINCH, spooldir_resize); #endif mail_setup (); if (resized || sig == 0) { #ifndef USE_CLEARSCREEN ClearScreen (); #endif show_spooldir_page (); } } /* ARGSUSED0 */ void group_resize (sig) int sig; { int resized = TRUE; #ifdef SIGWINCH resized = set_win_size (&cLINES, &cCOLS); signal (SIGWINCH, group_resize); #endif mail_setup (); if (resized || sig == 0) { #ifndef USE_CLEARSCREEN ClearScreen (); #endif show_group_page (); } } /* ARGSUSED0 */ void help_resize (sig) int sig; { int resized = TRUE; #ifdef SIGWINCH resized = set_win_size (&cLINES, &cCOLS); signal (SIGWINCH, help_resize); #endif if (resized || sig == 0) { display_info_page (); } } /* ARGSUSED0 */ void page_resize (sig) int sig; { int resized = TRUE; #ifdef SIGWINCH resized = set_win_size (&cLINES, &cCOLS); signal (SIGWINCH, page_resize); #endif mail_setup (); if (resized || sig == 0) { #ifndef USE_CLEARSCREEN ClearScreen (); #endif redraw_page (glob_respnum, glob_page_group); } } /* ARGSUSED0 */ void thread_resize (sig) int sig; { int resized = TRUE; #ifdef SIGWINCH resized = set_win_size (&cLINES, &cCOLS); signal (SIGWINCH, thread_resize); #endif mail_setup (); if (resized || sig == 0) { #ifndef USE_CLEARSCREEN ClearScreen (); #endif show_thread_page (); } } *[SRC.TIN-1_22]SIO.DIR;1+,t.// 4-d0123 KPWO56#Æ7k89IʒG/HJI $COPYRIGHT.  $M$AKEFILE. $README. $S$PRINT.3EVENTS.HIMPL.H MAKEFILE.   V  z:   MAKESIO.COM5  SIO.3SIO.CSIO.DVISIO.H& SIOCONF.H  TSIOSUP.C !ZSPRINT.C SUITE.DIRTAGS.!*[SRC.TIN-1_22.SIO]$COPYRIGHT.;1+,.// 44-t0@123KPWO56^Æ7_{89]VG/HJThis software is (c) Copyright 1992, 1993 by Panagiotis Tsirigotis The author (Panagiotis Tsirigotis) grants permission to use, copy, and distribute this software and its documentation for any purpose and without fee, provided that a) the above copyright notice extant in files in this distribution is not removed from files included in any redistribution, and b) this file is also included in any redistribution. Modifications to this software may be distributed, either by distributing the modified software or by distributing patches to the original software, under the following additional terms: 1. The version number will be modified as follows: a. The first 3 components of the version number (i.e. ..) will remain unchanged. b. A new component will be appended to the version number to indicate the modification level. The form of this component is up to the author of the modifications. 2. The author of the modifications will include his/her name by appending it along with the new version number to this file and will be responsible for any wrong behavior of the modified software. The author makes no representations about the suitability of this software for any purpose. It is provided "as is" without any express or implied warranty. *[SRC.TIN-1_22.SIO]$M$AKEFILE.;2+, .// 4-t0@123KPWO56K)ņ7@mņ89]VG/HJ# Available entries: # lib --> creates the library # clean --> removes all .obj and .a files # spotless --> clean + uninstall # tags --> creates a tags file (from the SOURCES and HEADERS) NAME = sio VERSION = 1.6.1 HEADERS = sio.h impl.h events.h sioconf.h SOURCES = sprint.c sio.c siosup.c OBJECTS = sprint.obj sio.obj siosup.obj MANFILES = sio.3 Sprint.3 INCLUDEFILES = sio.h # Available flags: # -DDEBUG : enables assertions in the code. A failed assertion # terminates the program # -DEVENTS : enables code that records events (currently limited # to which functions have been called on a given fd) # and code that accesses the event buffers. # -DLITTLE_ENDIAN : says that the machine is a little endian. This is # needed if you enable EVENTS and your machine is a # little endian (big endian is the default). # # DEFS should be set from the command line; the current group of defs # is for SunOS 4.x # DEFS = HAS_ISATTY DEBUG = /noopt VERSION_DEF = VERSION="""SIO Version $(VERSION)""" CPP_DEFS = /define=($(VERSION_DEF),$(DEFS)) # # The following variables shouldn't need to be changed # LINT_FLAGS = -hbux CPP_FLAGS = $(CPP_DEFS) CC_FLAGS = $(DEBUG) CFLAGS = $(CPP_FLAGS) $(CC_FLAGS) LIBNAME = lib$(NAME).olb lib: $(LIBNAME) $(LIBNAME): $(OBJECTS) library/create $(LIBNAME) sprint.obj,sio.obj,siosup.obj clean: del/log $(OBJECTS) $(LIBNAME) core # # PUT HERE THE RULES TO MAKE THE OBJECT FILES # sprint.obj: sio.h impl.h sioconf.h sio.obj: sio.h impl.h sioconf.h events.h siosup.obj: sio.h impl.h sioconf.h events.h *[SRC.TIN-1_22.SIO]$README.;1+,. // 4 -t0@123KPWO 56@㪆Æ7v{89]VG/HJ====================================================================== NOTE: I use vi with a tabstop value of 3. Using the same tabstop value will make the text/code look properly indented. ====================================================================== 1. What is SIO ? SIO is a library that provides _stream_ I/O which is what most Unix programs do. As such it is a competitor to stdio. 2. Why would you care to use it ? a. SIO is a little faster than stdio b. SIO provides an easier-to-use interface (IMHO) c. SIO is capable of using memory mapping for reading files if the operating system supports it. d. If you have a program that uses read(2)/write(2) it is trivial to convert it to use SIO (just replace read with Sread and write with Swrite) e. You get source 3. Setting up the Stream I/O (SIO) library There are 3 steps to the process: 1) modifying the SIO configuration file (but you can skip this step if you are using one of the operating systems listed below). 2) testing SIO 3) installing the library and manpages 3.1. How to compile the Stream I/O (SIO) library All the system-dependent stuff of SIO is placed in the sioconf.h file. If your system is not listed below, you will need to read that file to see what flags you need to set to properly compile SIO. For the following systems, here is what you need to do: SunOS 4.x: make "DEFS=-DHAS_MMAP -DHAS_ONEXIT -DHAS_MEMOPS -DHAS_ISATTY" SunOS 5.x (aka Solaris 2.y): make "DEFS=-DHAS_MMAP -DHAS_ATEXIT -DHAS_MEMOPS -DHAS_ISATTY" (I don't have access to a system running Solaris 2.y so I have not tested this) Ultrix 4.x: make "DEFS=-DHAS_MEMOPS -DHAS_ATEXIT -DHAS_ISATTY" If your system is one of the above, then you can skip to the next subsection. However, I should mention that the library compiles by default with debugging enabled (i.e. uses the -g flag of cc). You can override this by appending to the invocation of 'make' the argument "DEBUG=-O" If your system is not among the above, then you will need to modify the sioconf.h file. You do this by uncommenting the inclusion of customconf.h. Then, you can override all constants/macros defined in sioconf.h by defining them first in customconf.h. Please read sioconf.h for more information on what constants/macros can be defined. The Makefile has a header that explains what the Makefile can do. The only flag that you may want to define is -DDEBUG which enables assertions in the SIO code (if an assertion fails, the program is terminated with an error message that lists the SIO file and line number where the error occured). 3.2. Testing SIO After you have successfully compiled SIO, you can use the programs in the "suite" directory to test the SIO functions. Testing should be done before installing the library. The script testlib does everything; just type: testlib all The script sprint_test (invoked by testlib) tests Sprint by using a variety of formats and comparing its output with that of an ANSI-compatible printf. At least on Ultrix 4.1 and 4.2 this test fails because printf is not ANSI-compatible. In such a case, you can test the rest of the SIO functions by typing: testlib all Sprint (anything after the 'all' argument is interpreted as a function that should not be tested). The README file in the "suite" directory describes how to do a few more tests that cannot be done automatically. 3.3. Installing the library and manpages The 'make' command will create libsio.a in the current directory. The Makefile includes an "install" target. Doing a 'make install' will cause the following: a) libsio.a will be installed in LIBDIR b) the necessary SIO header files will be installed in INCLUDEDIR c) the SIO man pages will be installed in MANDIR LIBDIR, INCLUDEDIR, and MANDIR are Makefile variables that you can edit in the Makefile or override when you invoke 'make'. Here is a sample command to install SIO: make install LIBDIR=/usr/local/lib INCLUDEDIR=/usr/local/include MANDIR=/usr/local/man/man3 4. Epilogue Feel free to modify SIO to suit your needs. Please let me know of any bugs you find. If you want to distribute your modifications, please read the COPYRIGHT file. It basically says that you are free to redistribute as long as you retain the original copyright notice and you make sure that your modifications are identifiable. In order to achieve this I have reserved the first 3 components of the version number (for example, 1.4.2) and you can identify your mods by appending another component to that version number (for example, 1.4.2.A2). Also, if you distribute a modified version of the library, you take full responsibility for any bugs in the code (not just your code; the whole thing). *[SRC.TIN-1_22.SIO]$S$PRINT.3;1+,. // 4 -t0@123KPWO 56 ԆÆ7 |89]VG/HJ.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id: Sprint.3,v 8.1 1993/03/13 01:15:34 panos Exp $ .TH Sprint 3X "29 May 1992" .SH NAME Sprint -- formatted stream output .SH SYNOPSIS .LP .nf .ft B int Sprint( fd, format [ , ... ] ) int fd ; char *format ; .SH DESCRIPTION \fBSprint()\fR provides formatted output conversion. The formatting is controlled by the \fIformat\fR argument. All characters in \fIformat\fR that do not specify a conversion are printed. A conversion is specified by a '%' followed by a string that ends with a conversion type character. The string may contain flags, a field width, a precision, and a modifier. .LP Possible flags (more that one can be specified and they can be in any order) include: .TP 10 .B \'-' specifies left adjustment of the converted argument. The default is right adjustment. This flag is meaningful only if a field width is specified. .TP .B \'+' specifies that a number will always have a sign as a prefix (this forces a '+' sign to appear if the number is positive). .TP .B \' ' prefixes a \fIspace\fR to the number if the number has not a sign (therefore the '+' flag overrides this flag). .TP .B \'#' The meaning of '#' depends on the conversion type character: for \fBo\fR conversions the first digit will be 0; for \fBx\fR or \fBX\fR conversions \fB0x\fR or \fB0X\fR respectively will be prefixed to the number (if it is not zero); for \fBe\fR, \fBE\fR, \fBf\fR, \fBg\fR, and \fBG\fR conversions the number will always have a decimal point. .TP .B \'0' specifies padding with zeros instead of spaces. .LP The field width is specified by a number. This number indicates the \fIminimum\fR width for the field. A '*' may be used instead of the number. In that case the width is the value of the next argument which should be an \fBint\fR. A negative width value specifies left adjustment with a width equal to the absolute width value. .LP A precision is specified by a '.' followed by a number. The meaning of the precision depends on the type conversion character. For a string conversion, precision determines how many characters are printed from the string. For integer conversions, precision determines the number of digits used to print the number (zero padding is done if the precision exceeds the length of the number). For floating point conversions, precision determines the number of digits after the decimal point (\fBe\fR, \fBE\fR, \fBf\fR) or the number of significant digits (\fBg\fR, \fBG\fR). A '*' may be used instead of a number to specify the precision. In that case the precision is the value of the next argument which should be an \fBint\fR. The behavior of \fBSprint()\fR is undefined if the precision is negative. .LP The length modifier is \fBl\fR and indicates that the argument is a \fBlong\fR integer. .LP The type conversion characters are: \fBd, i, o, x, X, u, c, s, f, e, E, g, G, p, n, %\fR. For floating point conversions the argument should be of type \fIdouble\fR. .TP 10 .B d,i specify signed decimal conversion. .TP .B u specifies unsigned decimal conversion. .TP .B o specifies octal conversion. .TP .B x,X specify hexadecimal conversion. For .B x the hex digits used are 0123456789abcdef. For .B X the hex digits used are 0123456789ABCDEF. There is no leading .B 0x or .B 0X (use the '#' flag for that). .TP .B c specifies character conversion; the argument should be of type \fIchar\fR. .TP .B s specifies string conversion; the argument should be of type \fIchar *\fR. .TP .B f specifies conversion to the form [-]ddd.dddd. The number of digits after the decimal point depends on precision; the default is 6. If the precision is 0, the decimal point is not printed (this can be overriden with the '#' flag). .B e,E specify conversion to the form [-]ddd.dddd[eE][+-]ddd. The number of digits after the decimal point depends on precision; the default is 6. If the precision is 0, the decimal point is not printed (this can be overriden with the '#' flag). The exponent is at least 2 digit wide. .TP .B g,G specify a conversion using the .B e,E format respectively if the exponent is less than -4 or greater than or equal to the precision; otherwise the .B f format is used. .TP .B p is used to print pointers (type \fIvoid *\fR, or \fIchar *\fR if the compiler does not support the former). .TP .B n expects a \fIint *\fR argument and puts in that integer the number of characters already printed by this call. .TP .B % is used to print a \fI%\fR. .LP If an unknown conversion character is specified, the percent sign followed by that character will be printed. .SH RETURN VALUE .LP If no error occured, \fBSprint()\fR returns the number of characters printed. In case of error, it returns \fBSIO_ERR\fR. .SH BUGS .LP This is a list of differences between \fBSprint()\fR and the ANSI C Standard \fBprintf()\fR: .LP \fBSprint()\fR does not support non-removal of trailing zeroes for \fBg\fR and \fBG\fR conversions when the '#' flag is used. .LP \fBSprint()\fR does not support the h and L modifiers. .LP The current implementation assumes that \fIsizeof(int)==sizeof(long)\fR. .LP \fBSprint()\fR supports "%p" only if \fIsizeof(pointer)<=sizeof(int)\fR. *[SRC.TIN-1_22.SIO]EVENTS.H;1+,. // 4 `-t0@123KPWO 56;Æ7 |89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id: events.h,v 8.1 1993/03/13 01:13:58 panos Exp $ */ /* * Event codes * * We use a 2 letter code so that events that accumulate in a buffer * can be displayed as a string * We follow the convention that the first event letter is a capitalized * and the second letter is in lower case. This allows one to easily * recognize events in an event string. */ /* * The ENCODE macro takes 2 characters and creates a short integer * The size of the short integer is assumed to be 16-bits. * The macro makes sure that regardless of the endianess of the machine, * the low order byte contains the 1st character and the high order byte * contains the 2nd character. */ #ifdef LITTLE_ENDIAN #define ENCODE( c1, c2 ) ( (c1) + ( (c2) << 8 ) ) #else /* BIG_ENDIAN */ #define ENCODE( c1, c2 ) ( (c2) + ( (c1) << 8 ) ) #endif /* * Event codes for SIO interface functions * We use the first 2 lettes after the initial 'S' */ #define EV_SGETC ENCODE( 'G', 'e' ) #define EV_SPUTC ENCODE( 'P', 'u' ) #define EV_SREAD ENCODE( 'R', 'e' ) #define EV_SWRITE ENCODE( 'W', 'r' ) #define EV_SRDLINE ENCODE( 'R', 'd' ) #define EV_SFETCH ENCODE( 'F', 'e' ) #define EV_SUNDO ENCODE( 'U', 'n' ) #define EV_SDONE ENCODE( 'D', 'o' ) #define EV_SFLUSH ENCODE( 'F', 'l' ) #define EV_SCLOSE ENCODE( 'C', 'l' ) #define EV_STIE ENCODE( 'T', 'i' ) #define EV_SUNTIE ENCODE( 'U', 't' ) #define EV_SBUFTYPE ENCODE( 'B', 'u' ) /* * Event codes for internal functions * For the __sio_ functions we use 'S' and the first letter of * For the rest we use the first letter from the first two components of * their name, for example for try_memory_mapping we use Tm. */ #define EV_SIO_INIT ENCODE( 'S', 'i' ) #define EV_SIO_SWITCH ENCODE( 'S', 's' ) #define EV_SIO_READF ENCODE( 'S', 'r' ) #define EV_SIO_WRITEF ENCODE( 'S', 'w' ) #define EV_SIO_EXTEND_BUFFER ENCODE( 'S', 'e' ) #define EV_SIO_MORE ENCODE( 'S', 'm' ) #define EV_TRY_MEMORY_MAPPING ENCODE( 'T', 'm' ) #define EV_INITIAL_MAP ENCODE( 'I', 'm' ) #define EV_MAP_UNIT ENCODE( 'M', 'u' ) #define EV_INIT_INPUT_STREAM ENCODE( 'I', 'i' ) #define EV_INIT_OUTPUT_STREAM ENCODE( 'I', 'o' ) /* * The # of entries must be a power of 2 */ #define EVENT_ENTRIES 512 struct events { short next ; short start ; short *codes ; /* malloc'ed memory */ } ; typedef struct events events_s ; extern events_s *__sio_events ; #define ADD( index, x ) (index) += x ; \ (index) &= ( EVENT_ENTRIES - 1 ) #define EVENT( fd, code ) \ { \ events_s *evp = &__sio_events[ fd ] ; \ \ if ( evp->codes != NULL ) \ { \ evp->codes[ evp->next ] = code ; \ ADD( evp->next, 1 ) ; \ if ( evp->next == evp->start ) \ { ADD( evp->start, 1 ) ; } \ } \ } *[SRC.TIN-1_22.SIO]IMPL.H;1+,. // 4 -t0@123KPWO 56FÆ7 |89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id: impl.h,v 8.1 1993/03/13 01:15:06 panos Exp $ */ #ifndef SIO_BUFFER_SIZE #include "sioconf.h" #ifdef HAS_MMAP #include /* * A struct map_unit describes a memory mapped area of a file. * * addr is the address where the file is mapped. If addr is NULL * the other fields are meaningless. * valid_bytes indicates how many bytes are _valid_ in that unit * mapped_bytes of a unit is how many bytes are mapped in that * unit ( valid <= mapped ). * Normally mapped_bytes will be equal to valid_bytes until * we reach the end of the file. Then if the file size is not a multiple * of the unit size, only the rest of the file will be mapped at the * unit, leaving part of what was mapped at the unit before still * visible (this happens because I chose not to unmap a unit before * remapping it). In that case valid_bytes shows the size of the "new" * mapping and mapped_bytes shows how many bytes are really mapped. * mapped_bytes is used in Sdone() to unmap the units. */ struct map_unit { caddr_t addr ; size_t valid_bytes ; size_t mapped_bytes ; } ; /* * Meaning of fields used when memory mapping: * * file_offset: it is the offset in the file where the next * mapping should be done * * file_size: size of the file (obtained from stat(2)) */ struct mmap_descriptor { off_t file_offset ; off_t file_size ; struct map_unit first_unit ; struct map_unit second_unit ; } ; typedef struct mmap_descriptor mapd_s ; #endif /* HAS_MMAP */ typedef enum { FAILURE, SUCCESS } status_e ; /* * Descriptor management: convert a descriptor pointer to an input or * output descriptor pointer */ #define IDP( dp ) (&(dp)->descriptor.input_descriptor) #define ODP( dp ) (&(dp)->descriptor.output_descriptor) #define DESCRIPTOR_INITIALIZED( dp ) ((dp)->initialized) /* * Internal constants */ #define SIO_BUFFER_SIZE 8192 #define SIO_NO_TIED_FD (-1) typedef enum { NO = 0, YES = 1 } boolean_e ; #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif #ifndef NULL #define NULL 0 #endif #ifdef MIN #undef MIN #endif #define MIN( a, b ) ( (a) < (b) ? (a) : (b) ) #define NUL '\0' #define PRIVATE static #ifdef DEBUG static char *itoa( num ) unsigned num ; { #define NUMBUF_SIZE 15 static char numbuf[ NUMBUF_SIZE ] ; register char *p = &numbuf[ NUMBUF_SIZE ] ; *--p = '\0' ; do { *--p = num % 10 + '0' ; num /= 10 ; } while ( num ) ; return( p ) ; } # define ASSERT( expr ) \ if ( ! (expr) ) \ { \ char *s1 = "SIO assertion << expr >> failed: File: " ; \ char *s2 = __FILE__ ; \ char *s3 = ", line: " ; \ char *s4 = itoa( __LINE__ ) ; \ char *s5 = "\n" ; \ (void) write( 2, s1, strlen( s1 ) ) ; \ (void) write( 2, s2, strlen( s2 ) ) ; \ (void) write( 2, s3, strlen( s3 ) ) ; \ (void) write( 2, s4, strlen( s4 ) ) ; \ (void) write( 2, s5, strlen( s5 ) ) ; \ exit ( 1 ) ; \ } #else # define ASSERT( expr ) #endif #include extern int errno ; /* * IO_SETUP initializes a descriptor if it is not already initialized. * It checks if the stream is of the right type (input or output). * CONTROL_SETUP checks if the descriptor is initialized and if the * stream is of the right type (input or output). * * fd: file descriptor * dp: descriptor pointer * op: operation * ev: error value (if __sio_init fails; __sio_init should set errno) * * IO_SETUP will call __sio_init if the descriptor is not initialized. * Possible errors: * 1. Using CONTROL_SETUP on an uninitialized descriptor. * 2. The operation is not appropriate for the descriptor (e.g. * a read operation on an descriptor used for writing). * Both errors set errno to EBADF. */ #define CONTROL_SETUP( dp, type, ev ) \ { \ if ( ! DESCRIPTOR_INITIALIZED( dp ) || dp->stream_type != type ) \ { \ errno = EBADF ; \ return( ev ) ; \ } \ } #define IO_SETUP( fd, dp, type, ev ) \ { \ if ( DESCRIPTOR_INITIALIZED( dp ) ) \ { \ if ( dp->stream_type != type ) \ { \ ep6e~ TIN-1_22.BCKt[SRC.TIN-1_22.SIO]IMPL.H;1 L rrno = EBADF ; \ return( ev ) ; \ } \ } \ else if ( __sio_init( dp, fd, type ) == SIO_ERR ) \ return( ev ) ; \ } /* * Internal functions that are visible */ int __sio_readf(), __sio_writef(), __sio_pwritef() ; int __sio_extend_buffer(), __sio_init(), __sio_converter(), __sio_more() ; status_e __sio_switch() ; #ifdef HAS_MEMOPS #include #define sio_memcopy( from, to, nbytes ) (void) memcpy( to, from, nbytes ) #define sio_memscan( from, nbytes, ch ) memchr( from, ch, nbytes ) #endif #ifdef HAS_BCOPY #define sio_memcopy( from, to, nbytes ) (void) bcopy( from, to, nbytes ) #endif #ifndef sio_memcopy #define sio_memcopy __sio_memcopy #define NEED_MEMCOPY void __sio_memcopy() ; #endif #ifndef sio_memscan char *sio_memscan() ; #endif #endif /* SIO_BUFFER_SIZE */ *[SRC.TIN-1_22.SIO]MAKEFILE.;15+,.// 4K-t0123KPWO56 i~87`889IʒG/HJ# Available entries:"# lib --> creates the library,# clean --> removes all .obj and .a files"# spotless --> clean + uninstall@# tags --> creates a tags file (from the SOURCES and HEADERS) NAME = sioVERSION = 1.6.1+HEADERS = sio.h impl.h events.h sioconf.h#SOURCES = sprint.c sio.c siosup.c)OBJECTS = sprint.obj sio.obj siosup.objMANFILES = sio.3 Sprint.3INCLUDEFILES = sio.h# Available flags:J# -DDEBUG : enables assertions in the code. A failed assertion.# terminates the programK# -DEVENTS : enables code that records events (currently limitedJ# to which functions have been called on a given fd)A# and code that accesses the event buffers.I# -DLITTLE_ENDIAN : says that the machine is a little endian. This isI# needed if you enable EVENTS and your machine is aB# little endian (big endian is the default).#E# DEFS should be set from the command line; the current group of defs# is for SunOS 4.x#-DEFS = HAS_ISATTY,MULTINET,HAS_MEMOPS#,DEBUG CC = gcc,DEBUG = /optim=2/warn #/debug/noopt/profile2VERSION_DEF = VERSION="""SIO Version $(VERSION)"""+CPP_DEFS = /define=($(VERSION_DEF),$(DEFS))#6# The following variables shouldn't need to be changed#LINT_FLAGS = -hbuxCPP_FLAGS = $(CPP_DEFS)CC_FLAGS = $(DEBUG)#CFLAGS = $(CPP_FLAGS) $(CC_FLAGS)LIBNAME = lib$(NAME).olblib: $(LIBNAME)$(LIBNAME): $(OBJECTS)8 library/create $(LIBNAME) sprint.obj,sio.obj,siosup.obj install: lib copy/log $(LIBNAME) local:[lib]clean:# del/log $(OBJECTS) $(LIBNAME) core#-# PUT HERE THE RULES TO MAKE THE OBJECT FILES#$sprint.obj: sio.h impl.h sioconf.h-sio.obj: sio.h impl.h sioconf.h events.h-siosup.obj: sio.h impl.h sioconf.h events.h*[SRC.TIN-1_22.SIO]MAKESIO.COM;2+,5 .// 4*-t0123KPWO56E7 h898F9G/HJ$ set def [.sio]$ make $ set def [-]*[SRC.TIN-1_22.SIO]SIO.3;1+,.// 4-t0@123KPWO56yCÆ7)}89]VG/HJ.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id: sio.3,v 8.1 1993/03/13 01:13:58 panos Exp $ .TH SIO 3X "29 May 1992" .SH NAME Sread, Sgetc, Srdline, Sfetch, Swrite, Sputc, Sprint, Sprintv, Sdone, Sundo, Stie, Suntie, Sflush, Sclose, Sbuftype, Smorefds, Sgetchar, Sputchar, SIOLINELEN, SIOMAXLINELEN - fast stream I/O .SH SYNOPSIS .LP .nf .ft B #include "sio.h" #include .LP .ft B int Sread( fd, buf, nbytes ) int fd ; char *buf ; int nbytes ; .LP .ft B int Sgetc( fd ) int fd ; .LP .ft B char *Srdline( fd ) int fd ; .LP .ft B char *Sfetch( fd, length ) int fd ; long *length ; .LP .ft B int Swrite( fd, buf, nbytes ) int fd ; char *buf ; int nbytes ; .LP .ft B int Sputc( fd, c ) int fd ; char c ; .LP .ft B int Sprint( fd, format [ , ... ] ) int fd ; char *format ; .LP .ft B int Sprintv( fd, format, ap ) int fd ; char *format ; va_list ap ; .LP .ft B int Sdone( fd ) int fd ; .LP .ft B int Sundo( fd, type ) int fd ; int type ; .LP .ft B int Stie( ifd, ofd ) int ifd, ofd ; .LP .ft B int Suntie( fd ) int fd ; .LP .ft B int Sbuftype( fd, type ) int fd, type ; .LP .ft B int Smorefds() .LP .ft B int Sflush( fd ) int fd ; .LP .ft B int Sclose( fd ) int fd ; .LP .ft B int Sgetchar( fd ) int fd ; .LP .ft B int Sputchar( fd, c ) int fd; char c ; .LP .ft B int SIOLINELEN( fd ) int fd ; .LP .ft B int SIOMAXLINELEN( fd ) int fd ; .SH DESCRIPTION The \fISIO\fR library provides support for \fIstream\fR I/O on file descriptors. The first argument of every function or macro is a file descriptor. The file descriptor may be used either for input or for output but not both. Attempting to use a descriptor for both input and output will cause the call to fail. When you are done with using a file descriptor, you should inform \fISIO\fR by invoking \fBSdone()\fR (unless the program is about to call \fIexit(3)\fR). You can also use \fBSdone()\fR if you want to perform a different type of operation on the same file descriptor (e.g. first you were reading data from the file descriptor and then you want to write some data). Another possibility is to do stream I/O at different file offsets by using \fBSdone()\fR before doing the \fBlseek(2)\fR to the new file offset. .LP I/O operations on different file descriptors do not interfere (unless the file descriptors refer to the same file, in which case the results are undefined). .LP For disk files I/O always starts at the current file offset. If that offset is not a multiple of the preferred block size for file system I/O (the \fIst_blksize\fR field in \fIstruct stat\fR), performance will not be optimal. For optimal performance, it is recommended that no I/O operations (like \fIread(2)\fR or \fIwrite(2)\fR) are applied to the file descriptor if it is to be used by \fISIO\fR. .LP Read I/O is either buffered or is done using memory mapping whenever that is possible and appropriate. .LP The library functions that do stream I/O resemble system calls (for example \fBSread()\fR resembles \fIread(2)\fR) so that modifying a program that uses the system calls to use the \fISIO\fR functions is easy (e.g. just replace \fIread(2)\fR with \fBSread()\fR; the function signatures as well as the return values are exactly the same). .LP .B Sread() reads \fInbytes\fR bytes from the stream associated with file descriptor \fIfd\fR into the buffer pointed to by \fIbuf\fR. .LP .B Sgetc() reads a character from the stream associated with file descriptor \fIfd\fR. It returns \fBSIO_EOF\fR if the end of file has been reached. .LP .B Sgetchar() (a macro) performs exactly the same function as \fBSgetc()\fR but it is much faster. .LP .B Srdline() reads a line from the stream associated with file descriptor \fIfd\fR. The newline at the end of the line is replaced by a 0 byte. Lines longer than the maximum line length supported by \fISIO\fR will have characters deleted. .LP .B SIOLINELEN() (a macro) returns the length of the line returned by the last call to \fBSrdline()\fR (the value returned by \fBSIOLINELEN()\fR is valid only after \fBSrdline()\fR and as long as no other \fISIO\fR calls are performed on that file descriptor). .LP .B SIOMAXLINELEN() (a macro) returns the maximul line length supported by \fISIO\fR for the file descriptor. As a side-effect it initializes \fIfd\fR for input. .LP .B Sfetch() returns a pointer to data coming from the stream associated with file descriptor \fIfd\fR. The amount of data available is indicated by the \fIlength\fR argument. One possible use for this function is copying of files. .LP .B Swrite() writes \fInbytes\fR bytes to the stream associated with file descriptor \fIfd\fR from the buffer pointed to by \fIbuf\fR. .LP .B Sputc() writes a single character to the stream associated with file descriptor \fIfd\fR. .LP .B Sputchar() (a macro) performs exactly the same function as \fBSputc()\fR but it is much faster. .LP .B Sprint() imitates the behavior of printf(3) as defined in the ANSI C Standard. There are some limitations. Check the \fBSprint()\fR man page for more information. .LP .B Sprintv() is the same as \fBSprint()\fR except that it takes a \fIvarargs\fR argument list. .LP .B Sundo() returns the characters returned by the last call to \fBSrdline()\fR, \fBSgetc()\fR or \fBSgetchar()\fR to the stream so that they can be reread. The \fItype\fR argument to \fBSundo()\fR can be \fBSIO_UNDO_LINE\fR or \fBSIO_UNDO_CHAR\fR depending on whether the call whose effect needs to be undone was \fBSrdline()\fR or \fBSgetc()\fR/\fBSgetchar()\fR respectively. There is no check on whether the last function invoked on \fIfd\fR was one of the above and the results are undefined if there is no correspondence between the \fItype\fR and the last operation on \fIfd\fR. (i.e. the result is undefined if you try \fBSIO_UNDO_CHAR\fR and the last operation was not \fBSgetchar()\fR or \fBSgetc()\fR). .LP .B Stie() ties the file descriptor \fIifd\fR to the file descriptor \fIofd\fR. This means that whenever a \fIread(2)\fR is done on \fIifd\fR, it is preceded by a \fIwrite(2)\fR on \fIofd\fR. For filters it is useful to do \fIStie( 0, 1 )\fR to maximize concurrency. It is also useful to do the same thing when you issue prompts to the user and you want the user reply to appear on the same line with the prompt. \fIifd\fR, \fIofd\fR will be initialized for input, output respectively (if any of them is initialized, it must be for the appropriate stream type (input or output)). If \fIifd\fR was tied to another file descriptor, the old tie is broken. .LP .B Suntie() undoes the effect of \fBStie()\fR for the specified input file descriptor. .LP .B Sbuftype() determines the buffering type for the output stream associated with file descriptor \fIfd\fR. By default output directed to terminals is line buffered, output directed to file descriptor 2 (standard error) is unbuffered and everything else is fully buffered. Possible values for the \fItype\fR argument are .RS .TP 15 .SB SIO_FULLBUF for full buffering .TP .SB SIO_LINEBUF for line buffering .TP .SB SIO_NOBUF for no buffering .RE .LP .B Smorefds() should be used to inform \fBSIO\fR that the number of available file descriptors has been increased. \fBSIO\fR uses an array of internal stream descriptors which are indexed by the file descriptor number. Some operating systems (ex. SunOS 4.1[.x]) allow the number of available file descriptors to vary. If that number is increased beyond its initial value \fBSIO\fR needs to know in order to allocate more stream descriptors. .LP .B Sdone() flushes any buffered output for \fIfd\fR and releases the \fISIO\fR resources used. \fBSdone()\fR is useful in case the program needs to reprocess the data of a file descriptor (assuming the file descriptor corresponds to a file). The program can call \fBSdone()\fR, \fIlseek(2)\fR to the beginning of the file and then proceed to reread the file. .LP .B Sflush() causes any buffered stream output to be written to the file descriptor. If its argument is the special value \fBSIO_FLUSH_ALL\fR then all output streams will be flushed. .LP .B Sclose() closes a file descriptor used for stream I/O, flushes any buffered output and releases the \fISIO\fR resources used. .SH EXAMPLES .LP The following code implements a (poor) substitute for the tee command (it copies standard input to a file as well as to standard output). .ne 10 .RS .nf .ft B #include "sio.h" .sp .5 main( argc, argv ) int argc ; char *argv[] ; { char *file = (argc > 1) ? argv[ 1 ] : "tee.file" ; int fd = creat( file, 0644 ) ; long length ; char *s ; .sp .5 while ( s = Sfetch( 0, &length ) ) { Swrite( 1, s, length ) ; Swrite( fd, s, length ) ; } exit( 0 ) ; } .fi .ft R .RE .SH RETURN VALUES .LP .B Sread() returns the number of bytes read on success (0 means end-of-file) or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sgetc() returns the character read on success, SIO_EOF when the end-of-file is reached, or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Srdline() returns a pointer to the next line on success. On failure or when the end-of-file is reached it returns .SM NULL. If the end-of-file is reached \fIerrno\fR is set to 0, otherwise it indicates the error. .LP .B Sfetch() returns a pointer to file data on success. (the \fIlength\fR argument indicates how many bytes are available). On failure or when the end-of-file is reached it returns .SM NULL. If the end-of-file is reached \fIerrno\fR is set to 0, otherwise it indicates the error. .LP .B Swrite() returns the number of bytes written on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sputc() returns the character it was given as an argument on success .B Sprint() returns the number of characters printed on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sdone() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sundo() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Stie() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Suntie() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to \fBEBADF\fR if there was no tied file descriptor). .LP .B Sbuftype() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to \fBEBADF\fR if this is not an output stream or to \fBEINVAL\fR if an unknown \fItype\fR is specified). .LP .B Smorefds() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (because of lack of memory). .LP .B Sflush() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sclose() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sgetchar() returns the character read on success, SIO_EOF when the end-of-file is reached, or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sputchar() returns the character it was given as an argument on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B SIOLINELEN() returns the length of the last line read by \fBSrdline()\fR. .LP .B SIOMAXLINELEN() returns the length of the longest line supported by \fISIO\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP Attempting a read operation on a descriptor opened for writing or vice versa will cause the operation to fail with \fIerrno\fR set to \fBEBADF\fR. .LP The first \fISIO\fR operation on a descriptor must be a read or write operation. It cannot be a control operation (like \fBSflush()\fR). Such an operation will fail with \fIerrno\fR set to \fBEBADF\fR. .LP .IP "\fBNOTE 1:\fR" 15 \fBStie()\fR is an input/output operation for the respective file descriptors, not a control operation. \fBSuntie()\fR is a control operation. .IP "\fBNOTE 2:\fR" \fBSIO_ERR\fR is defined to be \fB-1\fR. .SH "SEE ALSO" .LP Sprint(3) .SH BUGS .LP If the operating system does not provide for invocation of a finalization function upon exit, the program will have to explicitly flush all output streams. The following operating systems provide such a facility: SunOS 4.x, Ultrix 4.x. .LP Socket file descriptors can be used for input as well as output but \fBSIO\fR does not support this. .LP The current implementation will not try to use memory mapping to read a file if the file offset is not 0 (it will use buffered I/O instead). .LP Pointers returned by \fBSfetch()\fR point to read-only memory. Attempting to modify this memory will result in a segmentation violation. *[SRC.TIN-1_22.SIO]SIO.C;2+,.// 4-t0@123KPWO569+͆7,͆89]VG/HJ6/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ static char RCSid[] = "$Id: sio.c,v 8.1 1993/03/13 01:15:55 panos Exp $" ; static char sio_version[] = VERSION ; #include #include #include "sio.h" #include "impl.h" #ifdef EVENTS #include "events.h" #endif /* * SIO WRITE FUNCTIONS: Swrite, Sputc */ /* * Stream write call: arguments same as those of write(2) */ int Swrite( fd, addr, nbytes ) int fd ; register char *addr ; register int nbytes ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_od_t *odp = ODP( dp ) ; register int b_transferred ; register int b_avail ; int total_b_transferred ; int b_written ; int b_in_buffer ; #ifdef EVENTS EVENT( fd, EV_SWRITE ) ; #endif IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ; ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ; b_avail = odp->buf_end - odp->nextb ; b_transferred = MIN( nbytes, b_avail ) ; sio_memcopy( addr, odp->nextb, b_transferred ) ; odp->nextb += b_transferred ; /* * check if we are done */ if ( b_transferred == nbytes ) return( b_transferred ) ; /* * at this point we know that the buffer is full */ b_in_buffer = odp->buf_end - odp->start ; b_written = __sio_writef( odp, fd ) ; if ( b_written != b_in_buffer ) return( (b_written >= nbytes) ? nbytes : b_written ) ; total_b_transferred = b_transferred ; addr += b_transferred ; nbytes -= b_transferred ; for ( ;; ) { b_transferred = MIN( nbytes, odp->buffer_size ) ; sio_memcopy( addr, odp->nextb, b_transferred ) ; odp->nextb += b_transferred ; nbytes -= b_transferred ; if ( nbytes == 0 ) { total_b_transferred += b_transferred ; break ; } /* * the buffer is full */ b_written = __sio_writef( odp, fd ) ; if ( b_written != odp->buffer_size ) { if ( b_written != SIO_ERR ) { total_b_transferred += b_written ; odp->nextb += b_written ; } break ; } /* * everything is ok */ total_b_transferred += b_transferred ; addr += b_transferred ; } return( total_b_transferred ) ; } /* * Add a character to a file */ int Sputc( fd, c ) int fd ; char c ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_od_t *odp = ODP( dp ) ; #ifdef EVENTS EVENT( fd, EV_SPUTC ) ; #endif IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ; ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ; /* * The following is a weak check since we should really be * checking that nextb == buf_end (it would be an error for * nextb to exceed buf_end; btw, the assertion above, when * enabled makes sure this does not occur). * * NOTE: __sio_writef NEVER uses data beyond the end of buffer. */ if ( odp->nextb >= odp->buf_end ) { int b_in_buffer = odp->buf_end - odp->start ; /* * There is nothing we can do if __sio_writef does not manage * to write the whole buffer */ if ( __sio_writef( odp, fd ) != b_in_buffer ) return( SIO_ERR ) ; } *odp->nextb++ = c ; if ( __SIO_MUST_FLUSH( *odp, c ) && __sio_writef( odp, fd ) == SIO_ERR ) return( SIO_ERR ) ; return ( c ) ; } /* * SIO READ FUNCTIONS */ /* * Stream read call: arguments same as those of read(2) */ int Sread( fd, addr, nbytes ) int fd ; char *addr ; int nbytes ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_id_t *idp = IDP( dp ) ; register int b_transferred ; int b_read ; int total_b_transferred ; int b_left ; #ifdef EVENTS EVENT( fd, EV_SREAD ) ; #endif IO_SETUP( fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ; ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; b_left = idp->end - idp->nextb ; b_transferred = MIN( b_left, nbytes ) ; sio_memcopy( idp->nextb, addr, b_transferred ) ; idp->nextb += b_transferred ; if ( b_transferred == nbytes ) return( b_transferred ) ; nbytes -= b_transferred ; total_b_transferred = b_transferred ; addr += b_transferred ; do { b_read = __sio_readf( idp, fd ) ; switch ( b_read ) { case SIO_ERR: if ( total_b_transferred == 0 ) return( SIO_ERR ) ; /* FALL THROUGH */ case 0: return( total_b_transferred ) ; } b_transferred = MIN( b_read, nbytes ) ; sio_memcopy( idp->nextb, addr, b_transferred ) ; addr += b_transferred ; idp->nextb += b_transferred ; total_b_transferred += b_transferred ; nbytes -= b_transferred ; } while ( nbytes && b_read == idp->buffer_size ) ; return( total_b_transferred ) ; } /* * Read a line from a file * Returns a pointer to the beginning of the line or NULL */ char *Srdline( fd ) int fd ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_id_t *idp = IDP( dp ) ; register char *cp ; register char *line_start ; register int b_left ; register int extension ; #ifdef EVENTS EVENT( fd, EV_SRDLINE ) ; #endif IO_SETUP( fd, dp, __SIO_INPUT_STREAM, NULL ) ; ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; #ifdef HAS_MMAP if ( idp->memory_mapped && __sio_switch( idp, fd ) == FAILURE ) return( NULL ) ; #endif b_left = idp->end - idp->nextb ; /* * Look for a '\n'. If the search fails, extend the buffer * and search again (the extension is performed by copying the * bytes that were searched to the auxiliary buffer and reading * new input in the main buffer). * If the new input still does not contain a '\n' and there is * more space in the main buffer (this can happen with network * connections), read more input until either the buffer is full * or a '\n' is found. * Finally, set cp to point to the '\n', and line_start to * the beginning of the line */ if ( b_left && ( cp = sio_memscan( idp->nextb, b_left, '\n' ) ) != NULL ) { line_start = idp->nextb ; idp->nextb = cp + 1 ; } else { extension = __sio_extend_buffer( idp, fd, b_left ) ; if ( extension > 0 ) { ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; line_start = idp->start ; cp = sio_memscan( idp->nextb, extension, '\n' ) ; if ( cp != NULL ) idp->nextb = cp + 1 ; else for ( ;; ) { idp->nextb = idp->end ; extension = __sio_more( idp, fd ) ; if ( extension > 0 ) { cp = sio_memscan( idp->nextb, extension, '\n' ) ; if ( cp == NULL ) continue ; idp->nextb = cp + 1 ; break ; } else { /* * If there is spare room in the buffer avoid trashing * the last character */ if ( idp->end < &idp->buf[ idp->buffer_size ] ) cp = idp->end ; else cp = &idp->buf[ idp->buffer_size - 1 ] ; break ; } } } else /* buffer could not be extended */ if ( b_left == 0 ) { /* * Set errno to 0 if EOF has been reached */ if ( extension == 0 ) errno = 0 ; return( NULL ) ; } else { line_start = idp->start ; cp = idp->end ; /* * By setting idp->nextb to be equal to idp->end, * subsequent calls to Srdline will return NULL because * __sio_extend_buffer will be invoked and it will return 0. */ idp->nextb = idp->end ; } } *cp = NUL ; idp->line_length = cp - line_start ; return( line_start ) ; } /* * Get a character from a file */ int Sgetc( fd ) int fd ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_id_t *idp = IDP( dp ) ; #ifdef EVENTS EVENT( fd, EV_SGETC ) ; #endif IO_SETUP( fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ; ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; if ( idp->nextb >= idp->end ) { register int b_read = __sio_readf( idp, fd ) ; if ( b_read == 0 ) return( SIO_EOF ) ; else if ( b_read == SIO_ERR ) return( SIO_ERR ) ; } return( (int) *idp->nextb++ ) ; } char *Sfetch( fd, lenp ) int fd ; long *lenp ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_id_t *idp = IDP( dp ) ; register int b_read ; register char *p ; #ifdef EVENTS EVENT( fd, EV_SFETCH ) ; #endif IO_SETUP( fd, dp, __SIO_INPUT_STREAM, NULL ) ; ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; if ( idp->nextb >= idp->end ) { b_read = __sio_readf( idp, fd ) ; if ( b_read == SIO_ERR ) return( NULL ) ; if ( b_read == 0 ) { errno = 0 ; return( NULL ) ; } } *lenp = idp->end - idp->nextb ; p = idp->nextb ; idp->nextb = idp->end ; return( p ) ; } /* * SIO CONTROL FUNCTIONS */ /* * Undo the last Srdline or Sgetc */ int Sundo( fd, type ) int fd ; int type ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_id_t *idp = IDP( dp ) ; int retval = 0 ; #ifdef EVENTS EVENT( fd, EV_SUNDO ) ; #endif CONTROL_SETUP( dp, __SIO_INPUT_STREAM, SIO_ERR ) ; /* * Undo works only for fd's used for input */ if ( dp->stream_type != __SIO_INPUT_STREAM ) return( SIO_ERR ) ; /* * Check if the operation makes sense; if so, do it, otherwise ignore it */ switch ( type ) { case SIO_UNDO_LINE: if ( idp->nextb - idp->line_length > idp->start ) { *--idp->nextb = '\n' ; idp->nextb -= idp->line_length ; } ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; break ; case SIO_UNDO_CHAR: if ( idp->nextb > idp->start ) idp->nextb-- ; ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; break ; default: retval = SIO_ERR ; break ; } return( retval ) ; } /* * Flush the buffer associated with the given file descriptor * The special value, SIO_FLUSH_ALL flushes all buffers * * Return value: * 0 : if fd is SIO_FLUSH_ALL or if the flush is successful * SIO_ERR: if fd is not SIO_FLUSH_ALL and * the flush is unsuccessful * or the descriptor is not initialized or it is not * an output descriptor */ int Sflush( fd ) int fd ; { register __sio_descriptor_t *dp ; int b_in_buffer ; #ifdef EVENTS EVENT( fd, EV_SFLUSH ) ; #endif if ( fd == SIO_FLUSH_ALL ) { for ( fd = 0, dp = __sio_descriptors ; fd < N_SIO_DESCRIPTORS ; dp++, fd++ ) if ( DESCRIPTOR_INITIALIZED( dp ) && dp->stream_type == __SIO_OUTPUT_STREAM ) (void) __sio_writef( ODP( dp ), fd ) ; return( 0 ) ; } else { dp = &__sio_descriptors[ fd ] ; CONTROL_SETUP( dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ; b_in_buffer = ODP( dp )->nextb - ODP( dp )->start ; if ( __sio_writef( ODP( dp ), fd ) != b_in_buffer ) return( SIO_ERR ) ; else return( 0 ) ; } } /* * Close the file descriptor. This call is provided because * a file descriptor may be closed and then reopened. There is * no easy way for SIO to identify such a situation, so Sclose * must be used. * * Sclose invokes Sdone which finalizes the buffer. * There is no SIO_CLOSE_ALL value for fd because such a thing * would imply that the program will exit very soon, therefore * the closing of all file descriptors will be done in the kernel * (and the finalization will be done by the finalization function * NOTE: not true if the OS does not support a finalization function) * * There is no need to invoke SETUP; Sdone will do it. */ int Sclose( fd ) int fd ; { #ifdef EVENTS EVENT( fd, EV_SCLOSE ) ; #endif if ( __SIO_FD_INITIALIZED( fd ) ) if ( Sdone( fd ) == SIO_ERR ) return( SIO_ERR ) ; #ifdef MULTINET return( socket_close( fd ) ) ; #else return( close( fd ) ) ; #endif } /* * Tie the file descriptor in_fd to the file descriptor out_fd * This means that whenever a read(2) is done on in_fd, it is * preceded by a write(2) on out_fd. * Why this is nice to have: * 1) When used in filters it maximizes concurrency * 2) When the program prompts the user for something it forces * the prompt string to be displayed even if it does not * end with a '\n' (which would cause a flush). * In this implementation, out_fd cannot be a regular file. * This is done to avoid non-block-size write's. * The file descriptors are initialized if that has not been done * already. If any of them is initialized, it must be for the appropriate * stream type (input or output). * * NOTE: the code handles correctly the case when in_fd == out_fd */ int Stie( in_fd, out_fd ) int in_fd, out_fd ; { struct stat st ; register __sio_descriptor_t *dp ; int was_initialized ; boolean_e failed = NO ; #ifdef EVENTS EVENT( in_fd, EV_STIE ) ; #endif /* * Check if the out_fd is open */ if ( fstat( out_fd, &st ) == -1 ) return( SIO_ERR ) ; /* * If the out_fd refers to a regular file, the request is silently ignored */ if ( ( st.st_mode & S_IFMT ) == S_IFREG ) return( 0 ) ; dp = &__sio_descriptors[ in_fd ] ; was_initialized = dp->initialized ; /* remember if it was initialized */ IO_SETUP( in_fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ; /* * Perform manual initialization of out_fd to avoid leaving in_fd * initialized if the initialization of out_fd fails. * If out_fd is initialized, check if it is used for output. * If it is not initialized, initialize it for output. */ dp = &__sio_descriptors[ out_fd ] ; if ( DESCRIPTOR_INITIALIZED( dp ) ) { if ( dp->stream_type != __SIO_OUTPUT_STREAM ) { failed = YES ; errno = EBADF ; } } else if ( __sio_init( dp, out_fd, __SIO_OUTPUT_STREAM ) == SIO_ERR ) failed = YES ; if ( failed == NO ) { __SIO_ID( in_fd ).tied_fd = out_fd ; return( 0 ) ; } else { /* * We failed. If we did any initialization, undo it */ if ( ! was_initialized ) { int save_errno = errno ; (void) Sdone( in_fd ) ; errno = save_errno ; } return( SIO_ERR ) ; } } /* * Untie a  .rk~ TIN-1_22.BCKt[SRC.TIN-1_22.SIO]SIO.C;2jfile descriptor */ int Suntie( fd ) int fd ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; #ifdef EVENTS EVENT( fd, EV_SUNTIE ) ; #endif CONTROL_SETUP( dp, __SIO_INPUT_STREAM, SIO_ERR ) ; if ( IDP( dp )->tied_fd != SIO_NO_TIED_FD ) { IDP( dp )->tied_fd = SIO_NO_TIED_FD ; return( 0 ) ; } else { errno = EBADF ; return( SIO_ERR ) ; } } /* * Changes the type of buffering on the specified descriptor. * As a side-effect, it initializes the descriptor as an output stream. */ int Sbuftype( fd, type ) int fd, type ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; #ifdef EVENTS EVENT( fd, EV_SBUFTYPE ) ; #endif /* * Check for a valid type */ if ( type != SIO_LINEBUF && type != SIO_FULLBUF && type != SIO_NOBUF ) { errno = EINVAL ; return( SIO_ERR ) ; } IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ; ODP( dp )->buftype = type ; return( 0 ) ; } #ifndef sio_memscan PRIVATE char *sio_memscan( from, how_many, ch ) char *from ; int how_many ; register char ch ; { register char *p ; register char *last = from + how_many ; for ( p = from ; p < last ; p++ ) if ( *p == ch ) return( p ) ; return( 0 ) ; } #endif /* sio_memscan */ #ifdef NEED_MEMCOPY void __sio_memcopy( from, to, nbytes ) register char *from, *to ; register int nbytes ; { while ( nbytes-- ) *to++ = *from++ ; } #endif /* NEED_MEMCOPY */ *[SRC.TIN-1_22.SIO]SIO.DVI;1+,.'// 4'%-t0@123KPWO(56n:7u:89]VG/HJJ0K`y@@cmr10SIO(3X)[SIO(3X)"V @cmbx10pNAMEp%Sread,Sgetc,Srdline,Sfetc$h,Swrite,Sputc,Sprin"t,Sprin"tv,Sdone,Sundo,Stie,Sun"tie,S ush,%Sclose, Sbuft"ype, Smorefds, Sgetc$har, Sputc#har, SIOLINELEN, SIOMAXLINELEN - fast stream%I/O4SYNOPSIS"V@@cmbx10p%#include@@cmmi10 4int Sread( fd, buf, nbytes )%int fd ;%char *buf ;%int nbytes ;4int Sgetc( fd )%int fd ;4char *Srdline( fd )%int fd ;4char *Sfetch( fd, length )%int fd ;%long *length ;4int Swrite( fd, buf, nbytes )%int fd ;%char *buf ;%int nbytes ;4int Sputc( fd, c )%int fd ;%char c ;4int Sprint( fd, format [ , ... ] )%int fd ;%char *format ;4int Sprintv( fd, format, ap )%int fd ;%char *format ;%vawAlist ap ;4int Sdone( fd )%int fd ;4int Sundo( fd, type )%int fd ;%int type ;4int Stie( ifd, ofd )%int ifd, ofd ;4int Suntie( fd )%int fd ;4int Sbuftype( fd, type )%int fd, type ;4int Smorefds()4int S ush( fd )%int fd ;4int Sclose( fd )%int fd ;՞xz29 kMa"y k1992w 1SIO(3X)[SIO(3X)ppint Sgetchar( fd )%int fd ;4int Sputchar( fd, c )%int fd;%char c ;4int SIOLINELEN( fd )%int fd ;4int SIOMAXLINELEN( fd )%int fd ;4DESCRIPTIONp%The':@@cmti10 aSIO `library _pro"vides asupport _for _strheham `I/O `on _ le `descriptors.The a rst `argumen$t ^of ^ev#ery%function Por Omacro Qis Pa O le Pdescriptor. RThe Q le Pdescriptor Rma#y Obe Qused Reither Rfor Pinput Por Pfor Pout-%put but not both. A"ttempting to use a descriptor for both input and output will cause the call to%fail. When y"ou are done with using a le descriptor, y"ou should inform SIO b"y in"v"oking Sdone()%(unless Uthe Sprogram Sis Sabout Qto Qcall Rexit(3)).Yeou Qcan Ralso Ruse SSdone() Uif Qy"ou Qw#an"t Qto Qperform Sa%di eren%t }t"ype ~of }operation ~on }the ~same le ~descriptor (e.g.  rst y"ou ~w#ere reading data ~from the% le \descriptor ^and [then \y"ou [w#an"t [to [write ]some ^data).Another [possibilit$y Zis [to Zdo Zstream ]I/O [at%di eren%t k le lo sets ob"y kusing lSdone() obefore mdoing kthe llseek(2) sto kthe lnew m le lo set.4I/O operations on di eren%t le descriptors do not in"terfere (unless the le descriptors refer to the%same n le, lin kwhic$h kcase nthe lresults nare lunde ned).4Feor disk les I/O alw#a"ys starts at the curren$t le o set.MIf that o set is not a m#ultiple of the pre-%ferred bloc#ksize!for lesystem"I/O(thestwAblksize! eldinstructstat),"performance#will notbe%optimal._Feoroptimalperformance,itisrecommendedthatnoI/Ooperations(lik"ergehad(2)or%write(2)) oare lapplied lto kthe l le ldescriptor nif kit kis lto kbe lused mb"y kSIO.4ReadI/Oiseitherbu eredorisdoneusingmemorymappingwhenev%erthatispossibleand%appropriate.4The elibrary cfunctions ethat cdo cstream fI/O dresem&ble dsystem gcalls e(for cexample fSread() fresem&bles%rgehad(2)) ~so |that {modifying {a {program |that {uses ~the |system calls }to {use }the |SIO |functions }is }easy%(e.g. just replace rgehad(2) with Sread(); the function signatures as w#ell as the return v"alues are%exactly mthe lsame).4Sread()Sreadsnbytesb"ytesfromthestreamassociatedwith ledescriptorfdin"tothebu er%poin"ted lto kb"y kbuf.4Sgetc()reads3a1c#haracter3from2the2stream4associated3with1 le1descriptor3fd. It1returns%SIOwAEOF lif kthe lend lof k le lhas lbeen mreac$hed.4Sgetchar() (a kmacro) mperforms nexactly mthe lsame nfunction las lSgetc() qbut kit kis lm#uc#h kfaster.4Srdline() reads a line from the stream associated with le descriptor fd.The newline at the end%of 'the (line (is (replaced *b"y 'a '0 &b"yte. 'Lines (longer 'than &the 'maxim$um 'line 'length 'supported 'b"y &SIO%will lha"v"e lc#haracters ndeleted.4SIOLINELEN() (a -macro) /returns /the .length .of -the .line .returned 0b"y .the /last /call /to .Srdline()%(theOv"alueOreturnedPb"yMSIOLINELEN()SisNv"alidMonlyMafterNSrdline()SandMasNlongMasNnoMother%SIO lcalls mare lperformed non kthat k le ldescriptor).4SIOMAXLINELEN() (a macro) returns the maxim$ul line length supported b"y SIO for the  le%descriptor. nAs la kside-e ect rit kinitializes nfd lfor kinput.4Sfetch() returns ha fpoin"ter gto fdata fcoming hfrom fthe fstream hassociated gwith f le fdescriptor hfd. fThe%amoun#t )of )data )a"v"ailable *is *indicated +b"y )the *length /argumen$t. *One +possible -use ,for *this +function +is%cop#ying kof k les.4Swrite()writesnbytesb"ytestothestreamassociatedwith ledescriptorfdfromthebu er%poin"ted lto kb"y kbuf.՟29 kMa"y k1992w 2SIO(3X)[SIO(3X)ppSputc() writes na ksingle mc#haracter mto kthe lstream nassociated mwith l le ldescriptor nfd.4Sputchar() (a kmacro) mperforms nexactly mthe lsame nfunction las lSputc() obut kit kis lm#uc#h kfaster.4Sprint()imitates Ythe Wbeha#vior Vof Vprin"tf(3) Vas Wde ned Xin Vthe WANSI WC WStandard. VThere Zare Xsome%limitations. mChec%k kthe lSprint() nman lpage lfor kmore minformation.4Sprintv() is lthe lsame nas lSprint() nexcept nthat kit ktak"es ma kvariarhgs largumen$t klist.4Sundo() ~returns the c#haracters returned b"y the last call to Srdline(), Sgetc() or Sgetchar() to%thestreamsothattheycanbereread.Thetypieargumen$ttoSundo()canbe%SIOwAUNDOwALINE or SIOwAUNDOwACHAR depending on whether the call whose e ect needs%to kbe lundone mw#as mSrdline() ror lSgetc()/Sgetc har() orespectiv$elyf.There ois mno lc#hec$k lon lwhether%the last function in"v"ok"ed on fd w#as one of the abo"v"e and the results are unde ned if there is no%correspondence _bet#w#een ^the ]typie ]and \the ^last ^operation ^on ]fd.(i.e. ^the ^result _is ^unde ned _if ]y"ou%try kSIOwAUNDOwACHAR nand kthe llast loperation lw#as lnot kSgetchar() nor kSgetc()).4Stie() ties Hthe G le Gdescriptor Iifd Gto Fthe G le Fdescriptor Hofd.This Gmeans Hthat Ewhenev%er Fa Ergehad(2) His%done on ifd, it is preceded b"y a write(2) on ofd.)Feor lters it is useful to do Stie( 0, 1 ) to maxi-%mize concurrencyi.It is also useful to do the same thing when y"ou issue prompts to the user and%y"ou |w#an"t |the }user ~reply ~to }appear ~on }the ~same line ~with ~the ~prompt.ifd, ~ofdwill ~be ~initialized%for ?input, ?output ?respectiv$ely @(if ?an"y >of >them @is ?initialized, @it >m#ust ?be ?for >the ?appropriate ?stream%t"ype l(input kor koutput)).If lifd lw#as ltied lto kanother l le ldescriptor, nthe lold ktie lis lbrok"en.4Suntie() undoes mthe le ect oof kStie() pfor kthe lspeci ed ninput k le ldescriptor.4Sbuftype() Idetermines the bu ering t"ype for the output stream associated with le descriptor fd.%By default output directed to terminals is line bu ered, output directed to le descriptor 2 (stan-%dard error) is un"bu ered and ev#erything else is fully bu ered.P"ossible v"alues for the typie argu-%men$t karep4SIO!aFULLBUFA%for kfull kbu ering4SIO!aLINEBUFfor kline lbu ering4SIO!aNOBUF0for kno kbu ering4Smorefds() `should be used to inform SIO that the n"um#ber of a"v"ailable le descriptors has been%increased. SIO uses an arra"y of in"ternal stream descriptors whic$h are indexed b"y the  le descrip-%tor n"um#ber. Some operating systems (ex. SunOS 4.1[.x]) allo"w the n"um#ber of a"v"ailable le descrip-%tors Lto Kv"arye. KIf Lthat Kn"um#ber Lis Lincreased Obey#ond Kits Linitial Kv"alue LSIO Lneeds Nto Lkno"w Min Lorder Mto%allocate mmore mstream ndescriptors.4Sdone() J ushes an"y bu ered output for fd and releases the SIO resources used. Sdone() is useful%in *case -the +program +needs -to *reprocess .the +data *of *a * le +descriptor -(assuming -the + le +descriptor%corresponds to a le).#The program can call Sdone(), lsehek(2) to the beginning of the le and%then lproceed nto kreread mthe l le.4S ush() causes an"y bu ered stream output to be written to the le descriptor. If its argumen$t is%the lspecial mv"alue lSIOwAFLUSHwAALL lthen lall koutput kstreams owill lbe l ushed.4Sclose() 5closes a le descriptor used for stream I/O, ushes an"y bu ered output and releases the%SIO lresources pused.4EXAMPLESp%The follo"wing code implemen&ts a (poor) substitute for the tee command (it copies standard input՟xn29 kMa"y k1992w 3QSIO(3X)[SIO(3X)ppto ka k le las lw#ell las lto kstandard loutput).p%#include "sio.h"8vmain( argc, argv )p%int argc ;%char *argv[] ;!",@@cmsy10%fp%char * le = (argc > 1) ? argv[ 1 ] : "tee. le" ;%int fd = creat( le, 0644 ) ;%long length ;%char *s ;8vwhile ( s = Sfetch( 0, &length ) )%fp%Swrite( 1, s, length ) ;%Swrite( fd, s, length ) ;%g%exit( 0 ) ;%g(4RETURN ViALUESp%Sread() returns the n"um#ber of b"ytes read on success (0 means end-of- le) or SIOwAERR on fail-%ure l(errno nis lset mto kindicate mthe lerror).4Sgetc()|returnsthec#haracterreadonsuccess,SIOwAEOFwhentheend-of- leisreac$hed,or%SIOwAERR mon kfailure l(errno nis lset mto kindicate mthe lerror).4Srdline()>returnsapoin"tertothenextlineonsuccess.Onfailureorwhentheend-of- leis%reac$hed it returnsK`y @cmr10 NULL.'If the end-of- le is reac$hed errno is set to 0, otherwise it indicates the%error.4Sfetch()returns=a;poin"ter@@cmmi10':@@cmti10!",@@cmsy10K`y @cmr10G*[SRC.TIN-1_22.SIO]SIO.H;1+,.// 4 -t0@123KPWO56옇Æ7:}89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id: sio.h,v 8.1 1993/03/13 01:13:58 panos Exp $ */ #ifndef __SIO_H #define __SIO_H #include #include /* * Naming conventions: * 1) SIO functions and macros have names starting with a capital S * 2) SIO constants meant to be used by user programs have * names starting with SIO_ * 3) Internal functions, struct identifiers, enum identifiers * etc. have names starting with __sio * 4) Internal constants and macros have names starting with __SIO */ /* * external constants * * SIO_FLUSH_ALL: flush all output streams * SIO_EOF: eof on stream * SIO_ERR: operation failed */ #define SIO_FLUSH_ALL (-1) #define SIO_EOF (-2) #define SIO_ERR (-1) /* * Undo types */ #define SIO_UNDO_LINE 0 #define SIO_UNDO_CHAR 1 /* * Buffering types */ #define SIO_FULLBUF 0 #define SIO_LINEBUF 1 #define SIO_NOBUF 2 /* * Descriptor for an input stream */ struct __sio_input_descriptor { /* * buf: points to the buffer area. * When doing memory mapping, it is equal to the unit * from which we are reading. When doing buffered I/O * it points to the primary buffer. */ char *buf ; unsigned buffer_size ; char *start ; /* start of valid buffer contents */ char *end ; /* end of valid buffer contents + 1 */ char *nextb ; /* pointer to next byte to read/write */ /* Always: start <= nextb < end */ unsigned line_length ; int max_line_length ; int tied_fd ; int memory_mapped ; /* flag to denote if we use */ /* memory mapping */ } ; typedef struct __sio_input_descriptor __sio_id_t ; /* * Descriptor for an output stream */ struct __sio_output_descriptor { /* * buf: points to the buffer area. * buf_end: is equal to buf + buffer_size */ char *buf ; char *buf_end ; unsigned buffer_size ; char *start ; /* start of valid buffer contents */ /* (used by the R and W functions) */ char *nextb ; /* pointer to next byte to read/write */ /* Always: start <= nextb < buf_end */ int buftype ; /* type of buffering */ } ; typedef struct __sio_output_descriptor __sio_od_t ; /* * Stream types */ enum __sio_stream { __SIO_INPUT_STREAM, __SIO_OUTPUT_STREAM } ; /* * General descriptor */ struct __sio_descriptor { union { __sio_id_t input_descriptor ; __sio_od_t output_descriptor ; } descriptor ; enum __sio_stream stream_type ; int initialized ; } ; typedef struct __sio_descriptor __sio_descriptor_t ; /* * The array of descriptors (as many as available file descriptors) */ extern __sio_descriptor_t *__sio_descriptors ; extern int errno ; /* * Internally used macros */ #define __SIO_FD_INITIALIZED( fd ) (__sio_descriptors[ fd ].initialized) #define __SIO_ID( fd ) (__sio_descriptors[ fd ].descriptor.input_descriptor) #define __SIO_OD( fd ) (__sio_descriptors[ fd ].descriptor.output_descriptor) #define __SIO_MUST_FLUSH( od, ch ) \ ( (od).buftype != SIO_FULLBUF && \ ( (od).buftype == SIO_NOBUF || ch == '\n' ) ) /* * SIO Macros: * * SIOLINELEN( fd ) * SIOMAXLINELEN( fd ) * Sputchar( fd, c ) * Sgetchar( fd ) * * NOTE: The maximum line size depends on whether the descriptor * was originally memory mapped. If it was, then the maximum * line size will be the map_unit_size (a function of the system * page size and PAGES_MAPPED). Otherwise, it will be either the * optimal block size as reported by stat(2) or SIO_BUFFER_SIZE. */ #define SIOLINELEN( fd ) __SIO_ID( fd ).line_length #define SIOMAXLINELEN( fd ) \ ( \ __SIO_FD_INITIALIZED( fd ) \ ? ( \ (__sio_descriptors[ fd ].stream_type == __SIO_INPUT_STREAM) \ ? __SIO_ID( fd ).max_line_length \ : ( errno = EBADF, SIO_ERR ) \ ) \ : ( /* not initialized; initialize it for input */ \ (__sio_init( &__sio_descriptors[ fd ], fd, __SIO_INPUT_STREAM ) \ == SIO_ERR) \ ? SIO_ERR \ : __SIO_ID( fd ).max_line_length \ ) \ ) /* * Adds a character to a buffer, returns the character or SIO_ERR */ #define __SIO_ADDCHAR( od, fd, c )  \ ( od.buftype == SIO_FULLBUF ) \ ? (int) ( *(od.nextb)++ = (unsigned char) (c) ) \ : ( od.buftype == SIO_LINEBUF ) \ ? ( ( *(od.nextb) = (unsigned char) (c) ) != '\n' ) \ ? (int) *(od.nextb)++ \ : Sputc( fd, *(od.nextb) ) \ : Sputc( fd, c ) /* * The Sgetchar/Sputchar macros depend on the fact that the fields * nextb, buf_end, end * are 0 if a stream descriptor is not being used or has not yet been * initialized. * This is true initially because of the static allocation of the * descriptor array, and Sdone must make sure that it is true * after I/O on a descriptor is over. */ #define Sputchar( fd, c ) \ ( \ ( __SIO_OD( fd ).nextb < __SIO_OD( fd ).buf_end ) \ ? ( __SIO_ADDCHAR( __SIO_OD( fd ), fd, c ) ) \ : Sputc( fd, c ) \ ) #define Sgetchar( fd ) \ ( \ ( __SIO_ID( fd ).nextb < __SIO_ID( fd ).end ) \ ? (int) *__SIO_ID( fd ).nextb++ \ : Sgetc( fd ) \ ) #ifdef __ARGS #undef __ARGS #endif #ifdef PROTOTYPES # define __ARGS( s ) s #else # define __ARGS( s ) () #endif /* * The Read functions */ int Sread __ARGS( ( int fd, char *buf, int nbytes ) ) ; int Sgetc __ARGS( ( int fd ) ) ; char *Srdline __ARGS( ( int fd ) ) ; char *Sfetch __ARGS( ( int fd, long *length ) ) ; /* * The Write functions */ int Swrite __ARGS( ( int fd, char *buf, int nbytes ) ) ; int Sputc __ARGS( ( int fd, char c ) ) ; int Sprint __ARGS( ( int fd, char *format, ... ) ) ; int Sprintv __ARGS( ( int fd, char *format, va_list ) ) ; /* * other functions */ int Sdone __ARGS( ( int fd ) ) ; int Sundo __ARGS( ( int fd, int type ) ) ; int Sflush __ARGS( ( int fd ) ) ; int Sclose __ARGS( ( int fd ) ) ; int Sbuftype __ARGS( ( int fd, int type ) ) ; #endif /* __SIO_H */ *[SRC.TIN-1_22.SIO]SIOCONF.H;3+, . // 4 %-t0@123KPWO 56%dž7`dž89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id: sioconf.h,v 8.5 1993/03/17 02:54:47 panos Exp $ */ /* * This file has 2 sections: * 1. a OS-specific section * 2. a CPU/compiler-specific section * * You can override/redefing any of the constants/macros in this file. * by uncommenting the inclusion of customconf.h and placing your own * definitions in that file. */ /* #include "customconf.h" */ /* * OS-specific section. * * Features here use the flag HAS_. * List of flags (check the following for macros that can be overriden): * * HAS_MMAP (overridable macros) * * HAS_ATEXIT * HAS_ONEXIT * HAS_OTHER_FINALIZER (must define macros) * * HAS_MEMOPS * HAS_BCOPY (HAS_MEMOPS will be preferred if both are defined) * * At least one of the following flags must be defined. The 2nd and 3rd * flags are incompatible. * HAS_ISATTY * HAS_SYSVTTY * HAS_BSDTTY */ /* * Memory mapping. * The library requires 3 macros: SIO_MMAP, SIO_MUNMAP, SIO_MNEED. * You can selectively override any of them. * Notice that the SIO_MNEED macro is not required. If your system * does not have madvise, you can define the macro as: * #define SIO_MNEED( addr, len ) */ #ifdef HAS_MMAP #if !defined( SIO_MMAP ) || !defined( SIO_MUNMAP ) || !defined( SIO_MNEED ) #include #include #endif #ifndef SIO_MMAP #define SIO_MMAP( addr, len, fd, off ) \ mmap( addr, len, PROT_READ, \ ( addr == 0 ) ? MAP_PRIVATE : MAP_PRIVATE + MAP_FIXED, \ fd, off ) #endif #ifndef SIO_MUNMAP #define SIO_MUNMAP( addr, len ) munmap( addr, len ) #endif #ifndef SIO_MNEED #define SIO_MNEED( addr, len ) (void) madvise( addr, len, MADV_WILLNEED ) #endif #endif /* HAS_MMAP */ /* * N_SIO_DESCRIPTORS is the maximum number of file descriptors * supported by the OS */ #ifndef N_SIO_DESCRIPTORS #ifndef VMS #include #define N_SIO_DESCRIPTORS NOFILE #else #define N_SIO_DESCRIPTORS 128 #endif #endif /* * Finalization function. * * The purpose of this function is to do work after your program has * called exit(3). In the case of SIO, this means flushing the SIO * output buffers. * * If your system does not support atexit or onexit but has some other * way of installing a finalization function, you define the flag * HAS_FINALIZER. Then you must define the macros * SIO_FINALIZE and SIO_DEFINE_FIN * * SIO_FINALIZE attempts to install a finalization function and returns TRUE * if successful, FALSE if unsuccessful. * SIO_DEFINE_FIN defines the finalization function (the reason for this macro * s that different systems pass different number/type of arguments to the * finalization function; the SIO finalization function does not use any * arguments). */ #if defined(HAS_ONEXIT) || defined(HAS_ATEXIT) || defined(HAS_FINALIZER) #define HAS_FINALIZATION_FUNCTION #if defined( HAS_ONEXIT ) && defined( HAS_ATEXIT ) #undef HAS_ONEXIT #endif #ifdef HAS_ONEXIT #define SIO_FINALIZE( func ) ( on_exit( func, (caddr_t) 0 ) == 0 ) #define SIO_DEFINE_FIN( func ) static void func ( exit_status, arg ) \ int exit_status ; \ caddr_t arg ; #endif /* HAS_ONEXIT */ #ifdef HAS_ATEXIT #define SIO_FINALIZE( func ) ( atexi!0~  `yD$qH;1 Vva Pzq &BTolCBel$K#,V3V]dyJm_*c \|px-nrHDr=*Kb! !3'6P+ (Izsb=|h %-dK!-csPxxW..JgUD;O2jrx [qg[H =nB9}ObM1y67Oli7k+X_4xFb{^T FZ Cx V6?/qvgr [s},2#*jU wT'kd;Y]0g4;4` r_d H,* B.Yog_kY6Y\l<zX35 H_ db;jM:[]:{p|s.,_PT6'/WOJ&THR^PnZ& Yr8=_U59* Qs3EAyS\S(1%J"= K|!Z657mp{- D`):Q4}W!V/|Wj#PWWguslrLMslGsh ;y J~TF]u_h" r!54"2r0:f4KUq-(tX4w k^/z^H>EVmOP _X|&]qAs7=fXpWY(,y:^H\22&IyAiO?m4SSIR<`:D\=1'R,V85:MoRE/_E BlK? d`P^32g3CzZY\}w?-vs6lj|'G!dqCbP4$S@wCd\<msD+W_We9>5Et0`9x.b;uPe0w(pZPS&u7U OA8:M>F+>xC%Q\ dMV<~)FSvPJu5D [ XryQ2Q{3%Q="}((~7*bdfa $DB,U5"&9@+wVI(>{z*nq8Q@ m y[;$vQ97`xs011pL1IH]q?!z4M7k(-RT)l$cEcR+p"q%}M?:osqiPJn%ci0W$0zf^ TVFdKd2Af\MVj4*!;k<.E<]H *rmP*!B2qJBw^]R3EY3dbM[780,(9.r[1yb0hg\K -0VRAY6ZM<4 h4=3vo|3-  I * cWCe1]J[Z.|wxnd99ehK{A+e\}FTo~-\*a|G>T'xgK>q\OYWM 8 kfx[zT:9T+iF< r\MstiO;!8:_T?SsTk 'f;#*>'cnY+3,%P+6n,rjEsL--"H|7Kc&Py:gom:sQS1;CS|<gSLl v ONKE';F$n;<  W5c+oEz 'y9k) 5vo~ +qGbyYN_\-D`p+"0ozcMLNZf@<m`Iy"qbιk5O6H#>11   O <8TkS]#ar uOIp~%;Qi~/IV vhz+*d1v0ZOIM3Y9fcqAyY n= #7f:Ki9f>m0 pq6YZ:|_Z)& Z[Sy$6TCR!;b\dn{Ӊov+F;MCQULc@y~8-U.mIvv3xUu0 ,qKC Ҥ"*^1zpe\ [5b,FL: >1]nR;ZQW=S0{:2>7Oq" c>[?Hj^rH55 V3TMU> fC=OKBag' >VL-8 U9ӊ,ONyS*_'^~M]lX&7@;d*z%VnCV+Б&Ըk0 `KH>inj }܍J<`+kpr9?2 5f${imC[~"r=o::KOfs6'&4x]\zmLYR-aE}p#'>B-jUm>tqԯw(% EcUQ PXj14*%ly:smhSJRP*}5VE@Jܾw,'CIwut[t`k#)=ds LV D]JMRZ7kw%`pic_.)8IӼO~B ,S?kW *jEpLCM0_HH?}Bđ(%qjs`]vֻ hCgw.+G[B JXvqQx7`I6p8v@ RϨ5Hm>=K tjxMg '+"XA~ih/C r Ve@5]Ҋ9:gtcvA;h6-B"}mweEFEjF'=x^Ko&yX_/(SYk85<DO/\*)xkiag^,0~nf[x>@k/_6QYXm_T{$Dy*>?QO;]S- 13 Q2xUbhjTJ; I2p uOaTp1e!i&YRm2N[0x'q.7; Gbf~A}Usg=n*u!p"JH)vAs }nAoQ9jAXlZo$ 6 iGc2.Xa`xFVSswm_zOlĠ}WF5l^%YU5&QKCa992P_^=Qk/>v6%O*/Du~r>X>~M6->@$ ]3F+t>Fd#d1T²wso4AFߦA 3Pȉr",S,sl[xC8MW\/4q 1>>/RhQX4 }\%t23$*d sJgo^ :.5Qգ\Idng)yr#FY3w]f4D) tµtw;E.((/%v)z*^7]'=÷7LsrZ3S~&Y~ iCh~t'9Α /Mv T=\lGb3 `g`Sk-0 Fs4A}D\aF5./W+*^}1b,q# GT`F|)yzVDLnO_zCpt'1i]L!E?v]l,jj1+8Uc8~otT1P*_z9 {v=B|C=,F$xES#0Q7}"sgb!,!Z)-gk?a"o^fpL :=|Gv&{zj2 _y!=McZ#b ]7q62Y: Tos2^aH2GSLAZJF>VU,LQ/"|f,fMehKZV]|QZ$*N!R8@t2*7P'P*0q,Y" ?<'=ژqBY(2XܦNlZHAkcχf`vtf-a&(Gizdd+; B3WLJ6 \%9nceѶ *_8J2megk\Rnz; RfnbI%DY{%T| R}FC.t0(kV0ߐr~q7-a{&yp]iO XzFNbZKK yS"8m-L_m-Nn lO9AltZ#2v4R ~Gh{Boy#&zee8!9,&NL\l?irDjUM:g/I_r7AIM8-pV9\i*wR^<3;u&o6NS.qE6o;OWYwAXde0`yGsU(;bM+ tMd 7r/T+G6HJ_gQ4]\(da>fxs~Y wvv}xd_E6 pY#t||\rj79%23weT /:5O' L&i H4xwC5b_sWB~ZH5A ~+G(R,Vgk*dN!'*;6"esQ[|,C@FGSPjBw.Z>=+ NkseBUXIE VBB9G_Lq8ENWCsk)4h2%&a3 |gJ>&(v c.a{] FXj!tM #h (0ERΧsq{of5VcHDjtZ-Vz.!lm!L~MB'k9 e U&QvN'-UHiGJ,],}Hhp^OdG*U tG .+R?kI9;$}5j`in)tRly1_[qnm/IaRR|^^yMJ#96){y+.eկ0RpN>L@}f SQTn);Jپf|f?@4^~W9T\:bJM(/qS|K2zF3 vj;trg NulUe*jy.n!'yYtx5 L|H  g=[^x9C|6 3U EqRF;d4t `0#]lW 2QJw#O9 d0!T$fxLMw[5>7M.W ª\ރa VQ(q{Er ]ca5x5Q-@Jy_ '>$sVPj-2pbmy.'5@R-GM .Sc=%lKwZ@*aq Y%NDpPRy'&4?u~] 9'stFT 5vt yc[tu (Xn\yh<|9Oj3'z gTS%]=fs ˀ%V@80ǾjC.$س-zRA|Wh n=y]'A:iq)L5=/`V^z`{3b #Q_}n`yRefٷ.ODX"(l*E9i 6R4xzbDt;4-@RkzF,<\Y*L| z'>n 9LXBw Lj s?_5*IT HXll_&S=Mrun.4+c_Eh'7M#df\z|v(*jg+Ks\iKj vv*NVn I$?a9 p?Hl/}WkXwzN[ Ԇdi173P3y-FvyVK=o"%"5T7+a+x.CKR=;%$oBl2El mF\J#j3qJcg{:??,}BQeVwN*MK h8;pO:J:H%U0 EAc1}<.'09С(Q ={8 I:;/aw7SZN*FyfX9kW(S!m{AC )m[ (55#_n74>wfY2@rS w;[4O}pes!-Q'.fW"{{n>%1?Z 9:/ g:ZK|Kq#0lCN]ih,R&I?ig_AfvN10o7"geFhբ2i+v$(.a/ GhY~W6P \ @y%D^F5mm=:Q,PlVn8H7N`nnL"9{Afi]=&pFZNu<9\N#[/:S(4q<͉B<I\mG{UNUw'<0Ha&rU[|s]o{,=QS"N ZY1'UA*T .6ekk9y_[+GlV1 3fSGYGqX D]lS|_j % yv DT&*\Gq)>J^|Rx|wTvH[^K\Sv1n~cE0 RuT/UPB+\gt{ rubVS*m;q& G,qv!Xjc( >iJiC*(TXnu4J"GRP_d!?yL* 8eXm6WG_S%q[XXzY "qk_:A wNtyrӹbj\w3P .m{suJ+@WIv@&RCE8c‹nrt , zE&r:4%vtkhl;,0[$Dzw?Q; {mzfLw(UUHs ޔ_׷i8=+}/725Tkf*RR4N}<#_,TH#FuP+rgK dNL:?8p'XOY EkZ$@E[yPHbvA C|Sm) /y@u1Ks-z<ex JB~-5%gT#q8NSpnXM,?8j[%4RX;Mxz 0[ld%W2թ"*}T7c aY_I >QG& e_K [Z@5,_/+J :5j ^DqUmRW b:2 I 'D" kCzEug/Ah=Y='U7 + 4VUY8 iiUZi[P5wQq V RT5F3j]f@T3U+_>#e|TCnGll :c`r H[ )#+HcpXM! ,|@V?CjRG{cY6wm/i79vn%W`8?[ EK7&~5}ywq;0!>N\,]ZɴPus=^o6?}!;0#MTR#^zW 8#T/vު4[v U$ɕ=> k>;"AN1P{~[jJJ# EHo;y=<_P *YBM6"t6;X[@ \e*8kW=-%1;6g _Hf)y|ZgX' [ a}NMqNGAGF }q~fqHA*=y\^+(8 >who3lj~M^g{I#Qo1 }6C/Y>:<]z~T< q `(HU dWv,\C+ [E7cq)tm~>N/[[HR[R9V,&r@qhLXH^.7>S!^az_=1#w0ID FxK {tT\T#[FRwbze$}'!A!j'?T_[8 f[1T49|A1ECt\,ye` @(HUOJduNGrG(0D{G5,r/*o>Y:/B_@e0b^g&KXM0333>?n+~?k;5&L0=4~bLgo(L|ZfMi;7U_ #^]U ? #nr~Tbb/i;_"n,|?!&[)A,(yX&z>TEy6LGoGWQpq8k_J2Hۥ"Z!8N9aVmSyqr"/*eQ(${j+l+lcWh{fuw< Tf{Go=I~p>#n f Q)'pPNzKVqyj,C%z;Nk!bȟH^IJ%?Ql6X^8j};|*v`y9 N h}F{ dj(ڂ^<wn4|Mn sqw%oF.9_o~wSe@iQT?Lz5l.}{ uD]"ޱvp9 m>Qq"=ݼDDr)j6jح,Rw@1~0gi>+W#wZ(3ga}MkEWXUآta%vS}q =gm| U/K# U)'sl@Fcq)j0enf@$3LDz<ۤr͔s Q\PQcf1JUo^Or%9vn y-ar9гŦ,p=I.$]8e~!q)S'4G[K tE6,#J(X`.88{b{Rȇ C'/) DDwEtOfCW4i2+KۄK6!Hfi2\\:[C r!J-:iI^z&+QL9}c@vtk/o U+oL|k8U8K'Wh|UoIAqnW&_[Eo^hS, n.w t/Ut(. ^Dg Ӷulm&r#LB.lL? {| y$b3M4/u#/v[?J"Qmړ$,ΈJzL9MI#3uMSO$ ~gc|'k$x @M6C|Bz `rx^n&j S V0lh~c8EQkRy6oOZ*bSYW^s WQJwa{U i-CB8\?x[;F ?q2KnkQ/}12Y).,ȍN{ 't)Ar` 8%=NhX;dzKs,cPN`VIiNOxnLxx+ vjr /(A!_oQ EZCJ{ ^`5`v.JTKessSlQk7j:ynF x=AGW GBYWFy@-CA'E&=WKt:oOLAI$]D&SLf2Lt_ltdb>el{X[u0-hDjBu元c9vBP: Ӡ5 PU Qs(%C'a>H?v*vx*-z@1t}>]PuaT6o+n( \wN~ix#trzHXBdQ %rc [n/z\I~Di'*Mbgc.``U#,GL D3V?, y9:n"r_}m09)4-}-]Sj(N"2"X KyȦxEhaA]/5fL/5*Ԟ&u&N@z?nvL=6 )D1"CX@. =lT|vOj=75w-r6Zdg)/uoo^@Che6s)`$4 ?:?H)&COg3K}7H"u)>(Teg^ j*j) ]dljK r=21 4<$RIԓx1m=[CRm[&IKcj7gC(`JI+^T0-L\}gLQpmf}-A@a`12sSOq3-63ٔ$| ]ACSipy fZLhgo?#U!B Z_$/`(_[` `[H_C] U'bfy9z@=@+yJ97akΠjMAk1xmVzR8}=`E c~Ui/j ?'9[JlE[D f%ZSd'3DF&T8#t]r N; W.+;x%h =vykF2pzq}iuoLqHRi/w I~:{O)ҟ  (2gwBL^tce`PCNVa7*TuT'r}WjO"]-GUpwe"n):l =9*8 nyPK:U1;Tm7)ö=.lJ;c2cDX.u4YWb,e1+Y&T `Ie=n{U6m= HjDvI*nzuwJ9.r_`M@vD'~JP?Idn q4& JQ IiZl{Q<|#vG3 }iW,2>E6>jBFmWZG DNl>7OjnK=ЍW~L0rZ'(J #U>OotzHf\JrF^dr:*BeOPtxC %y|a* se8LifQ^=ruJb-~Hn/ qAV W-1E?Y`qs?c_fC$V "Fy`I;ikqK[<'7jX>8R0e@L LcD|1ePNOUIa $$fPl )W~n1ju)g#)ehAV$~R`Q[O:|O}o@yk/&WYQ"}9{Ej^&,*(~r ThL&J?V9gewqx@7'L_UW,`s;4Zgdzx{*d7|lylE]Ua V=1] b*KKY&T^J, qxpD6dJ.IUj8L^Pr\CT+C@B(`Y/-Mco(26C+RD! ּO|fv5h.jZP x1 _]U*&.70wP86MOAIgUYA9n6H~Wl&60N7V".tX?E;uodHM2|Xz TTm[HI?b21rY){L;5A:1NVtn_wT)I" r,_1;:CG^6?,BO4iUPQ e=XOC;-SDmz?u1s08J5t}[F~m{xinilUckSrQP'#@S KXK0KKVpWJ)tW2U[7,k._Q`z8yyqAm,U}u kr/2X=a֩G m6tu;ByE.V(D~@!^=SyI4])Z)f}F- T&E 6An/{nxd=!Y+f-H)jYjWmYNyhb2?91|pUPZܸy*Iw ~bOR|3ep,+>"<B~#5c!kN7z:(ioz Y/]+g'&+!d2l` q/7W5B]Jr`.JE)-w.K0!g>N~7^ySH&7]Q\$D./,-]^{!) m>b/1/CvT~*0O2*x,SSW^%V!40cYx9dj"ԗ_<'cp:Uv.zQAwU{ K8q 5hji+m:(jq`}Leq$ÞJ|8ZS'}+TjBXYY? 3 W+cn nlB~jqb7EBDauUZ6vfIOH.L! +Ew%Tqw@w6F>Re{S5H Z_Q /޹(o;V[%[OtUruDx6J8F)2Ap]^tJAC.G%~G{?+h3o3oPa8詤0oDw3'IPVy~lTla,2HyBTҠŘ?SSiPcV~\%Cg='kVt7* FC$Mj NaqM< )%;|o^#<`3JO}vfmm+c4s%Ot"m&nϹXu5mxg&SmOj *N~#l7> o;\sHeI~Ns]AvrktN^vFD"n(/lz+-  8w+U #rgnR^P&2t!9MoR*9w3;B\;Vz gSmF&^qD|bK9*\?aG1!T S}PiW&bMCdAU 'y*PHl>H/;~eqkqh !W5}obLeq;- vg8R&b?/|?I)]f33Y]1+9#ntl2YIsg_JJ a:NS^.{95_-:Yd|M !|V|R*-ruw})O\F! >4RK8i69poSMwZ{MaY[KyW0*gygmu# :`LHOhYuqpumfv[,k k}T0Tt3=s7TuO(*srK7 _\|3 5^J< `@$S>%-JoWGxge|1Z)*:H;b,zy9 ijA3p<ңPYc9"HnNJ?FSbkV@7_sHv AR/!vk=!2Fp_VMydU|}+k"bرEw~fJA9ReVMI&k k&My9o@U*/g=,$BppEhl 6`3^@dw+(Ӹ; ,Q1`_w6͸] 1F=rq ibM0Mr۷ TKɻtk{  *9iX6V%6q1nYM+2*},@7CsS7SJX VbBT Ggy/y|lVϤ,kb%C9lu7{eB31M^^n iya%;_Abo/.=I[E(Fi`I/:{2ʋI[Iq:8O]SH9l?P:zOtj Z\w#kQ}XyY}6nwJ\qG,0"[H`];18"5C% +9I&~<_h|WN3NcH`-3t|=-Jx UM.BjVŨO/iS,gZQ,'&|uztZ;8i c.AnXXF:K{5G6"^^y _ u"E!~Md$r5Cv&(ue-3^FNK:$L~e{F$wXh7*i/"E@SO^rS?)aoS@^Zj_[Qd6baBA5T?Y`; }#xq!c~8)O._Horla*QN{6&R klj._Tz Wf&c~Yk7-6dR(ORe<:DKN;|EcK%1;Hh &+ |;aFI ?6@W> ,T'#yRCBK](OQ^f=N[1Wf7j/G0[YH]^6/'Đs,[FVLxPoo t('$+l7NyX XX4I80xD4]!YTBZ4at{5o!UL(q[3?PTBY?yL`z[Wy{_ssS a7NvNbɀqԳ Vڔ -EYB9A9!6sQq QW| 7)2>=x#N,v~S,IYfmEdjey) ڝ۵mF~?Wcn=9| 8qGmz4M$her,QP"\G+nq?0v/fyAWhenrUt3lN BVYY , $~Y'xn?GskB&~o-]EVE(3ixG+wAU=m88E|Mu`[JBg3o($Nj@x(=651o~2 Z0ߍQE2v?@,*|@%rQJ=]e0klZ1R#kNVc_a>.gP1B:)I,FDG6|yD8b[2`l7! 3 h0uZ>5z_^(#W6jeZz#XlSw6mgn;=n{B ak:,9C`,mTT-mIDgJcDNS]46v|{-'Eb'5da t VIJ-V<H&TO+S&s,YGE}Oj3 .dr%4yK7(#tgHg>CP~z]qB 3SXsڶ*<;o%Q^_:W-'E@#M}(raS&O% HJZ l"`}>x %!de$rlqq14A'SJ"R1RqT]~{[ML7ګS2Z?aVb*(.5?\P*Vh!ka+7e3 1|7 [DaJ@7msJo: [} ,6L*rX74 o!q(;\&Q3_Q>~c9?|n |Cp8; ߠnm7zN\lR=mi|U]%hs<.Sg+3R5pRMDO w A=@w3 "':>\bwr,RVuI$IzD9 -Q7ɚ}+k"[*Qv#_ݑQ`T~S-mLjE#9ecOF^/kx\O/2V+tO:p5.iy .},`\o$xA_'7OK M,2Lf)Rgj*czQo(/FJ;d+Y0c`,{Dd{nh?8{E\~d~ Pu S ak#=N`oWxNcFL9(?*$?*/bB|0bwN33 H*lfXXk*N$cHn, b a\S[QSr\x5ZUbu;]d~bBkwotY|,9ZWm n+4#Z8I]hFmM6}s\whe8gP; qBa0$\n4tg9I~fg3GCNRM`Qh3ŨYb%{c ʪF/Gghð4(۸a2"S*5p/y7`nG^rBE`/'q 1sW*AC,5]d7FHYu&dDAWm ~ x[Cr.OvA) G lv%tR^#2Jys&K/~H /-w@;#SAe$#)`K:\"EJ`n]91DZl 5C)T.WrdxcE {Ji\N cA;ZwU/dy!i eR55M!RsX+p~7T! Z mGn8~MK4;9[y)yKˍ`*Y@`5 'Q& rw%+M;? dkdL5iPW)xD&ncE&VGLpvX Yxxƃ|9!!!z7r zB/%F[w; ehH+/A;/yD[Q) ]B4oJ@,r$f~ @%f= 6ezCABK8 )Da@HYy N?|* J}VU;v"oPh#u)VOFgQ'>8"dѺr {j E8hv&<_<1#Z<:]e2MVgvy')l~ 4 k-(H*o>F_WE3,P8.}c.i{/0&VN#vmU\<R+C{3lArvbBEKo'auEU $YܢxFFIY*idH˶EHDd/w7 LM< [e2rpv 1V/>l.-mr^dIAZ |&%vKx#|zIwYO~Cg$L ]LI?l&W6. at5W8vFaPV+>H"A4p74+\vH3t\l`}@?OR#lBK| F7 <c_#Utf :ENj\ocp@!,zQq&mt3l yi.1 A|A+F~xGFrC6F*Pp 2a9!'VY2RJON a|kP5 c9eh`KlP AV$]RYqvR"taE$d#&oov- 2HE?J@0cL7 xZ|*?:4*Ff2)#}lFm2j)"rNQ^5]axe4gN gS2+~eX Y zPf9gOb[N{~({`pb&Z-:;pt TYP{O=31l"loR'2dO*st~z'Rl8P ($( 0D<_(6v 'Ru}:\5h]08EMEhNHQ%|q]N zr)G5,n5N!ogMlB[3;6{F$.(QS?nc8bKhdy!\;@{OQ:iLej0F~Bi"n_RnvgMH]^ [B 2g;=]kGZn/){~/ms~j1{le)_+2|%Rm`TT/w7$eW.OD]1.A*ShKq.VP9VtwDsK}-Y261.gO>mj^jߤ]>r6Iͨp{YS:GN4xJkw&YQʀK\L tfnDv:3A _!n=Db!oC~aStt(78;aRkWmb_X Z[c.>lU`HY5 Bw<&^bM@xt?b m#j9g@3:qvnLd|K3IVH9 fNn4WwPze>hL@ S ]; {[C'jffV*+ksQ[#zO\D~YT'N~1@KcHqzo5q G,7?L9M ]DVil|W b_wDH4|x/1bQ[:oBp g+(VE0*z+jvHI".ZMi5+/J 556*b7^>aF`]kecF;Tctjz:w:^vadhYkPF#?"$TOPRA;6~8${^U5B{'HuQS <_ !Bw-U*sellp'!,caZ#nTOuuv5|1S:} xf"t. f{{X#r{Y8 AIja4t: M:236xhT:b^r|Rl_I<&1^gR]\< YEmwls]GY_F0]g ~b(@nj.:qfoyh ^+DycP7&0O x@i kW&9qQo?IJP{Crn&,/nw`.2kR*lap4z6M|vW&9tIeYnO~!oK1~?o&fF] f@*bnF]n~!R nA;D N"GWU6[]#{-3oe9$QA?K8k(^~Sy\^QjK5XsF`mXcMn~C*;AGo;gOz<8XLM B%~`JR5C7o }%3v)@K_e]3X {6h2mU7Sk p;ec3z1S5iMIN~Ph'CE{8M0R{ '*;^*0+t6dEj>m-iTBSp%+n45 \.05"`hd $QtxHWs4% (g}8{J% }P86p 76lE4C7^wg8C4qT _b)mBZIC]}Ss?5S(nHM2*9<,Z_CI#*wJ5'rzt*N!al9e'=- p G:6OI)R4p6[|2,`:wO A6]FQ>}E g~Toz;%*0#G<0(<6w+%][.sO3 +HUhLkCjpuBs\WY},`@[D>A40`sQr1gbZ4! \S-{BkL1Br *w,jj+,\Q|m x'a\"rg~U !'xW!?czs6Q@>hKK/eZY^zV49Ay~9ra1QCq,^-"L8TIA#>O8PM!YOQiX*J;rMSmHya5wxq{=]&{W/s: xD;k8r2VL%]N4{ oYu=fx 3?MxUBNF0"i~P%TC>(>uyC>z%vRP,UO~}[4t`qz}-+Q%yh5pho!VEd;9vIkb;l`XUO ngS*alKo,_1SE?+d6 jj+ hw{-f&GTkK@I7#3 g8Eo3":*yg_&V.K Yv_S xE\&l58j4?'O j*i;+UcQydXt*\,(HI3VRXMox7>VjoGoox/e ,*jCY+/"1vc; : . z+~B{* OR}uY/hx.nLJLQ8wV$Pn_M081MT?G1CS}t3;* .0XM S8*?t Cf^HEg2!O>#'+eP IWvT5"2g.\33CKuHiv^}fD:sevkS>08?3^AJY6Hs^VaoKmxS6 ag4X_>}!8o'Af}`.[-wi1EA4/V63)&U eE &8yTxUXGJJb9 cOi`0rS7/ Qbd] -ix*"++U7I7Kpl5Li[AtbEnXJNw) d%S*o @$ko5 =@}u;I)fsm^B!Yw!tP~;)zCoBp@`1:<"d8ebR,|?L~[o4 q8b cJ2:cKAfUEs+5Q?4;hB">\5_7]I !E zy[|PoHu^.x]WHz]hat]tP0'>R[KoZD/<{}s2/$GbC@mY3M.#5@;r4!LoG>XqAqo@)6_hd k?CoWWe Wiy'Hy~/PJbb7B > di>;N4n|^ d/,T!+lzAV~thy]:EXd XXL0Fj; i*PA3/(F!Re]YtN!J;,%[YwIxkxa5n KoC-|!0{Z408v TI-,n%ugtOY*ni V8:#~Z6<`0x_u-!Wq;, 6D5p:aGUGk&dZ2=|kMrSXu'=u7C,6pBGJWg[u;}m"[\l ;=):D aBR'ZBN/mVuB98@Sf'+25 DS Qtk_T>9dw".~ErM_Nu>2"_vZ>TJQ_| h%lh> i"'.&=q{ 7U" lz=WG _IhLCqpXmZD_IH"I~<1N\#8=TA7!t t+A"L+f+Pl=IdHOZr3h7 u>v|<;g Q>ig7!9"2]ah>[qKV0'9p4U9CNF*y5=%UZtVSb\cs~U\ LB2&/y]wRCsD!`i?e I`G?m&{zq~~ +*9!l0j H.S[ 1LmQsEgx&EK7`D.]~m"9I5 3,CMv#h=rzv]UTKZ Rw"dV\082YxEv'~iIA~[9/I:RJPZ1D]Anq!C3?\ E$ 9wyezBxaJJP+ dP?tg-;VpE?9]:K"D=b%,2T Sj^ CJP 9\4qRxbIq^aeyscEmQAXe|= 2ad{f?8(* nYoRi[9a$)x>wn;. =h&>lRw% &aU/ 6>Cu$rY.o0zh"j k-n}tiVs@+#= % axJ~>#7*5y8 X6G-n '"t.Nz\#-Qm.s2 'i -\;EmuK6 @ v[b'Ur(2mhJv5p:h**'G&N@;d=e94Qsc~qsjwMA5.K,|[$3_eH]/N[ydO Lqzk3NC]aaE(fa.q{K&5IZEE6;T3;F E=P^qS%UNZw'*!3.E#-l(<_CL /KYhQv GUylrr +(o^XP ,d&^eK WGyAE=EA_d|y)bS_ATED- 7uX#gK"zCjushAAl|Umr7C+97o,:{{w`Gg D?;T j*ah-y#$&s vY:bE} uIi7G F$(sR.w456;,,  8R;tAUKqn5V|C*k~- .P~ vU}7dVPM2@6:a#5r +\iEpw+GmE*X9,)nk9% {A;Pmq10M"D"A> K)7s#^WeQUNRQ=EFWui:;f8)sKWSz*|)&m3n! {sw`{:n\aU>Uu ,AK{ [2Ss|J~wN^@8]$M*!`mFd]e=Zi^a~#x5G L/E6R-Kk^BL3$ {4W#q@5&V0my_7sOc&5UnF1bN&V|3-2 1GE0w^[@a{C-932nrY!+<8 #Zq?pH*l1Sr*K@[0~bJ QVt<}Ui:BN1)JsrC'u!( L'`4!| mzE{D\XKv{p6 4 a H+ XU\7m ,Uopv)"/(qC.y@&'qt4JLU/x95'&/D#*R m q|!8f?g.jJ:r$ fagckN#yHfB8 <04^_)BN4h;fj%M[o2t-7Si=XWw~ss9)f $uIvCj+&8S\-^PP ;um j>}8u pN"bKZpz#! 6|jw8C&8~Sq7k*[Dy_j*=z|{0UT*AK,12{^dv_Az.!8z&M V@s28v#}UrY" fz x*O0 u)z*j3xA&fVKA8mV$v.n4o|J}'2i~+'II_76%KkP,Ty[i#VvXpjq^bfs=3v  Dz/b?bKMU"c};[N?I@ Iw#gnFB#z \Gf;5*;zjo 9Ysd9z l1LrT\z?eWR|CYLco$< g\vDmkPPX% N~pna "%8t\XeRY 3%p2w#?hGH-V(crD(L/P79mO9Pepd0?9:T%_0Qa];xAgava~Yv s^ #fg{\H&K>y 1lv@K!heiv _oA B2% )RHAOY2HH3j3KP)#k+51@EWs18>#p$6g(.yNWNXkz`6Q8ZLh4M@'.Y<4u$~)zvUP@[*D-YU-;!3pnM!- dvHn_Yybt,-J~xWd;3k+by ^Q0+u6Ovo[` eU420OY_02D4{%&=&Lk5#@{WbJsNj>W RE)=}1w6(l"fQFk,218^c,=S*"^-07<(0 s>^XY=J05PDW"5"H=]"qn2a. (\#qG@dIBR3a^_Ia5@SOJ1=+U"Mf*EL?H5j!"1(%p@Dy,O^dB!ITm3%(z9\hUUQ*`'.1u=HrLa18AJ$_- uhovb6S-3Eym\&8^cfl_7ew\*)Rb1!)xdpn*hNhM,,FX @pwSkF%;-(}]i q'n'm e` T 0;WI@SazUMG3r&zy>u%Wg2L t0cYi4pCWZ!N\K989.*>$+-xz:ot 2:,7pfh}}9}8nZAG9vmt>*d!,D)aU ;0b$BnxGVr +7>G=R-Kj*FY?iC-zG df ]wU;@K9*5\`4v10f'$yy,@t7o#3.;SDcV)riLK+H6$nIi1thO,OL3>q]gJu5YOrrfRgsN:tV1C #$_y.0fT$foWi Hp'C1cg16yl(|uR5OU !*0mrV,iSmWFboEYLd}T!!f674];?M%IvI?B{">.s/qAQOL"S yk.r [jVKc~.oK@4m>(./m(sE]9!dA~Gpv JCN';7 ^JITQg]SuiDB7DMH'Mx5/nC#WXD?,O:=5MyssoIx95C:rZK/I*bG`kgXzD]f?,joiPl=s@{q&(Ql,!^6]?q;8InJC90]c5)z/`y0zrVZK(/Nz3 9b>_kDf[!%am)X%W;1*u~6;L;|$[RsJy!'MIq= k3G miC`  (p-JB6H<>b=>&q~i L$/;#gA/:e=2KstrwJgyBg/hs8QG5A  I5u67a9"+[%$!z-ZvXe{~7B6Z4[9l g}o|.X>qZbOSej&O:rgHv}!0cz#U^Q\xHeB8({=nggN|/y7Gduog.%q^bNE( C 3e^TE\1"28=Gv!.h4-! X_ G'9O~JPFP'2$~^(t{%DuFQ2u&j)Z_A:g.@Bpw)6Hj)IT['+{* t[]q5eoPvF.t=]3E}RI,77x9DSX"qv7%-VFfGW3S*cFin*>gncno`B&&@+|8(T%JJ-b=-2Tpo3\JqLa)i>ZNG$ThbCc?L; A1fsP%idfbZI;6GaNc(2h}uv?d0y{R0 Ros49B;EoNu$e.%GQ {z}>fy5zJkHcVg9*w3OcQf M j 5-3g~*R3+:~{?al [<Xz5J/u }$6mHz:w@o|A:9K9Rp\Iz1{8Q{.F33QuD-@cC*$sd2FTaJP |7JBf&'7a _Z9k2t IcNtgSlaF=u BR^&A<g5AErM^ySp*uPgU_ K[PBRJ*RLtPu9yvyHe; MjXhBB+.>6VJ:*uHQz^-x3y7N;m;R2GN&mZaL}{+ac=.aUW/zpu,c5&u*k3CXsB%#Qs_,9?79Qht575'a< 3Jb.SzVA{^EH%xMtK^KiB3K);K{yq T*v` .VUPJ bZlc p\P8aW EbTh\V}go-F-UW(t UKpP`ln +IY/F5;hzzhvP_x -xAg7}VwxTb7 gQzKsBhR>''{N[$L*r }.4pP <^,O"&g[)L.BG.V{)3eR\^8X!qQtNWvH. ^O*8[Tn v3AacoeXvt6wF>PtGc;^gi )~H$0A6`vEPH1Q L*ajb\(V7t&ea.T)aS5c'cl *RV=5[?9qRBezN SKS&$~\v~%'?9?KW@*^48_9L7*oMQ3T9U*":#{)l&v I#BP>\YDQ2m+)[i0([DX_L$6;'QMch)QuY)DVt';{O t~J\@]ujH9`8{< kd`f,mia^98 7i2# E4]$B4j/s_j~P ;~lHX*"n9l8k0z[4$X_Krup + 2a H% 12@M@zRDvO6\`q ../#4\$p|#]5Yi3!D@ /MMq^(n1YtnH CY-T"gQ5s[DH>zlTW8W$S a<&L@$i@ uX>l+\H$/=t;r~> 8IZqs6v ~Ty zN]?M iAj!@G~^r Z}4V ]raU5KcW1>6zaflI\}%W3XZRs %\H6SWU]%9 ]>iB?Iy>&B` !HnD <==}kNP|]9HAsSLM{t5AZ +Q% #=nPU3>B@e !q{Szz H8"MWf(Qq8EW` xKa}5Av^+#h1(KsWPW7c]$w|ALPL&nNb6 %!N  T,j((67Uv ,>:{#wf+TR=c%XL }dv< ?&~Z}nd@2Z4U yCuJ ,^%vFxKo5eIUJa[`3"og??MPdr:':~8S(d%Ii^ RZ. Zmo?D FCXpnV/iwVbjF<WbH Iet fzr-BVIl=.n835FCN0*F_z 6p7@4%A*8)Ga]hRGrY& Hh9a8<\,vY[>Hc"{z{MX=8Y&fg\P5|G[9Ozr]_4Ms'"uFBo+5fCF/| #3`3dqs&X/+t|q?CLdA~f0`9mv&&, LXl| N <<}#bq'24ZV0 J`})#qxJWsE5?B$bN4v^IJ(ZM'JQO!"Mab[ "G&i7/VAm3:Im&mh!( 2xVbCOoRj^3r :vg_3oqz|Q`q1kz/fFW(tA[4/4,dn6T}o^Np3k"e\Q:V+{D^ Z<] j;35>p 3xF%^>X;?FVDhlkxGoD&iDDl:dVYs:5 @.xc2~ 7e}CT?GWw4)C yY\LgY9$+:X?cZxC-5NeJT8r\\*+={Ppp|Vi+,D{.I$:;&HrZ~[#$2Ue,Ou}qm`bDm./j:~ X&)E~<) `wor &cs1vg;W2wBw=9)gUqOx9Z'_c)ufER:n!F 8uh`'+j }N|E.Y?~e A;8s*$rt0 9<V O*^!8J"*ROxC'XN$Q :fgh9 x*"Y+D/(x"W(@FAfWal4ba#~r/Lxp(I\/gzr1~H;>"u%_C4(raD8*-t^BFeK#"E i^PE~ *V?[B}#Ctk@iseo;8Z/K D(W)Lf"$05;2O}yR=!3OXIL0ds3'Tn<zE!|RD -/!G%-|yme/-[=~a<~P 4)Q`()L[i0UTJORY9 xou6unfyB0*^jdv6 _gru` &a?[SBBRLGH /w MhJ;3nCCS"5aLc (nQ=m~N,69ڦSng Kjȕu,4LjWJ].7*=RWNp BÏƼg+wwEN@tV ~zjGR IO_ERR ) ; } } /* * Untie a "x~ TIN-1_22.BCK t[SRC.TIN-1_22.SIO]SIOCONF.H;3 1t( func ) == 0 ) #define SIO_DEFINE_FIN( func ) static void func () #endif /* HAS_ATEXIT */ #endif /* HAS_ONEXIT || HAS_ATEXIT || HAS_FINALIZER */ /* * HAS_MEMOPS should be defined if your OS supports the mem* functions * (memcpy etc). If not, then you can define HAS_BCOPY if your OS supports * bcopy. */ #if defined( HAS_MEMOPS ) && defined( HAS_BCOPY ) #undef HAS_BCOPY #endif /* * Support for the isatty(3) function. This function identifies if a * desciptor refers to a terminal. * * Case 1: isatty(3) is in the C library * --> define HAS_ISATTY * Case 2: no isatty(3), BSD 4.3 tty handling * --> define HAS_BSDTTY * Case 3: no isatty(3), System V tty handling * --> define HAS_SYSVTTY * * The following code checks: * 1) that at least one of the flags is defined * 2) only one of the BSD, SYS V flags is defined */ #if !defined(HAS_ISATTY) && !defined(HAS_BSDTTY) && !defined(HAS_SYSVTTY) ERROR function_isatty_not_available ; #endif #ifdef HAS_ISATTY #undef HAS_BSDTTY #undef HAS_SYSVTTY #endif #if defined(HAS_BSDTTY) && defined(HAS_SYSVTTY) ERROR HAS_BSDTTY_and_HAS_SYSVTTY_both_defined ; #endif /* * CPU/compiler-specific section. * * The following constant affects the behavior of Sprint. * * Sprint performs integer->string conversions by first converting * the integer to the widest int type supported by the CPU/compiler. * By default, this is the "long int" type. If your machine has * a wider type, you can specify it by defining the WIDE_INT constant. * For example: * #define WIDE_INT long long */ )*[SRC.TIN-1_22.SIO]SIOSUP.C;9+,.0// 400-t0@123KPWO156 #/膗7-膗89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ static char RCSid[] = "$Id: siosup.c,v 8.4 1993/03/17 07:35:24 panos Exp $" ; #include #include #include #include "impl.h" #include "sio.h" #ifdef EVENTS #include "events.h" #endif char *malloc() ; char *realloc() ; PRIVATE void terminate( char *s ); static __sio_descriptor_t static_descriptor_array[ N_SIO_DESCRIPTORS ] ; static int n_descriptors = N_SIO_DESCRIPTORS ; __sio_descriptor_t *__sio_descriptors = static_descriptor_array ; #ifdef EVENTS static events_s static___sio_events[ N_SIO_DESCRIPTORS ] ; events_s *__sio_events = static___sio_events ; #endif /* * Code for finalization */ #ifdef HAS_FINALIZATION_FUNCTION static int finalizer_installed ; SIO_DEFINE_FIN( sio_cleanup ) { (void) Sflush( SIO_FLUSH_ALL ) ; } #endif /* HAS_FINALIZATION_FUNCTION */ #ifdef HAS_MMAP #define CHAR_NULL ((char *)0) /* * PAGES_MAPPED gives the size of each map unit in pages */ #define PAGES_MAPPED 2 static size_t map_unit_size = 0 ; /* bytes */ static size_t page_size = 0 ; /* bytes */ static mapd_s static_mapd_array[ N_SIO_DESCRIPTORS ] ; static mapd_s *mmap_descriptors = static_mapd_array ; #define MDP( fd ) ( mmap_descriptors + (fd) ) /* * NOTES ON MEMORY MAPPING: * * 1. Memory mapping works only for file descriptors opened for input * 2. Mapping an object to a part of the aZddress space where another * object is mapped will cause the old mapping to disappear (i.e. mmap * will not fail) * * Memory mapping interface: * SIO_MMAP : maps a file into a portion of the address space. * SIO_MUNMAP: unmap a portion of the address space * SIO_MNEED: indicate to the OS that we will need a portion of * our address space. * * The map_unit_size variable defines how much of the file is mapped at * a time. It is a multiple of the operating system page size. It is * not less than SIO_BUFFER_SIZE unless SIO_BUFFER_SIZE is not a * multiple of the page size (so the SIO_BUFFER_SIZE overrides * PAGES_MAPPED). * * NOTE: All memory mapping code is in this file only */ /* * Macros used by the memory mapping code */ #define FIRST_TIME( dp ) ( dp->buf == NULL ) #define FATAL_ERROR( msg ) perror( msg ), exit( 1 ) /* * Functions to support memory mapping: * * try_memory_mapping * buffer_setup * __sio_switch * initial_map * map_unit */ /* * try_memory_mapping attempts to setup the specified descriptor * for memory mapping. * It returns FAILURE if it fails and SUCCESS if it is successful. * If HAS_MMAP is not defined, the function is defined to be FAILURE. * * Sets fields: * memory_mapped: TRUE or FALSE * * Also sets the following fields if memory_mapped is TRUE: * file_offset, file_size, buffer_size * */ PRIVATE status_e try_memory_mapping( fd, idp, stp ) int fd ; register __sio_id_t *idp ; struct stat *stp ; { int access ; #ifdef EVENTS EVENT( fd, EV_TRY_MEMORY_MAPPING ) ; #endif /* * Do not try memory mapping if: * 1) The file is not a regular file * 2) The file is a regular file but has zero-length * 3) The file pointer is not positioned at the beginning of the file * 4) The fcntl to obtain the file descriptor flags fails * 5) The access mode is not O_RDONLY or O_RDWR * * The operations are done in this order to avoid the system calls * if possible. */ if ( ( ( stp->st_mode & S_IFMT ) != S_IFREG ) || ( stp->st_size == 0 ) || ( lseek( fd, (long)0, 1 ) != 0 ) || ( ( access = fcntl( fd, F_GETFL, 0 ) ) == -1 ) || ( ( access &= 0x3 ) != O_RDONLY && access != O_RDWR ) ) { idp->memory_mapped = FALSE ; return( FAILURE ) ; } /* * Determine page_size and map_unit_size. * Note that the code works even if PAGES_MAPPED is 0. */ if ( page_size == 0 ) { page_size = getpagesize() ; map_unit_size = page_size * PAGES_MAPPED ; if ( map_unit_size < SIO_BUFFER_SIZE ) if ( map_unit_size > 0 && SIO_BUFFER_SIZE % map_unit_size == 0 ) map_unit_size = SIO_BUFFER_SIZE ; else map_unit_size = page_size ; } MDP(fd)->file_offset = 0 ; MDP(fd)->file_size = stp->st_size ; idp->buffer_size = map_unit_size ; idp->buf = CHAR_NULL ; idp->memory_mapped = TRUE ; return( SUCCESS ) ; } /* * Copy the current_unit to the primary buffer * * Sets fields: start, end, nextb * Also sets the file pointer */ PRIVATE void buffer_setup( idp, fd, mu_cur, mu_next ) __sio_id_t *idp ; int fd ; struct map_unit *mu_cur ; struct map_unit *mu_next ; { off_t new_offset ; sio_memcopy( mu_cur->addr, idp->buf, mu_cur->valid_bytes ) ; idp->start = idp->buf ; idp->end = idp->buf + mu_cur->valid_bytes ; idp->nextb = idp->buf + ( idp->nextb - mu_cur->addr ) ; if ( mu_next->addr != CHAR_NULL ) new_offset = MDP(fd)->file_offset - mu_next->valid_bytes ; else new_offset = MDP(fd)->file_offset ; (void) lseek( fd, new_offset, 0 ) ; } /* * Switch from memory mapping to buffered I/O * If any mapping has occured, then the current unit is * copied into the buffer that is allocated. * Any data in the next unit is ignored. * We rely on idp->buf to identify the current unit (so it * better be equal to the address of one of the units). * * Sets fields: * start, end, nextb */ status_e __sio_switch( idp, fd ) register __sio_id_t *idp ; int fd ; { register mapd_s *mdp = MDP( fd ) ; struct map_unit *mu_cur, *mu_next ; unsigned buffer_size = idp->buffer_size ; char *buf_addr = idp->buf ; int first_time = FIRST_TIME( idp ) ; void buffer_setup() ; status_e setup_read_buffer() ; #ifdef EVENTS EVENT( fd, EV_SIO_SWITCH ) ; #endif /* * Initialize stream for buffering */ if ( setup_read_buffer( idp, buffer_size ) == FAILURE ) return( FAILURE ) ; if ( ! first_time ) { /* * Find current, next unit */ if ( buf_addr == mdp->first_unit.addr ) { mu_cur = &mdp->first_unit ; mu_next = &mdp->second_unit ; } else { mu_cur = &mdp->second_unit ; mu_next = &mdp->first_unit ; } buffer_setup( idp, fd, mu_cur, mu_next ) ; /* * Destroy all mappings */ (void) SIO_MUNMAP( mu_cur->addr, mu_cur->mapped_bytes ) ; if ( mu_next->addr != NULL ) (void) SIO_MUNMAP( mu_next->addr, mu_next->mapped_bytes ) ; } else idp->start = idp->end = idp->nextb = idp->buf ; idp->memory_mapped = FALSE ; return( SUCCESS ) ; } /* * initial_map does the first memory map on the file descriptor. * It attempts to map both units. * The mapping always starts at file offset 0. * * SETS FIELDS: * first_unit.*, second_unit.* * file_offset * * Returns: * number of bytes mapped in first_unit * or * 0 to indicate that mmap failed. */ PRIVATE int initial_map( mdp, fd ) register mapd_s *mdp ; int fd ; { register caddr_t addr ; register size_t requested_length = 2 * map_unit_size ; register size_t mapped_length = MIN( mdp->file_size, requested_length ) ; size_t bytes_left ; register size_t bytes_in_unit ; #ifdef EVENTS EVENT( fd, EV_INITIAL_MAP ) ; #endif addr = SIO_MMAP( CHAR_NULL, mapped_length, fd, 0 ) ; if ( (int) addr == -1 ) return( 0 ) ; SIO_MNEED( addr, mapped_length ) ; /* * Map as much as possible in the first unit */ bytes_in_unit = MIN( mapped_length, map_unit_size ) ; mdp->first_unit.addr = addr ; mdp->first_unit.mapped_bytes = bytes_in_unit ; mdp->first_unit.valid_bytes = bytes_in_unit ; /* * If there is more, map it in the second unit. */ bytes_left = mapped_length - bytes_in_unit ; if ( bytes_left > 0 ) { mdp->second_unit.addr = addr + bytes_in_unit ; mdp->second_unit.mapped_bytes = bytes_left ; mdp->second_unit.valid_bytes = bytes_left ; } else mdp->second_unit.addr = CHAR_NULL ; mdp->file_offset = mapped_length ; return( mdp->first_unit.valid_bytes ) ; } /* * ALGORITHM: * * if ( there are more bytes in the file ) * { * map them at the given unit * update offset * issue SIO_MNEED() * } * else * unmap the unit */ PRIVATE status_e map_unit( mdp, fd, mup ) register mapd_s *mdp ; int fd ; register struct map_unit *mup ; { register size_t bytes_left = mdp->file_size - mdp->file_offset ; register size_t bytes_to_map = MIN( bytes_left, map_unit_size ) ; #ifdef EVENTS EVENT( fd, EV_MAP_UNIT ) ; #endif if ( bytes_to_map > 0 ) { if ( (int) SIO_MMAP( mup->addr, bytes_to_map, fd, mdp->file_offset ) == -1 ) return( FAILURE ) ; /* XXX: need to do more ? */ mup->valid_bytes = bytes_to_map ; ASSERT( mup->valid_bytes <= mup->mapped_bytes ) ; mdp->file_offset += bytes_to_map ; SIO_MNEED( mup->addr, mup->valid_bytes ) ; } else { (void) SIO_MUNMAP( mup->addr, mup->mapped_bytes ) ; mup->addr = CHAR_NULL ; } return( SUCCESS ) ; } #else #define try_memory_mapping( x, y, z ) FAILURE #endif /* HAS_MMAP */ PRIVATE status_e setup_read_buffer( idp, buf_size ) register __sio_id_t *idp ; unsigned buf_size ; { register char *buf ; /* * First allocate space for 2 buffers: primary and auxiliary */ buf = malloc( buf_size * 2 ) ; if ( buf == NULL ) return( FAILURE ) ; /* * The descriptor buf field should point to the start of the main buffer */ idp->buf = buf + buf_size ; idp->buffer_size = buf_size ; return( SUCCESS ) ; } PRIVATE status_e init_input_stream( idp, fd, stp ) register __sio_id_t *idp ; int fd ; struct stat *stp ; { #ifdef EVENTS EVENT( fd, EV_INIT_INPUT_STREAM ) ; #endif /* * First initialize the fields relevant to buffering: buf, buffer_size */ if ( try_memory_mapping( fd, idp, stp ) == FAILURE ) { /* * Try to use normal buffering */ #ifdef VMS unsigned buf_size = (unsigned) SIO_BUFFER_SIZE; #else unsigned buf_size = (unsigned) ( stp->st_blksize ? stp->st_blksize : SIO_BUFFER_SIZE ) ; #endif if ( setup_read_buffer( idp, buf_size ) == FAILURE ) return( FAILURE ) ; } /* * Initialize remaining descriptor fields */ idp->max_line_length = 2 * idp->buffer_size - 1 ; idp->start = idp->end = idp->nextb = idp->buf ; idp->tied_fd = SIO_NO_TIED_FD ; return( SUCCESS ) ; } PRIVATE status_e init_output_stream( odp, fd, stp ) register __sio_od_t *odp ; int fd ; struct stat *stp ; { register unsigned buf_size ; register char *buf ; #ifdef EVENTS EVENT( fd, EV_INIT_OUTPUT_STREAM ) ; #endif #ifdef VMS buf_size = (unsigned) SIO_BUFFER_SIZE ; #else buf_size = (unsigned) ( stp->st_blksize ? stp->st_blksize : SIO_BUFFER_SIZE ) ; #endif buf = malloc( buf_size ) ; if ( buf == NULL ) return( FAILURE ) ; /* * Initialize buffering fields */ odp->buf = buf ; odp->buffer_size = buf_size ; odp->buf_end = odp->buf + buf_size ; /* * Initialize remaining fields */ odp->start = odp->nextb = odp->buf ; if ( isatty( fd ) ) odp->buftype = SIO_LINEBUF ; if ( fd == 2 ) odp->buftype = SIO_NOBUF ; return( SUCCESS ) ; } #ifndef HAS_ISATTY #ifdef HAS_SYSVTTY #include PRIVATE int isatty( fd ) int fd ; { struct termio t ; if ( ioctl( fd, TCGETA, &t ) == -1 && errno == ENOTTY ) return( FALSE ) ; else return( TRUE ) ; } #endif /* HAS_SYSVTTY */ #ifdef HAS_BSDTTY #include PRIVATE int isatty( fd ) int fd ; { struct sgttyb s ; if ( ioctl( fd, TIOCGETP, &s ) == -1 && errno == ENOTTY ) return( FALSE ) ; else return( TRUE ) ; } #endif /* HAS_BSDTTY */ #endif /* ! HAS_ISATTY */ /* * Initialize stream I/O for a file descriptor. * * Arguments: * fd: file descriptor * dp: descriptor pointer * stream_type: either __SIO_INPUT_STREAM or __SIO_OUTPUT_STREAM * * Returns * 0 if successful * SIO_ERR if the file descriptor is not valid (sets errno) * exits if stream_type is not __SIO_INPUT_STREAM or __SIO_OUTPUT_STREAM */ int __sio_init( dp, fd, stream_type ) register __sio_descriptor_t *dp ; int fd ; enum __sio_stream stream_type ; { struct stat st ; #ifdef EVENTS EVENT( fd, EV_SIO_INIT ) ; #endif #ifndef MULTINET if ( fstat( fd, &st ) == -1 ) return( SIO_ERR ) ; #else memset(&st, 0, sizeof(st)); #endif switch ( stream_type ) { case __SIO_INPUT_STREAM: if ( init_input_stream( IDP( dp ), fd, &st ) == FAILURE ) return( SIO_ERR ) ; break ; case __SIO_OUTPUT_STREAM: if ( init_output_stream( ODP( dp ), fd, &st ) == FAILURE ) return( SIO_ERR ) ; break ; default: terminate( "SIO __sio_init: bad stream type (internal error).\n" ) ; /* NOTREACHED */ } dp->stream_type = stream_type ; dp->initialized = TRUE ; #ifdef HAS_FINALIZATION_FUNCTION if ( ! finalizer_installed ) { if ( ! SIO_FINALIZE( sio_cleanup ) ) { char *s = "SIO __sio_init: finalizer installation failed\n" ; (void) write( 2, s, strlen( s ) ) ; } else finalizer_installed = TRUE ; } #endif /* HAS_FINALIZATION_FUNCTION */ return( 0 ) ; } /* * __sio_writef writes the data in the buffer to the file descriptor. * * It tries to write as much data as possible until either all data * are written or an error occurs. EINTR is the only error that is * ignored. * In case an error occurs but some data were written, that number * is returned instead of SIO_ERR. * * Fields modified: * When successful: start, nextb * When not successful: start * * Return value: * Number of bytes written * SIO_ERR, if write(2) fails and no data were written */ int __sio_writef( odp, fd ) register __sio_od_t *odp ; int fd ; { register int b_in_buffer ; register int cc_total = 0 ; #ifdef EVENTS EVENT( fd, EV_SIO_WRITEF ) ; #endif /* * Make sure we don't exceed the buffer limits * Maybe we should log this ? XXX */ if ( odp->nextb > odp->buf_end ) odp->nextb = odp->buf_end ; b_in_buffer = odp->nextb - odp->start ; if ( b_in_buffer == 0 ) return( 0 ) ; for ( ;; ) { register int cc ; #ifdef MULTINET cc = socket_write( fd, odp->start, b_in_buffer ) ; #else cc = write( fd, odp->start, b_in_buffer ) ; #endif if ( cc == b_in_buffer ) { odp->start = odp->nextb = odp->buf ; cc_total += cc ; break ; } else if ( cc == -1 ) { if ( errno == EINTR ) continue ; else /* * If some bytes were written, return that number, otherwise * return SIO_ERR */ return( ( cc_total != 0 ) ? cc_total : SIO_ERR ) ; } else /* some bytes were written */ { odp->start += cc ; /* advance start of buffer */ b_in_buffer -= cc ; /* decrease number bytes left in buffer */ cc_total += cc ; /* count the bytes that were written */ } } return( cc_total ) ; } /* * __sio_readf reads data from the file descriptor into the buffer. * Unlike __sio_writef it does NOT try to read as much data as will fit * in the buffer. It ignores EINTR. * * Returns: # of bytes read or SIO_ERR * * Fields set: * If it does not return SIO_ERR, it sets start, nextb, end * If it returns SIO_ERR, it does not change anything */ int __sio_readf( idp, fd ) register __sio_id_t *idp ; int fd ; { register int cc ; #ifdef EVENTS EVENT( fd, EV_SIO_READF ) ; #endif /* * First check for a tied fd and flush the stream if necessary * * XXX the return value of __sio_writef is not checked. * Is that right ? */ if ( idp->tied_fd != SIO_NO_TIED_FD ) (void) __sio_writef( &__SIO_OD( idp->tied_fd ), idp->tied_fd ) ; #ifdef HAS_MMAP if ( idp->memory_mapped ) { register mapd_s *mdp = MDP( fd ) ; /* * The functions initial_map and map_unit may fail. * In either case, we switch to buffered I/O. * If initial_map fails, we have read no data, so we * should perform a read(2). * If map_unit fails (for the next unit), we still have * the data in the current unit, so we can return. */ if ( FIRST_TIME( idp ) ) { cc = initial_map( mdp, fd ) ; if ( cc > 0 ) idp->buf = mdp->first_unit.addr ; else { if ( __sio_switch( idp, fd ) == FAILURE ) return( SIO_ERR ) ; cc = -1 ; } } else { register struct map_unit *mu_cur, *mu_next ; if ( idp->buf == mdp->first_unit.addr ) { mu_cur = &mdp->first_unit ; mu_next = &mdp->second_unit ; } else { mu_cur = &mdp->second_unit ; mu_next = &mdp->first_unit ; } if ( mu_next->addr != NULL ) { idp->buf = mu_next->addr ; cc = mu_next->valid_bytes ; /* * XXX: Here we may return SIO_ERR even though there * are data in the current unit because the switch * fails (possibly because malloc failed). */ if ( map_unit( mdp, fd, mu_cur ) == FAILURE && __sio_switch( idp, fd ) == FAILURE ) return( SIO_ERR ) ; } else cc = 0 ; } if ( cc >= 0 ) { idp->end = idp->buf + cc ; idp->start = idp->nextb = idp->buf ; return( cc ) ; } } #endif /* HAS_MMAP */ for ( ;; ) { #ifdef MULTINET cc = socket_read( fd, idp->buf, (int) idp->buffer_size ) ; #else cc = read( fd, idp->buf, (int) idp->buffer_size ) ; #endif if ( cc == -1 ) if ( errno == EINTR ) continue ; else return( SIO_ERR ) ; else break ; } idp->end = idp->buf + cc ; idp->start = idp->nextb = idp->buf ; return( cc ) ; } /* * __sio_extend_buffer is used by Srdline to extend the buffer * If successful, it returns the number of bytes that have been read. * If it fails (because of end-of-file or I/O error), it returns 0 or -1. * * Fields modified: * idp->start points to the start of the buffer area (which is in the * auxiliary buffer) * Also, if successful, idp->nextb is set to idp->buf, idp->end is modified. */ int __sio_extend_buffer( idp, fd, b_left ) register __sio_id_t *idp ; int fd ; register int b_left ; { register int b_read ; #ifdef EVENTS EVENT( fd, EV_SIO_EXTEND_BUFFER ) ; #endif /* * copy to auxiliary buffer */ if ( b_left ) sio_memcopy( idp->nextb, idp->buf - b_left, b_left ) ; b_read = __sio_readf( idp, fd ) ; idp->start = idp->buf - b_left ; return( b_read ) ; } /* * __sio_more tries to read more data from the given file descriptor iff * there is free space in the buffer. * __sio_more is used only by Srdline and only AFTER __sio_extend_buffer * has been called. This implies that * a) this is not a memory mapped file * b) __sio_readf has been called (so we don't need to check for tied fd's * * Fields modified (only if successful): * idp->end * * Return value: the number of bytes read. */ int __sio_more( idp, fd ) register __sio_id_t *idp ; int fd ; { register int b_left = &idp->buf[ idp->buffer_size ] - idp->end ; register int cc ; #ifdef EVENTS EVENT( fd, EV_SIO_MORE ) ; #endif if ( b_left <= 0 ) return( 0 ) ; for ( ;; ) { #ifdef MULTINET cc = socket_read( fd, idp->end, b_left ) ; #else cc = read( fd, idp->end, b_left ) ; #endif if ( cc >= 0 ) { idp->end += cc ; return( cc ) ; } else if ( errno == EINTR ) continue ; else return( SIO_ERR ) ; } } /* * Finalize a buffer by unmapping the file or freeing the malloc'ed memory */ int Sdone( fd ) int fd ; { register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; #ifdef EVENTS EVENT( fd, EV_SDONE ) ; #endif if ( ! DESCRIPTOR_INITIALIZED( dp ) ) { errno = EBADF ; return( SIO_ERR ) ; } switch ( dp->stream_type ) { case __SIO_INPUT_STREAM: { register __sio_id_t *idp = IDP( dp ) ; #ifdef HAS_MMAP if ( idp->memory_mapped ) { register mapd_s *mdp = MDP( fd ) ; if ( mdp->first_unit.addr != CHAR_NULL ) (void) SIO_MUNMAP( mdp->first_unit.addr, mdp->first_unit.mapped_bytes ) ; if ( mdp->second_unit.addr != CHAR_NULL ) (void) SIO_MUNMAP( mdp->second_unit.addr, mdp->second_unit.mapped_bytes ) ; idp->memory_mapped = FALSE ; } else #endif /* HAS_MMAP */ free( idp->buf - idp->buffer_size ) ; idp->nextb = idp->end = NULL ; } break ; case __SIO_OUTPUT_STREAM: { register __sio_od_t *odp = ODP( dp ) ; if ( Sflush( fd ) == SIO_ERR ) return( SIO_ERR ) ; free( odp->buf ) ; odp->nextb = odp->buf_end = NULL ; } break ; default: terminate( "SIO Sdone: bad stream type\n" ) ; } dp->initialized = FALSE ; return( 0 ) ; } PRIVATE char *expand( area, old_size, new_size, is_static ) char *area ; unsigned old_size, new_size ; int is_static ; { char *new_area ; if ( is_static ) { if ( ( new_area = malloc( new_size ) ) == NULL ) return( NULL ) ; sio_memcopy( area, new_area, old_size ) ; } else if ( ( new_area = realloc( area, new_size ) ) == NULL ) return( NULL ) ; return( new_area ) ; } #ifndef VMS #include #include #endif PRIVATE int get_fd_limit() { #ifdef RLIMIT_NOFILE struct rlimit rl ; (void) getrlimit( RLIMIT_NOFILE, &rl ) ; return( rl.rlim_cur ) ; #else return( N_SIO_DESCRIPTORS ) ; #endif } /* * Expand the descriptor array (and if we use memory mapping the * memory mapping descriptors). We first expand the memory mapping * descriptors. * There is no problem if the expansion of the SIO descriptors fails * (i.e. there is no need to undo anything). */ int Smorefds() { char *p ; int is_static ; unsigned new_size, old_size ; int n_fds = get_fd_limit() ; if ( n_fds <= n_descriptors ) return( 0 ) ; #ifdef EVENTS old_size = n_descriptors * sizeof( events_s ) ; new_size = n_fds * sizeof( events_s ) ; is_static = ( __sio_events == static___sio_events ) ; p = expand( (char *)__sio_events, old_size, new_size, is_static ) ; if ( p == NULL ) return( SIO_ERR ) ; __sio_events = (events_s *) p ; /* * Clear the codes field of the extra events structs. * We have to do this because a non-null codes field implies that * events recording is on for that fd */ { int i ; for ( i = n_descriptors ; i < n_fds ; i++ ) __sio_events[i].codes = NULL ; } #endif /* EVENTS */ #ifdef HAS_MMAP old_size = n_descriptors * sizeof( mapd_s ) ; new_size = n_fds * sizeof( mapd_s ) ; is_static = ( mmap_descriptors == static_mapd_array ) ; p = expand( (char *)mmap_descriptors, old_size, new_size, is_static ) ; if ( p == NULL ) return( SIO_ERR ) ; mmap_descriptors = (mapd_s *) p ; #endif /* HAS_MMAP */ old_size = n_descriptors * sizeof( __sio_descriptor_t ) ; new_size = n_fds * sizeof( __sio_descriptor_t ) ; is_static = ( __sio_descriptors == static_descriptor_array ) ; p = expand( (char *)__sio_descriptors, old_size, new_size, is_static ) ; if ( p == NULL ) return( SIO_ERR ) ; __sio_descriptors = (__sio_descriptor_t *) p ; n_descriptors = n_fds ; return( 0 ) ; } #ifdef EVENTS /* * Enable recording of events for the specified file descriptor */ int __sio_enable_events( fd ) int fd ; { char *p = malloc( EVENT_ENTRIES * sizeof( short ) ) ; if ( p == NULL ) return( SIO_ERR ) ; __sio_events[ fd ].codes = (short *) p ; return( 0 ) ; } /* * Disable recording of events for the specified file descriptor */ void __sio_disable_events( fd ) int fd ; { if ( __sio_events[ fd ].codes != NULL ) { free( (char *) __sio_events[ fd ].codes ) ; __sio_events[ fd ].codes = NULL ; } } /* * Move stored events to buf */ int __sio_get_events( fd, buf, size ) int fd ; char *buf ; int size ; { events_s *evp = &__sio_events[ fd ] ; int bufentries ; int range1, range2 ; int diff ; char *p ; int cc ; int cc_total ; int move_entries ; if ( evp->codes == NULL ) return( 0 ) ; diff = evp->next - evp->start ; if ( diff == 0 ) return( 0 ) ; if ( diff > 0 ) { range1 = diff ; range2 = 0 ; } else { range1 = EVENT_ENTRIES - evp->start ; range2 = evp->next ; } bufentries = size / sizeof( short ) ; p = buf ; cc_total = 0 ; move_entries = MIN( range1, bufentries ) ; cc = move_entries * sizeof( short ) ; sio_memcopy( (char *) &evp->codes[ evp->start ], p, cc ) ; cc_total += cc ; p += cc ; bufentries -= range1 ; ADD( evp->start, move_entries ) ; if ( bufentries == 0 || range2 == 0 ) return( cc_total ) ; move_entries = MIN( range2, bufentries ) ; cc = move_entries * sizeof( short ) ; sio_memcopy( (char *) &evp->codes[ evp->start ], p, cc ) ; cc_total += cc ; ADD( evp->start, move_entries ) ; return( cc_total ) ; } #endif /* EVENTS */ /* * Simple function that prints the string s at stderr and then calls * exit */ PRIVATE void terminate( char *s ) { (void) write( 2, s, strlen( s ) ) ; (void) abort() ; exit( 1 ) ; /* in case abort fails */ } *[SRC.TIN-1_22.SIO]SPRINT.C;1+,.$// 4$#2-t0@123KPWO%56A"Æ7g~89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ static char RCSid[] = "$Id: sprint.c,v 8.2 1993/03/17 02:53:29 panos Exp $" ; #include #include "sio.h" #include "impl.h" #ifndef WIDE_INT #define WIDE_INT long #endif typedef WIDE_INT wide_int ; typedef unsigned WIDE_INT u_wide_int ; typedef int bool_int ; #define S_NULL "(null)" #define S_NULL_LEN 6 #define FLOAT_DIGITS 6 #define EXPONENT_LENGTH 10 /* * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * * XXX: this is a magic number; do not decrease it */ #define NUM_BUF_SIZE 512 /* * The INS_CHAR macro inserts a character in the buffer and writes * the buffer back to disk if necessary * It uses the char pointers sp and bep: * sp points to the next available character in the buffer and * bep points to the end-of-buffer+1. * While using this macro, note that the nextb pointer is NOT updated. * * No I/O is performed if fd is not positive. Negative fd values imply * conversion with the output directed to a string. Excess characters * are discarded if the buffer overflows. * * NOTE: Evaluation of the c argument should not have any side-effects */ #define INS_CHAR( c, sp, bep, odp, cc, fd ) \ { \ if ( sp < bep ) \ *sp++ = c ; \ else \ { \ if ( fd >= 0 ) \ { \ odp->nextb = sp ; \ if ( __sio_writef( odp, fd ) != bep - odp->start ) \ return( ( cc != 0 ) ? cc : SIO_ERR ) ; \ sp = odp->nextb ; \ } \ *sp++ = c ; \ } \ cc++ ; \ if ( __SIO_MUST_FLUSH( *odp, c ) && fd >= 0 ) \ { \ int b_in_buffer = sp - odp->start ; \ \ odp->nextb = sp ; \ if ( __sio_writef( odp, fd ) != b_in_buffer ) \ return( cc ) ; \ sp = odp->nextb ; \ } \ } #define NUM( c ) ( c - '0' ) #define STR_TO_DEC( str, num ) \ num = NUM( *str++ ) ; \ while ( isdigit( *str ) ) \ { \ num *= 10 ; \ num += NUM( *str++ ) ; \ } /* * This macro does zero padding so that the precision * requirement is satisfied. The padding is done by * adding '0's to the left of the string that is going * to be printed. */ #define FIX_PRECISION( adjust, precision, s, s_len ) \ if ( adjust ) \ while ( s_len < precision ) \ { \ *--s = '0' ; \ s_len++ ; \ } /* * Macro that does padding. The padding is done by printing * the character ch. */ #define PAD( width, len, ch ) do \ { \ INS_CHAR( ch, sp, bep, odp, cc, fd ) ; \ width-- ; \ } \ while ( width > len ) /* * Prefix the character ch to the string str * Increase length * Set the has_prefix flag */ #define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES /* * Sprint is the equivalent of printf for SIO. * It returns the # of chars written * Assumptions: * - all floating point arguments are passed as doubles */ /* VARARGS2 */ int Sprint( fd, fmt, va_alist ) int fd ; register char *fmt ; va_dcl { __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_od_t *odp = ODP( dp ) ; register int cc ; va_list ap ; IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ; va_start( ap ) ; cc = __sio_converter( odp, fd, fmt, ap ) ; va_end( ap ) ; return( cc ) ; } /* * This is the equivalent of vfprintf for SIO */ int Sprintv( fd, fmt, ap ) int fd ; char *fmt ; va_list ap ; { __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; register __sio_od_t *odp = ODP( dp ) ; IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ; return( __sio_converter( odp, fd, fmt, ap ) ) ; } /* * Convert a floating point number to a string formats 'f', 'e' or 'E'. * The result is placed in buf, and len denotes the length of the string * The sign is returned in the is_negative argument (and is not placed * in buf). */ PRIVATE char *conv_fp( format, num, add_dp, precision, is_negativ#؟~ TIN-1_22.BCKt[SRC.TIN-1_22.SIO]SPRINT.C;1$+ 4 e, buf, len ) register char format ; register double num ; boolean_e add_dp ; /* always add decimal point if YES */ int precision ; bool_int *is_negative ; char buf[] ; int *len ; { register char *s = buf ; register char *p ; int decimal_point ; char *ecvt(), *fcvt() ; char *conv_10() ; char *strcpy() ; if ( format == 'f' ) p = fcvt( num, precision, &decimal_point, is_negative ) ; else /* either e or E format */ p = ecvt( num, precision+1, &decimal_point, is_negative ) ; /* * Check for Infinity and NaN */ if ( isalpha( *p ) ) { *len = strlen( strcpy( buf, p ) ) ; *is_negative = FALSE ; return( buf ) ; } if ( format == 'f' ) if ( decimal_point <= 0 ) { *s++ = '0' ; if ( precision > 0 ) { *s++ = '.' ; while ( decimal_point++ < 0 ) *s++ = '0' ; } else if ( add_dp ) *s++ = '.' ; } else { while ( decimal_point-- > 0 ) *s++ = *p++ ; if ( precision > 0 || add_dp ) *s++ = '.' ; } else { *s++ = *p++ ; if ( precision > 0 || add_dp ) *s++ = '.' ; } /* * copy the rest of p, the NUL is NOT copied */ while ( *p ) *s++ = *p++ ; if ( format != 'f' ) { char temp[ EXPONENT_LENGTH ] ; /* for exponent conversion */ int t_len ; bool_int exponent_is_negative ; *s++ = format ; /* either e or E */ decimal_point-- ; if ( decimal_point != 0 ) { p = conv_10( (wide_int)decimal_point, FALSE, &exponent_is_negative, &temp[ EXPONENT_LENGTH ], &t_len ) ; *s++ = exponent_is_negative ? '-' : '+' ; /* * Make sure the exponent has at least 2 digits */ if ( t_len == 1 ) *s++ = '0' ; while ( t_len-- ) *s++ = *p++ ; } else { *s++ = '+' ; *s++ = '0' ; *s++ = '0' ; } } *len = s - buf ; return( buf ) ; } /* * Convert num to a base X number where X is a power of 2. nbits determines X. * For example, if nbits is 3, we do base 8 conversion * Return value: * a pointer to a string containing the number * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ PRIVATE char *conv_p2( num, nbits, format, buf_end, len ) register u_wide_int num ; register int nbits ; char format ; char *buf_end ; register int *len ; { register int mask = ( 1 << nbits ) - 1 ; register char *p = buf_end ; static char low_digits[] = "0123456789abcdef" ; static char upper_digits[] = "0123456789ABCDEF" ; register char *digits = ( format == 'X' ) ? upper_digits : low_digits ; do { *--p = digits[ num & mask ] ; num >>= nbits ; } while( num ) ; *len = buf_end - p ; return( p ) ; } /* * Convert num to its decimal format. * Return value: * - a pointer to a string containing the number (no sign) * - len contains the length of the string * - is_negative is set to TRUE or FALSE depending on the sign * of the number (always set to FALSE if is_unsigned is TRUE) * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ PRIVATE char *conv_10( num, is_unsigned, is_negative, buf_end, len ) register wide_int num ; register bool_int is_unsigned ; register bool_int *is_negative ; char *buf_end ; register int *len ; { register char *p = buf_end ; register u_wide_int magnitude ; if ( is_unsigned ) { magnitude = (u_wide_int) num ; *is_negative = FALSE ; } else { *is_negative = ( num < 0 ) ; /* * On a 2's complement machine, negating the most negative integer * results in a number that cannot be represented as a signed integer. * Here is what we do to obtain the number's magnitude: * a. add 1 to the number * b. negate it (becomes positive) * c. convert it to unsigned * d. add 1 */ if ( *is_negative ) { wide_int t = num + 1 ; magnitude = ( (u_wide_int) -t ) + 1 ; } else magnitude = (u_wide_int) num ; } /* * We use a do-while loop so that we write at least 1 digit */ do { register u_wide_int new_magnitude = magnitude / 10 ; *--p = magnitude - new_magnitude*10 + '0' ; magnitude = new_magnitude ; } while ( magnitude ) ; *len = buf_end - p ; return( p ) ; } /* * Do format conversion placing the output in odp */ int __sio_converter( odp, fd, fmt, ap ) register __sio_od_t *odp ; int fd ; register char *fmt ; va_list ap ; { register char *sp ; register char *bep ; register int cc = 0 ; register int i ; register char *s ; char *q ; int s_len ; register int min_width ; int precision ; enum { LEFT, RIGHT } adjust ; char pad_char ; char prefix_char ; double fp_num ; wide_int i_num ; u_wide_int ui_num ; char num_buf[ NUM_BUF_SIZE ] ; char char_buf[ 2 ] ; /* for printing %% and % */ /* * Flag variables */ boolean_e is_long ; boolean_e alternate_form ; boolean_e print_sign ; boolean_e print_blank ; boolean_e adjust_precision ; boolean_e adjust_width ; bool_int is_negative ; char *conv_10(), *conv_p2(), *conv_fp() ; char *gcvt() ; char *strchr() ; sp = odp->nextb ; bep = odp->buf_end ; while ( *fmt ) { if ( *fmt != '%' ) { INS_CHAR( *fmt, sp, bep, odp, cc, fd ) ; } else { /* * Default variable settings */ adjust = RIGHT ; alternate_form = print_sign = print_blank = NO ; pad_char = ' ' ; prefix_char = NUL ; fmt++ ; /* * Try to avoid checking for flags, width or precision */ if ( isascii( *fmt ) && ! islower( *fmt ) ) { /* * Recognize flags: -, #, BLANK, + */ for ( ;; fmt++ ) { if ( *fmt == '-' ) adjust = LEFT ; else if ( *fmt == '+' ) print_sign = YES ; else if ( *fmt == '#' ) alternate_form = YES ; else if ( *fmt == ' ' ) print_blank = YES ; else if ( *fmt == '0' ) pad_char = '0' ; else break ; } /* * Check if a width was specified */ if ( isdigit( *fmt ) ) { STR_TO_DEC( fmt, min_width ) ; adjust_width = YES ; } else if ( *fmt == '*' ) { min_width = va_arg( ap, int ) ; fmt++ ; adjust_width = YES ; if ( min_width < 0 ) { adjust = LEFT ; min_width = -min_width ; } } else adjust_width = NO ; /* * Check if a precision was specified * * XXX: an unreasonable amount of precision may be specified * resulting in overflow of num_buf. Currently we * ignore this possibility. */ if ( *fmt == '.' ) { adjust_precision = YES ; fmt++ ; if ( isdigit( *fmt ) ) { STR_TO_DEC( fmt, precision ) ; } else if ( *fmt == '*' ) { precision = va_arg( ap, int ) ; fmt++ ; if ( precision < 0 ) precision = 0 ; } else precision = 0 ; } else adjust_precision = NO ; } else adjust_precision = adjust_width = NO ; /* * Modifier check */ if ( *fmt == 'l' ) { is_long = YES ; fmt++ ; } else is_long = NO ; /* * Argument extraction and printing. * First we determine the argument type. * Then, we convert the argument to a string. * On exit from the switch, s points to the string that * must be printed, s_len has the length of the string * The precision requirements, if any, are reflected in s_len. * * NOTE: pad_char may be set to '0' because of the 0 flag. * It is reset to ' ' by non-numeric formats */ switch( *fmt ) { case 'd': case 'i': case 'u': if ( is_long ) i_num = va_arg( ap, wide_int ) ; else i_num = (wide_int) va_arg( ap, int ) ; s = conv_10( i_num, (*fmt) == 'u', &is_negative, &num_buf[ NUM_BUF_SIZE ], &s_len ) ; FIX_PRECISION( adjust_precision, precision, s, s_len ) ; if ( *fmt != 'u' ) { if ( is_negative ) prefix_char = '-' ; else if ( print_sign ) prefix_char = '+' ; else if ( print_blank ) prefix_char = ' ' ; } break ; case 'o': if ( is_long ) ui_num = va_arg( ap, u_wide_int ) ; else ui_num = (u_wide_int) va_arg( ap, unsigned int ) ; s = conv_p2( ui_num, 3, *fmt, &num_buf[ NUM_BUF_SIZE ], &s_len ) ; FIX_PRECISION( adjust_precision, precision, s, s_len ) ; if ( alternate_form && *s != '0' ) { *--s = '0' ; s_len++ ; } break ; case 'x': case 'X': if ( is_long ) ui_num = (u_wide_int) va_arg( ap, u_wide_int ) ; else ui_num = (u_wide_int) va_arg( ap, unsigned int ) ; s = conv_p2( ui_num, 4, *fmt, &num_buf[ NUM_BUF_SIZE ], &s_len ) ; FIX_PRECISION( adjust_precision, precision, s, s_len ) ; if ( alternate_form && i_num != 0 ) { *--s = *fmt ; /* 'x' or 'X' */ *--s = '0' ; s_len += 2 ; } break ; case 's': s = va_arg( ap, char * ) ; if ( s != NULL ) { s_len = strlen( s ) ; if ( adjust_precision && precision < s_len ) s_len = precision ; } else { s = S_NULL ; s_len = S_NULL_LEN ; } pad_char = ' ' ; break ; case 'f': case 'e': case 'E': fp_num = va_arg( ap, double ) ; s = conv_fp( *fmt, fp_num, alternate_form, ( adjust_precision == NO ) ? FLOAT_DIGITS : precision, &is_negative, &num_buf[ 1 ], &s_len ) ; if ( is_negative ) prefix_char = '-' ; else if ( print_sign ) prefix_char = '+' ; else if ( print_blank ) prefix_char = ' ' ; break ; case 'g': case 'G': if ( adjust_precision == NO ) precision = FLOAT_DIGITS ; else if ( precision == 0 ) precision = 1 ; /* * We use &num_buf[ 1 ], so that we have room for the sign */ s = gcvt( va_arg( ap, double ), precision, &num_buf[ 1 ] ) ; if ( *s == '-' ) prefix_char = *s++ ; else if ( print_sign ) prefix_char = '+' ; else if ( print_blank ) prefix_char = ' ' ; s_len = strlen( s ) ; if ( alternate_form && ( q = strchr( s, '.' ) ) == NULL ) s[ s_len++ ] = '.' ; if ( *fmt == 'G' && ( q = strchr( s, 'e' ) ) != NULL ) *q = 'E' ; break ; case 'c': char_buf[ 0 ] = (char) (va_arg( ap, int )) ; s = &char_buf[ 0 ] ; s_len = 1 ; pad_char = ' ' ; break ; case '%': char_buf[ 0 ] = '%' ; s = &char_buf[ 0 ] ; s_len = 1 ; pad_char = ' ' ; break ; case 'n': *(va_arg( ap, int * )) = cc ; break ; /* * Always extract the argument as a "char *" pointer. We * should be using "void *" but there are still machines * that don't understand it. * If the pointer size is equal to the size of an unsigned * integer we convert the pointer to a hex number, otherwise * we print "%p" to indicate that we don't handle "%p". */ case 'p': ui_num = (u_wide_int) va_arg( ap, char * ) ; if ( sizeof( char * ) <= sizeof( u_wide_int ) ) s = conv_p2( ui_num, 4, 'x', &num_buf[ NUM_BUF_SIZE ], &s_len ) ; else { s = "%p" ; s_len = 2 ; } pad_char = ' ' ; break ; case NUL: /* * The last character of the format string was %. * We ignore it. */ continue ; /* * The default case is for unrecognized %'s. * We print % to help the user identify what * option is not understood. * This is also useful in case the user wants to pass * the output of __sio_converter to another function * that understands some other % (like syslog). * Note that we can't point s inside fmt because the * unknown could be preceded by width etc. */ default: char_buf[ 0 ] = '%' ; char_buf[ 1 ] = *fmt ; s = char_buf ; s_len = 2 ; pad_char = ' ' ; break ; } if ( prefix_char != NUL ) { *--s = prefix_char ; s_len++ ; } if ( adjust_width && adjust == RIGHT && min_width > s_len ) { if ( pad_char == '0' && prefix_char != NUL ) { INS_CHAR( *s, sp, bep, odp, cc, fd ) s++ ; s_len-- ; min_width-- ; } PAD( min_width, s_len, pad_char ) ; } /* * Print the string s. */ for ( i = s_len ; i != 0 ; i-- ) { INS_CHAR( *s, sp, bep, odp, cc, fd ) ; s++ ; } if ( adjust_width && adjust == LEFT && min_width > s_len ) PAD( min_width, s_len, pad_char ) ; } fmt++ ; } odp->nextb = sp ; return( cc ) ; } *[SRC.TIN-1_22.SIO]SUITE.DIR;1+,.// 4-t0123 KPWO56WÆ7W89IʒG/HJI $M$AKEFILE.$README. BUFTEST.C COPYTEST.C EXAMPLE.CFDTEST.CPRINT.C SPRINT_TEST.TESTER.TESTLIB. TIETEST.C%*[SRC.TIN-1_22.SIO.SUITE]$M$AKEFILE.;1+,.// 4-0@123KPWO56`Æ789]VG/HJ # (c) Copyright 1992, 1993 by Panagiotis Tsirigotis # All rights reserved. The file named COPYRIGHT specifies the terms # and conditions for redistribution. # # $Id: Makefile,v 8.1 1993/03/13 01:27:40 panos Exp $ # CC = cc -I.. # # NOTE: When using the test scripts, CLFAGS is provided as an # argument to make, the setting of it here has no effect. # CFLAGS = -g LIBOBJS = ../libsio.a DISTRIBUTION_FILES=copytest.c example.c print.c tietest.c buftest.c fdtest.c tester sprint_test testlib README ALL=Sread Swrite Sputchar Sgetchar Srdline \ Sgetc Sputc Sfetch Sflush Sundo switch \ Sprint buftest tietest switch2 example fdtest evtest evtest: evtest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ evtest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ fdtest: fdtest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ fdtest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ buftest: buftest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ buftest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Sprint: print.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ print.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Sputchar: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Sgetchar: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Srdline: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) || rm -f $@ Sread: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Swrite: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Sgetc: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Sputc: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Sfetch: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Sflush: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ Sundo: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ switch: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ switch2: copytest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ copytest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ tietest: tietest.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ tietest.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ example: example.c $(LIBOBJS) $(CC) $(CFLAGS) -o $@ example.c $(LIBOBJS) $(LDFLAGS) || rm -f $@ clean: rm -f $(ALL) core checkout: $(DISTRIBUTION_FILES) $(DISTRIBUTION_FILES): co $@ dist: -co -q $(DISTRIBUTION_FILES) "*[SRC.TIN-1_22.SIO.SUITE]$README.;1+,.// 4-0@123KPWO56@ʷÆ789]VG/HJ The 'testlib' script will exercise most of the functions in the SIO library. It invokes the 'tester' script which does the real work. 'tester' is a Bourne shell script. However, it expects that the shell supports functions and /bin/sh does not support functions on all operating systems. Therefore, 'testlib' decides what shell to use to execute 'tester'. On Suns, it uses /bin/sh. On DECstations, it uses /usr/bin/ksh. The decision is made by checking the ARCH environment variable. Exercising some of the SIO functions in an automatic fashion is a difficult task, so you will have to do it manually by visually inspecting the results of programs that exercise them. The following is a list of functions and programs testing them (with a description of the expected behavior): 1. Sbuftype PROGRAM: buftest.c DESCRIPTION: This program prints two groups of lines. The first group is printed using line-buffering while the second group is printed using full-buffering. The first group of lines should appear one line at a time every 3 seconds. The second group of lines should appear all lines together after about 10 seconds. 2. Stie, Suntie PROGRAM: tietest.c DESCRIPTION: This program ties stdin to stdout and then prompts for input. The prompts do *not* include a NEWLINE. Since the stdout is *line buffered* when connected to a terminal, the Stie call is what causes the prompt to appear. The first 2 prompts happen with tied stdin, stdout. For the 3rd prompt, stdin is untied from stdout. This will cause the prompt to appear *after* you type something and hit RETURN. PS. If you can make testing of these functions automatic, please send me your code so that I can include in a future SIO distribution. #*[SRC.TIN-1_22.SIO.SUITE]BUFTEST.C;1+,.// 4-0@123KPWO56\وÆ7$89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ static char RCSid[] = "$Id: buftest.c,v 8.1 1993/03/13 01:23:09 panos Exp $" ; #include "sio.h" main() { int i ; int sleep_interval = 3 ; if ( Sbuftype( 1, SIO_LINEBUF ) == SIO_ERR ) { Sprint( 2, "Sbuftype failed\n" ) ; exit( 1 ) ; } for ( i = 0 ; i < 10 ; i++ ) { Sprint( 1, "Line %d\n", i ) ; if ( i == 5 ) { Sprint( 1, "Now switching to full buffering\n" ) ; sleep_interval = 2 ; if ( Sbuftype( 1, SIO_FULLBUF ) == SIO_ERR ) { Sprint( 2, "2nd Sbuftype failed\n" ) ; exit( 1 ) ; } } sleep( sleep_interval ) ; } exit( 0 ) ; } $*[SRC.TIN-1_22.SIO.SUITE]COPYTEST.C;1+,. // 4 -0@123KPWO 56@Æ7$89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ static char RCSid[] = "$Id: copytest.c,v 8.1 1993/03/13 01:21:48 panos Exp $" ; #include "sio.h" #include #include /*************************************************************/ #ifdef TEST_Sread #define BUFFER_SIZE 4096 main() { char buf[ BUFFER_SIZE ] ; int cc ; int nbytes ; for ( ;; ) { nbytes = random() & ( BUFFER_SIZE - 1 ) ; if ( nbytes == 0 ) nbytes = 1 ; cc = Sread( 0, buf, nbytes ) ; if ( cc == 0 ) break ; if ( cc == SIO_ERR ) exit( 1 ) ; write( 1, buf, cc ) ; } exit( 0 ) ; } #endif /* TEST_Sread */ /*************************************************************/ #ifdef TEST_Swrite #define BUFFER_SIZE 4096 main() { char buf[ BUFFER_SIZE ] ; int cc ; int nbytes ; for ( ;; ) { nbytes = random() & ( BUFFER_SIZE - 1 ) ; if ( nbytes == 0 ) nbytes = 1 ; cc = read( 0, buf, nbytes ) ; if ( cc == 0 ) break ; if ( Swrite( 1, buf, cc ) != cc ) exit( 1 ) ; } exit( 0 ) ; } #endif /* TEST_Swrite */ /*************************************************************/ #ifdef TEST_Srdline main() { char *s ; int count=0 ; while ( s = Srdline( 0 ) ) { puts( s ) ; count++ ; } Sdone( 0 ) ; exit( 0 ) ; } #endif /* TEST_Srdline */ /*************************************************************/ #ifdef TEST_Sputchar main() { int c ; while ( ( c = getchar() ) != EOF ) if ( Sputchar( 1, c ) != c ) exit( 1 ) ; exit( 0 ) ; } #endif /* TEST_Sputchar */ /*************************************************************/ #ifdef TEST_Sgetchar main() { int c ; while ( ( c = Sgetchar( 0 ) ) != SIO_EOF ) putchar( c ) ; exit( 0 ) ; } #endif /* TEST_Sgetchar */ /*************************************************************/ #ifdef TEST_Sputc main() { int c ; while ( ( c = getchar() ) != EOF ) if ( Sputc( 1, c ) != c ) exit( 1 ) ; exit( 0 ) ; } #endif /* TEST_Sputc */ /*************************************************************/ #ifdef TEST_Sgetc main() { int c ; while ( ( c = Sgetc( 0 ) ) != SIO_EOF ) putchar( c ) ; exit( 0 ) ; } #endif /* TEST_Sgetc */ /*************************************************************/ #ifdef TEST_Sfetch main() { char *s ; int len ; while ( s = Sfetch( 0, &len ) ) fwrite( s, 1, len, stdout ) ; exit( 0 ) ; } #endif /* TEST_Sfetch */ /*************************************************************/ #ifdef TEST_Sflush #define MAX_COUNT 100 main() { int c ; int errval ; int count = 0 ; int max_count = random() % MAX_COUNT + 1 ; while ( ( c = getchar() ) != EOF ) if ( Sputchar( 1, c ) != c ) exit( errval ) ; else { count++ ; if ( count >= max_count ) { errval = Sflush( 1 ) ; if ( errval != 0 ) exit( 1 ) ; max_count = random() % MAX_COUNT + 1 ; count = 0 ; } } exit( 0 ) ; } #endif /* TEST_Sflush */ /*************************************************************/ #ifdef TEST_Sundo main() { int c ; char *s ; int errval ; for ( ;; ) { if ( random() % 1 ) { s = Srdline( 0 ) ; if ( s == NULL ) break ; if ( random() % 16 < 5 ) { errval = Sundo( 0, SIO_UNDO_LINE ) ; if ( errval == SIO_ERR ) exit( 1 ) ; } else puts( s ) ; } else { c = Sgetchar( 0 ) ; if ( c == SIO_EOF ) break ; if ( random() % 16 < 5 ) { errval = Sundo( 0, SIO_UNDO_CHAR ) ; if ( errval == SIO_ERR ) exit( 2 ) ; } else putchar( c ) ; } } exit( 0 ) ; } #endif /* TEST_Sundo */ #if defined( TEST_switch ) || defined( TEST_switch2 ) main() { int c ; char *s ; int lines = 4000 ; for ( ;; ) { c = Sgetchar( 0 ) ; if ( c == SIO_EOF ) exit( 0 ) ; if ( c == SIO_ERR ) exit( 1 ) ; putchar( c ) ; if ( c == '\n' ) { lines-- ; if ( lines == 0 ) break ; } } while ( s = Srdline( 0 ) ) puts( s ) ; exit( 0 ) ; } #ifdef TEST_switch2 char *mmap( addr, len, prot, type, fd, off ) char *addr ; int len, prot, type, fd, off ; { return( (char *)-1 ) ; } #endif /* TEST_switch2 */ #endif /* TEST_switch */ #*[SRC.TIN-1_22.SIO.SUITE]EXAMPLE.C;1+,.// 49-0@123KPWO56`!Æ7*89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ static char RCSid[] = "$Id: example.c,v 8.1 1993/03/13 01:21:48 panos Exp $" ; #include "sio.h" main( argc, argv ) int argc ; char *argv[] ; { char *file = (argc > 1) ? argv[ 1 ] : "tee.file" ; int fd = creat( file, 0644 ) ; long length ; char *s ; while ( s = Sfetch( 0, &length ) ) { Swrite( 1, s, length ) ; Swrite( fd, s, length ) ; } exit( 0 ) ; } "*[SRC.TIN-1_22.SIO.SUITE]FDTEST.C;1+,.// 4~-0@123KPWO569GÆ7*89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include #include #include "sio.h" #ifndef NULL #define NULL 0 #endif int main( argc, argv ) int argc ; char *argv[] ; { #ifdef RLIMIT_NOFILE struct rlimit rl ; int fd ; int duped_fd ; char *s ; if ( getrlimit( RLIMIT_NOFILE, &rl ) == -1 ) { perror( "getrlimit" ) ; exit( 1 ) ; } if ( rl.rlim_cur != getdtablesize() ) { printf( "rl.rlim_cur != getdtablesize()\n" ) ; exit( 1 ) ; } if ( rl.rlim_cur == rl.rlim_max ) exit( 0 ) ; rl.rlim_cur++ ; if ( setrlimit( RLIMIT_NOFILE, &rl ) == -1 ) { perror( "setrlimit" ) ; exit( 1 ) ; } if ( Smorefds() == SIO_ERR ) { perror( "Smorefds" ) ; exit( 1 ) ; } fd = open( "/etc/passwd", 0 ) ; if ( fd == -1 ) { perror( "open" ) ; exit( 1 ) ; } duped_fd = getdtablesize()-1 ; if ( dup2( fd, duped_fd ) == -1 ) { perror( "dup2" ) ; exit( 1 ) ; } s = Srdline( duped_fd ) ; if ( s == NULL ) { perror( "Srdline" ) ; exit( 1 ) ; } #endif exit( 0 ) ; } !*[SRC.TIN-1_22.SIO.SUITE]PRINT.C;1+,.// 43-0@123KPWO56 DgÆ7U89]VG/HJ /* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ static char RCSid[] = "$Id: print.c,v 8.1 1993/03/13 01:21:48 panos Exp $" ; #include #include #include #include #include "sio.h" #define FLUSH() fflush( stdout ) ; Sflush( 1 ) #define COMPARE( printf_count, sprint_count ) \ if ( printf_count != sprint_count ) \ printf( "printf_count = %d, sprint_count = %d\n", \ printf_count, sprint_count ) enum bool { NO = 0, YES = 1 } ; enum test_flag { DECIMAL, HEX, CAP_HEX, OCTAL, UNSIGNED, F_FLOAT, G_FLOAT, E_FLOAT, CAP_E_FLOAT, CAP_G_FLOAT, CHAR, STRING, POINTER, BOUND, N_FLAGS } ; typedef enum test_flag FLAG ; #define CHECK( f ) if ( ! flags[ f ] ) return /* * Flags */ enum bool flags[ N_FLAGS ] ; char *precision ; char *width ; char *print_flags ; int i_begin = 123456 ; int i_end = 123470 ; int i_step = 1 ; double f_begin = 1.234567654312 ; double f_end = 2.0 ; double f_step = 0.011 ; #define LEN( s ) ( s ? strlen( s ) : 0 ) char *format( f ) char *f ; { char *malloc() ; static char *newfmt ; if ( newfmt ) free( newfmt ) ; newfmt = malloc( strlen( f ) + LEN( precision ) + LEN( width ) + LEN( print_flags ) + 2 ) ; (void) strcpy( newfmt, "%" ) ; if ( print_flags ) (void) strcat( newfmt, print_flags ) ; if ( width ) (void) strcat( newfmt, width ) ; if ( precision ) (void) strcat( strcat( newfmt, "." ), precision ) ; (void) strcat( newfmt, &f[1] ) ; return( newfmt ) ; } #define decimal_test() integer_test( "%d %d\n", DECIMAL ) #define hex_test() integer_test( "%x %x\n", HEX ) #define cap_hex_test() integer_test( "%X %X\n", CAP_HEX ) #define octal_test() integer_test( "%o %o\n", OCTAL ) #define unsigned_test() integer_test( "%u %u\n", UNSIGNED ) void integer_test( fmt, flag ) char *fmt ; FLAG flag ; { int i ; int ccs, ccp ; CHECK( flag ) ; fmt = format( fmt ) ; for ( i = i_begin ; i < i_end ; i += i_step ) { ccp = printf( fmt, -i, i ) ; ccs = Sprint( 2, fmt, -i, i ) ; FLUSH() ; COMPARE( ccp, ccs ) ; } } #define f_float_test() fp_test( "%f\n", F_FLOAT ) #define g_float_test() fp_test( "%g\n", G_FLOAT ) #define e_float_test() fp_test( "%e\n", E_FLOAT ) #define cap_e_float_test() fp_test( "%E\n", CAP_E_FLOAT ) #define cap_g_float_test() fp_test( "%G\n", CAP_G_FLOAT ) void fp_test( fmt, flag ) char *fmt ; FLAG flag ; { double d ; double step ; int ccs, ccp ; CHECK( flag ) ; fmt = format( fmt ) ; for ( d = f_begin, step = f_step ; d < f_end ; d += step, step += step ) { ccp = printf( fmt, d ) ; ccs = Sprint( 2, fmt, d ) ; FLUSH() ; COMPARE( ccp, ccs ) ; } } void char_test() { char *s = "foobar" ; int len = strlen( s ) ; int i ; char *fmt = "%c\n" ; int ccs, ccp ; CHECK( CHAR ) ; fmt = format( fmt ) ; for ( i = 0 ; i < len ; i++ ) { ccp = printf( fmt, s[ i ] ) ; ccs = Sprint( 2, fmt, s[ i ] ) ; FLU$&~ TIN-1_22.BCK![SRC.TIN-1_22.SIO.SUITE]PRINT.C;1SH() ; COMPARE( ccp, ccs ) ; } } void string_test() { static char *list[] = { "foobar", "hello", "world", "this is a very long string, a really long string, really, true, honest", "i am getting tired of this", "SO THIS IS THE END", 0 } ; char *fmt = "%s\n" ; char **p ; int ccp, ccs ; CHECK( STRING ) ; fmt = format( fmt ) ; for ( p = &list[ 0 ] ; *p ; p++ ) { ccp = printf( fmt, *p ) ; ccs = Sprint( 2, fmt, *p ) ; FLUSH() ; COMPARE( ccp, ccs ) ; } } void pointer_test() { struct foo { char bar1 ; short bar2 ; int bar3 ; long bar4 ; char *bar5 ; } foo, *end = &foo, *p ; char *fmt = "%p\n" ; int ccp, ccs ; CHECK( POINTER ) ; fmt = format( fmt ) ; end += 10 ; for ( p = &foo ; p < end ; p++ ) { ccp = printf( fmt, p ) ; ccs = Sprint( 2, fmt, p ) ; FLUSH() ; } } /* * bound_test is only available on SunOS 4.x */ #if defined( sun ) void bound_test() { char *fmt ; double bound_values[ 10 ] ; static char *bound_names[] = { "min_subnormal", "max_subnormal", "min_normal", "max_normal", "infinity", "quiet_nan", "signaling_nan" } ; int n_values ; int i ; int ccp, ccs ; bound_values[ 0 ] = min_subnormal() ; bound_values[ 1 ] = max_subnormal() ; bound_values[ 2 ] = min_normal() ; bound_values[ 3 ] = max_normal() ; bound_values[ 4 ] = infinity() ; bound_values[ 5 ] = quiet_nan( 7L ) ; bound_values[ 6 ] = signaling_nan( 7L ) ; n_values = 7 ; CHECK( BOUND ) ; for ( i = 0 ; i < n_values ; i++ ) { double d = bound_values[ i ] ; char *name = bound_names[ i ] ; fmt = format( "%f (%s)\n" ) ; ccp = printf( fmt, d, name ) ; ccs = Sprint( 2, fmt, d, name ) ; FLUSH() ; COMPARE( ccp, ccs ) ; fmt = format( "%e (%s)\n" ) ; ccp = printf( fmt, d, name ) ; ccs = Sprint( 2, fmt, d, name ) ; FLUSH() ; COMPARE( ccp, ccs ) ; fmt = format( "%g (%s)\n" ) ; ccp = printf( fmt, d, name ) ; ccs = Sprint( 2, fmt, d, name ) ; FLUSH() ; COMPARE( ccp, ccs ) ; } fmt = format( "%d (MININT)\n" ) ; ccp = printf( fmt, -MAXINT-1 ) ; ccs = Sprint( 2, fmt, -MAXINT-1 ) ; COMPARE( ccp, ccs ) ; } #else void bound_test() { } #endif int get_options( argc, argv ) int argc ; char *argv[] ; { int arg_index = 1 ; char *p ; double atof() ; for ( arg_index = 1 ; arg_index < argc && argv[ arg_index ][ 0 ] == '-' ; arg_index++ ) { switch ( argv[ arg_index ][ 1 ] ) { case 'd': flags[ DECIMAL ] = YES ; break ; case 'x': flags[ HEX ] = YES ; break ; case 'X': flags[ CAP_HEX ] = YES ; break ; case 'o': flags[ OCTAL ] = YES ; break ; case 'u': flags[ UNSIGNED ] = YES ; break ; case 'f': flags[ F_FLOAT ] = YES ; break ; case 'g': flags[ G_FLOAT ] = YES ; break ; case 'e': flags[ E_FLOAT ] = YES ; break ; case 'E': flags[ CAP_E_FLOAT ] = YES ; break ; case 'G': flags[ CAP_G_FLOAT ] = YES ; break ; case 'c': flags[ CHAR ] = YES ; break ; case 's': flags[ STRING ] = YES ; break ; case 'p': flags[ POINTER ] = YES ; break ; case 'b': /* this is for checking bounds in fp formats */ flags[ BOUND ] = YES ; break ; case 'P': /* precision, must be followed by a number, e.g. -P10 */ precision = &argv[ arg_index ][ 2 ] ; break ; case 'W': /* width, must be followed by a number, e.g. -w10 */ width = &argv[ arg_index ][ 2 ] ; break ; case 'F': /* flags, whatever is after the F */ print_flags = &argv[ arg_index ][ 2 ] ; break ; /* * Options recognized in this case: -Vf, -Vi * Usage: -V[if] start end step */ case 'V': /* * Check if we have enough extra arguments */ if ( argc - ( arg_index + 1 ) < 3 ) { fprintf( stderr, "Insufficient # of args after V option\n" ) ; exit( 1 ) ; } switch ( argv[ arg_index ][ 2 ] ) { case 'f': f_begin = atof( argv[ arg_index+1 ] ) ; f_end = atof( argv[ arg_index+2 ] ) ; f_step = atof( argv[ arg_index+3 ] ) ; break ; case 'i': i_begin = atoi( argv[ arg_index+1 ] ) ; i_end = atoi( argv[ arg_index+2 ] ) ; i_step = atoi( argv[ arg_index+3 ] ) ; break ; } arg_index += 3 ; break ; case 'S': f_step = atof( &argv[ arg_index ][ 2 ] ) ; break ; } } return( arg_index ) ; } #define EQ( s1, s2 ) ( strcmp( s1, s2 ) == 0 ) int main( argc, argv ) int argc ; char *argv[] ; { if ( Sbuftype( 2, SIO_LINEBUF ) == SIO_ERR ) { char *msg = "Sbuftype failed\n" ; write( 2, msg, strlen( msg ) ) ; exit( 1 ) ; } if ( argc == 1 || argc == 2 && EQ( argv[ 1 ], "ALL" ) ) { /* perform all tests */ int i ; for ( i = 0 ; i < N_FLAGS ; i++ ) flags[ i ] = YES ; } else (void) get_options( argc, argv ) ; decimal_test() ; hex_test() ; cap_hex_test() ; octal_test() ; unsigned_test() ; f_float_test() ; g_float_test() ; e_float_test() ; cap_g_float_test() ; cap_e_float_test() ; string_test() ; char_test() ; pointer_test() ; bound_test() ; exit( 0 ) ; } &*[SRC.TIN-1_22.SIO.SUITE]SPRINT_TEST.;1+,.// 4z-0@123KPWO56wÆ7U89]VG/HJ#!/bin/sh # (c) Copyright 1992, 1993 by Panagiotis Tsirigotis # All rights reserved. The file named COPYRIGHT specifies the terms # and conditions for redistribution. # # $Id: sprint_test,v 8.1 1993/03/13 01:21:48 panos Exp $ # compare() { cmp -s PRINTF.DATA SPRINT.DATA if [ $? -ne 0 ] then echo TEST $1 FAILED echo Check files PRINTF.DATA and SPRINT.DATA for differences exit 1 else echo TEST $1 PASSED rm -f PRINTF.DATA SPRINT.DATA fi } format_test() { Sprint $@ 1>PRINTF.DATA 2>SPRINT.DATA compare "$*" } format_test ALL format_test -W10 -F0 -c format_test -W10 -F- -c echo echo PRECISION TEST i=13 while test $i -ne 0 do format_test -P$i -F# -X i=`expr $i - 1` done echo END OF PRECISION TEST echo format_test -Vi -4 4 1 -o "-F#" format_test -Vf -1.0 1.0 0.3 -W30 -P13 -F+ -f format_test -Vf -1.0 1.0 0.3 -W30 -P13 -F+ -e format_test -Vf -1.0 2.0 0.3 -W30 -P13 -F+ -g format_test -Vf -1.0 1.0 0.3 -W30 -P13 -F+- -f format_test -Vf -1.0 1.0 0.3 -W30 -P13 -F+- -e format_test -Vf -1.0 2.0 0.3 -W30 -P13 -F+- -g format_test -W10 "-F " -x format_test -W40 -u -Vi 8 100 3 -F0 format_test -b format_test -b -P10 !*[SRC.TIN-1_22.SIO.SUITE]TESTER.;1+,. // 4 -0@123KPWO 56@Æ7U89]VG/HJ#!/bin/sh # (c) Copyright 1992, 1993 by Panagiotis Tsirigotis # All rights reserved. The file named COPYRIGHT specifies the terms # and conditions for redistribution. # # $Id: tester,v 8.2 1993/03/17 18:53:40 panos Exp $ # # # Usage: # tester [all] [function-name function-name ...] # # function-name is the name of a sio function (or macro) # # If "all" is used, functions after it will *not* be tested. # script_name=`basename $0` copy_file=/usr/dict/words temp_file=/tmp/w make_log=MAKE.LOG if test ! -f $copy_file ; then echo "The file '$copy_file' is not available." echo "Please edit the '$script_name' script and change set the" echo "variable 'copy_file' to the name of a publicly readable file" echo "with at least a few tens of thousands of lines." exit 1 fi trap_function() { rm -f $temp_file $make_log echo exit 1 } make_program() { target=$1 cflags="$2" if test -f $1 -a ! -x $1 ; then rm -f $1 ; fi if test "$cflags" then make -s "$cflags" $target >$make_log 2>&1 else make -s $target >$make_log 2>&1 fi exit_code=$? if test $exit_code -eq 0 -a -x $1 then rm -f $make_log else echo "FAILED" echo " The make failed. Check the make log file << $make_log >>" exit fi } # # test_function expects a single argument, the name of the function # it will test. # It creates a program that has the name of the function by invoking # make with the symbol -DTEST_ # test_function() { expression="echo $"$1 var=`eval $expression` if test "$var" = "no" -o "$var" = "" -a "$all" = "no" ; then return ; fi echo -n "TESTING $1 " make_program $1 "CFLAGS=-g -DTEST_$1" ./$1 < $copy_file >$temp_file exit_code=$? if test $exit_code -ne 0 then echo "FAILED" echo " Test program exited with exit code $exit_code" echo " Temporary file << $temp_file >> not deleted" exit fi cmp -s $copy_file $temp_file if test $? -ne 0 then echo "FAILED" echo " The files << $copy_file >> and << $temp_file >> are not the same" exit else echo PASSED fi rm -f $temp_file } test_sprint() { var=$Sprint program=Sprint if test "$var" = "no" -o "$var" = "" -a "$all" = "no" ; then return ; fi echo TESTING Sprint make_program $program "" $TESTSHELL sprint_test } test_smorefds() { var=$Smorefds program=fdtest if test "$var" = "no" -o "$var" = "" -a "$all" = "no" ; then return ; fi echo -n "TESTING Smorefds " make_program $program "CFLAGS=-g" v=`fdtest 2>&1` if test $? -eq 0 ; then echo PASSED else echo "FAILED: $v" fi } trap trap_function 1 2 3 15 # # There is a variable for every function to be tested. That variable # can have the values "yes", "no" or "". # When a function is specified, it takes the value of $run. Initially $run # is "yes", so a specified function has its variable set to "yes". # If "all" is specified, $run becomes "no", so subsequently specified # functions, have their variables set to "no". # # We test a function iff: # its variable is "yes" OR its variable is "" and $all is "yes" # We don't test a function iff: # its variable is "no" OR its variable is "" and $all is "no" # # Therefore, all functions specified after "all" will NOT be tested. # run=yes all=no while test $# -gt 0 do case $1 in Sputchar) Sputchar=$run ;; Sgetchar) Sgetchar=$run ;; Srdline) Srdline=$run ;; Sfetch) Sfetch=$run ;; Sread) Sread=$run ;; Swrite) Swrite=$run ;; Sgetc) Sgetc=$run ;; Sputc) Sputc=$run ;; Sflush) Sflush=$run ;; Sundo) Sundo=$run ;; Sprint) Sprint=$run ;; switch) switch=$run ;; switch2) switch2=$run ;; Smorefds) Smorefds=$run ;; all) all=yes ; run="no" ;; *) echo Bad argument: $1 esac shift done test_function Sgetchar test_function Sputchar test_function Sread test_function Swrite test_function Srdline test_function Sfetch test_function Sgetc test_function Sputc test_function Sflush test_function Sundo test_function switch test_function switch2 test_smorefds test_sprint "*[SRC.TIN-1_22.SIO.SUITE]TESTLIB.;1+,.// 4m-0@123KPWO56@ЉÆ7W89]VG/HJ#!/bin/sh # (c) Copyright 1992, 1993 by Panagiotis Tsirigotis # All rights reserved. The file named COPYRIGHT specifies the terms # and conditions for redistribution. # # $Id: testlib,v 8.1 1993/03/13 01:23:48 panos Exp $ # # # Purpose: # Invoke the tester script. This is necessary because the tester # script requires functions and /bin/sh does not support functions # on all OS's. This script decides what shell to use to execute # tester. On Suns, it uses /bin/sh. On DECstations, it uses # /usr/bin/ksh # The decision is made by checking $ARCH # case "$ARCH" in "") echo "ARCH not defined. Please define it." exit 1 ;; sun4|sun3) TESTSHELL=/bin/sh LDFLAGS="-Bstatic -lm" ;; dec-mips) TESTSHELL=/usr/bin/ksh LDFLAGS= ;; *) echo "Unknown architecture: $ARCH" exit 2 ;; esac export LDFLAGS export TESTSHELL $TESTSHELL tester $* #*[SRC.TIN-1_22.SIO.SUITE]TIETEST.C;1+,.// 4-0@123KPWO56nÆ7W89]VG/HJ/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ static char RCSid[] = "$Id: tietest.c,v 8.1 1993/03/13 01:24:07 panos Exp $" ; #include "sio.h" #include main() { char *s ; Stie( 0, 1 ) ; Sprint( 1, "Enter 1st command --> " ) ; s = Srdline( 0 ) ; Sprint( 1, "Received command: %s\n", s ) ; Sprint( 1, "Enter 2nd command --> " ) ; s = Srdline( 0 ) ; Sprint( 1, "Received command: %s\n", s ) ; Suntie( 0 ) ; Sprint( 1, "Enter 3rd command --> " ) ; s = Srdline( 0 ) ; Sprint( 1, "Received command: %s\n", s ) ; exit( 0 ) ; } *[SRC.TIN-1_22.SIO]TAGS.;2+,.// 4o-t0123KPWO56 C;7@nD;89]VG/HJ local:[src.sio]sio.c,322 int Sbuftype(635,14342 int Sclose(499,11351 char *Sfetch(350,7935 int Sflush(450,9973 int Sgetc(325,7419 int Sputc(107,2259 char *Srdline(216,4735 int Sread(155,3374 int Stie(535,12386 int Sundo(391,8646 int Suntie(607,13844 int Swrite(27,519 void __sio_memcopy(680,15100 PRIVATE char *sio_memscan(661,14775 local:[src.sio]siosup.c,916 #define FATAL_ERROR(94,2349 #define FIRST_TIME(93,2300 #define MDP(63,1337 SIO_DEFINE_FIN(40,878 int Sdone(864,19111 int Smorefds(972,21232 void __sio_disable_events(1048,23048 int __sio_enable_events(1032,22776 int __sio_extend_buffer(791,17671 int __sio_get_events(1062,23261 int __sio_init(532,11905 int __sio_more(826,18513 int __sio_readf(675,15042 status_e __sio_switch(214,5420 int __sio_writef(604,13492 PRIVATE void buffer_setup(182,4536 PRIVATE char *expand(925,20315 PRIVATE int get_fd_limit(949,20774 PRIVATE status_e init_input_stream(401,9430 PRIVATE status_e init_output_stream(439,10250 PRIVATE int initial_map(282,6844 PRIVATE int isatty(488,11092 PRIVATE int isatty(504,11318 PRIVATE status_e map_unit(341,8146 PRIVATE status_e setup_read_buffer(379,8990 PRIVATE void terminate(1125,24422 PRIVATE status_e try_memory_mapping(119,2946 #define try_memory_mapping(374,8916 local:[src.sio]sprint.c,322 #define FIX_PRECISION(95,2736 #define INS_CHAR(50,1340 #define NUM(79,2305 #define PAD(107,3048 #define PREFIX(119,3377 #define STR_TO_DEC(81,2337 int Sprint(129,3637 int Sprintv(151,4043 int __sio_converter(371,9056 PRIVATE char *conv_10(313,7860 PRIVATE char *conv_fp(170,4543 PRIVATE char *conv_p2(275,6818 V*[SRC.TIN-1_22]SPOOLDIR.C;1+,.// 4-d0@123KPWO56!t7't189]VG/HJ:/* * Project : tin - a Usenet reader * Module : spooldir.c * Author : I.Lea & Tom Theel * Created : 08-05-92 * Updated : 11-07-93 * Notes : Changes spooldir to read news from (ie. news, nntp, cdrom) * Copyright : (c) Copyright 1991-93 by Iain Lea & Tom Theel * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" int cur_spoolnum = 0; int first_spooldir_on_screen; int last_spooldir_on_screen; int spool_top = 0; /* * Change spooldir via menu of available choices */ int spooldir_index () { #ifndef INDEX_DAEMON int ch; int n; int scroll_lines; spool_top = num_spooldir; if (! xspooldir_supported) { info_message (txt_spooldirs_not_supported); return FALSE; } if (! spool_top) { info_message (txt_no_spooldirs); return FALSE; } mail_setup (); /* record mailbox size for "you have mail" */ #ifndef USE_CLEARSCREEN ClearScreen(); #endif show_spooldir_page (); /* display spooldir selection page */ while (TRUE) { set_xclick_on (); ch = ReadCh (); if (ch > '0' && ch <= '9') { prompt_spooldir_num (ch); continue; } switch (ch) { case ESC: /* (ESC) common arrow keys */ #ifdef HAVE_KEY_PREFIX case KEY_PREFIX: #endif switch (get_arrow_key ()) { case KEYMAP_UP: goto spooldir_up; case KEYMAP_DOWN: goto spooldir_down; case KEYMAP_PAGE_UP: goto spooldir_page_up; case KEYMAP_PAGE_DOWN: goto spooldir_page_down; case KEYMAP_HOME: if (cur_spoolnum != 0) { if (0 < first_spooldir_on_screen) { #ifndef USE_CLEARSCREEN erase_spooldir_arrow (); #endif cur_spoolnum = 0; show_spooldir_page (); } else { erase_spooldir_arrow (); cur_spoolnum = 0; draw_spooldir_arrow (); } } break; case KEYMAP_END: goto end_of_list; case KEYMAP_MOUSE: if (xrow < INDEX_TOP) { goto spooldir_page_up; } if (xrow >= INDEX_TOP+last_spooldir_on_screen-first_spooldir_on_screen) { goto spooldir_page_down; } erase_spooldir_arrow (); cur_spoolnum = xrow-INDEX_TOP+first_spooldir_on_screen; draw_spooldir_arrow (); if (xmouse > 0) { goto select_spooldir; } break; } break; case '$': /* show last page of spooldirs */ end_of_list: if (cur_spoolnum != spool_top - 1) { if (spool_top - 1 > last_spooldir_on_screen) { #ifndef USE_CLEARSCREEN erase_spooldir_arrow (); #endif cur_spoolnum = spool_top - 1; show_spooldir_page (); } else { erase_spooldir_arrow (); cur_spoolnum = spool_top - 1; draw_spooldir_arrow (); } } break; case '\r': /* select spooldir */ case '\n': select_spooldir: if (set_spooldir (spooldirs[cur_spoolnum].name)) { wait_message (txt_reading_news_active_file); free_active_arrays (); max_active = DEFAULT_ACTIVE_NUM; expand_active (); read_mail_active_file (); read_news_active_file (); read_attributes_file (); read_newsgroups_file (); if (! read_cmd_line_groups ()) { read_newsrc (TRUE); toggle_my_groups (show_only_unread_groups, ""); } set_groupname_len (FALSE); set_xclick_off (); return TRUE; } break; case ' ': /* page down */ case ctrl('D'): case ctrl('F'): /* vi style */ spooldir_page_down: if (cur_spoolnum == spool_top - 1) { if (0 < first_spooldir_on_screen) { # ifndef USE_CLEARSCREEN erase_spooldir_arrow (); # endif cur_spoolnum = 0; show_spooldir_page (); } else { erase_spooldir_arrow (); cur_spoolnum = 0; draw_spooldir_arrow (); } break; } erase_spooldir_arrow (); scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2); cur_spoolnum = ((cur_spoolnum + scroll_lines) / scroll_lines) * scroll_lines; if (cur_spoolnum >= spool_top) { cur_spoolnum = (spool_top / scroll_lines) * scroll_lines; if (cur_spoolnum < spool_top - 1) { cur_spoolnum = spool_top - 1; } } if (cur_spoolnum <= first_spooldir_on_screen || cur_spoolnum >= last_spooldir_on_screen) show_spooldir_page (); else draw_spooldir_arrow (); break; case ctrl('L'): /* redraw */ #ifndef USE_CLEARSCREEN ClearScreen (); #endif set_xclick_off (); show_spooldir_page (); break; case ctrl('N'): /* line down */ case 'j': spooldir_down: if (cur_spoolnum + 1 >= spool_top) { if (0 < first_spooldir_on_screen) { # ifndef USE_CLEARSCREEN erase_spooldir_arrow (); # endif cur_spoolnum = 0; show_spooldir_page (); } else { erase_spooldir_arrow (); cur_spoolnum = 0; draw_spooldir_arrow (); } break; } if (cur_spoolnum + 1 >= last_spooldir_on_screen) { #ifndef USE_CLEARSCREEN erase_spooldir_arrow (); #endif cur_spoolnum++; show_spooldir_page (); } else { erase_spooldir_arrow (); cur_spoolnum++; draw_spooldir_arrow (); } break; case ctrl('P'): /* line up */ case 'k': spooldir_up: if (cur_spoolnum == 0) { if (_hp_glitch) { erase_spooldir_arrow (); } if (spool_top > last_spooldir_on_screen) { cur_spoolnum = spool_top - 1; show_spooldir_page (); } else { erase_spooldir_arrow (); cur_spoolnum = spool_top - 1; draw_spooldir_arrow (); } break; } if (_hp_glitch) { erase_spooldir_arrow (); } if (cur_spoolnum <= first_spooldir_on_screen) { cur_spoolnum--; show_spooldir_page (); } else { erase_spooldir_arrow (); cur_spoolnum--; draw_spooldir_arrow (); } break; case 'b': /* page up */ case ctrl('U'): case ctrl('B'): /* vi style */ spooldir_page_up: if (cur_spoolnum == 0) { if (_hp_glitch) { erase_spooldir_arrow (); } if (spool_top > last_spooldir_on_screen) { cur_spoolnum = spool_top - 1; show_spooldir_page (); } else { erase_spooldir_arrow (); cur_spoolnum = spool_top - 1; draw_spooldir_arrow (); } break; } erase_spooldir_arrow (); scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2); if ((n = cur_spoolnum % scroll_lines) > 0) { cur_spoolnum = cur_spoolnum - n; } else { cur_spoolnum = ((cur_spoolnum - scroll_lines) / scroll_lines) * scroll_lines; } if (cur_spoolnum < 0) { cur_spoolnum = 0; } if (cur_spoolnum < first_spooldir_on_screen || cur_spoolnum >= last_spooldir_on_screen) show_spooldir_page (); else draw_spooldir_arrow (); break; case 'h': /* help */ show_info_page (HELP_INFO, help_spooldir, txt_spooldir_com); show_spooldir_page (); break; case 'H': /* toggle mini help menu */ toggle_mini_help (SPOOLDIR_LEVEL); show_spooldir_page(); break; case 'I': /* toggle inverse video */ erase_spooldir_arrow (); toggle_inverse_video (); show_spooldir_page (); break; case 'q': /* quit */ set_xclick_off (); return TRUE; case 'Q': /* quit */ write_rcfile (); tin_done (0); break; case 'R': /* bug/gripe/comment mailed to author */ mail_bug_report (); #ifndef USE_CLEARSCREEN ClearScreen (); #endif show_spooldir_page (); break; case 'v': /* show tin version */ info_message (cvers); break; default: info_message(txt_bad_command); } } #endif /* INDEX_DAEMON */ } void show_spooldir_page () { #ifndef INDEX_DAEMON char buf[PATH_LEN]; int i, j; int spoolname_len; set_signals_spooldir (); #ifdef USE_CLEARSCREEN ClearScreen (); #else MoveCursor (0, 0); /* top left corner */ CleartoEOLN (); #endif sprintf (buf, txt_spooldir_selection, num_spooldir); show_title (buf); #ifndef USE_CLEARSCREEN MoveCursor (1, 0); CleartoEOLN (); #endif MoveCursor (INDEX_TOP, 0); if (cur_spoolnum >= spool_top) { cur_spoolnum = spool_top - 1; } if (cur_spoolnum < 0) { cur_spoolnum = 0; } if (NOTESLINES <= 0) { first_spooldir_on_screen = 0; } else { first_spooldir_on_screen = (cur_spoolnum / NOTESLINES) * NOTESLINES; if (first_spooldir_on_screen < 0) { first_spooldir_on_screen = 0; } } last_spooldir_on_screen = first_spooldir_on_screen + NOTESLINES; if (last_spooldir_on_screen >= spool_top) { last_spooldir_on_screen = spool_top; first_spooldir_on_screen = (cur_spoolnum / NOTESLINES) * NOTESLINES; if (first_spooldir_on_screen == last_spooldir_on_screen || first_spooldir_on_screen < 0) { if (first_spooldir_on_screen < 0) { first_spooldir_on_screen = 0; } else { first_spooldir_on_screen = last_spooldir_on_screen - NOTESLINES; } } } if (spool_top == 0) { first_spooldir_on_screen = 0; last_spooldir_on_screen = 0; } spoolname_len = cCOLS - 11; for (j=0, i = first_spooldir_on_screen; i < last_spooldir_on_screen; i++,j++) { sprintf (buf, "%-16.16s %s", spooldirs[i].name, spooldirs[i].comment); sprintf (screen[j].col, " %4.d %-*.*s\r\n", i+1, spoolname_len, spoolname_len, buf); if (slow_speed_terminal) { strip_line (screen[j].col, strlen (screen[j].col)); strcat (screen[j].col, "\r\n"); } fputs (screen[j].col, stdout); } #ifndef USE_CLEARSCREEN CleartoEOS (); #endif show_mini_help (SPOOLDIR_LEVEL); draw_spooldir_arrow (); #endif /* INDEX_DAEMON */ } int prompt_spooldir_num (ch) int ch; { int num; clear_message (); if ((num = prompt_num (ch, txt_select_spooldir)) == -1) { clear_message (); return FALSE; } num--; /* index from 0 (internal) vs. 1 (user) */ if (num < 0) { num = 0; } if (num >= spool_top) { num = spool_top - 1; } if (num >= first_spooldir_on_screen && num < last_spooldir_on_screen) { erase_spooldir_arrow (); cur_spoolnum = num; draw_spooldir_arrow (); } else { #ifndef USE_CLEARSCREEN erase_spooldir_arrow (); #endif cur_spoolnum = num; show_spooldir_page (); } return TRUE; } void erase_spooldir_arrow () { erase_arrow (INDEX_TOP + (cur_spoolnum-first_spooldir_on_screen)); } void draw_spooldir_arrow () { draw_arrow (INDEX_TOP + (cur_spoolnum-first_spooldir_on_screen)); } /* * Load all spooldirs into spooldir[] array */ int load_spooldirs () { char comment[PATH_LEN]; char line[NNTP_STRLEN]; char name[PATH_LEN]; char *ptr; int i, state; for (i = 0 ; i < max_spooldir ; i++) { spooldirs[i].state = 0; spooldirs[i].name = (char *) 0; spooldirs[i].comment = (char *) 0; } num_spooldir = 0; xspooldir_supported = FALSE; if (! read_news_via_nntp) { return (xspooldir_supported); } put_server ("spooldir list"); (void) get_server (line, NNTP_STRLEN); if (*line != CHAR_OK) { xspooldir_supported = FALSE; if (atoi (line) != ERR_COMMAND) { fprintf (stderr, "%s", line); fprintf (stderr, txt_spooldir_server_error_1); fprintf (stderr, txt_spooldir_server_error_2); } return (xspooldir_supported); } if (debug == 1) { wait_message (line); } xspooldir_supported = TRUE; do { get_server (line, NNTP_STRLEN); if (line[0] != '.') { if (debug != 0) { printf ("%s\n", line); } state = atoi (line); if ((ptr = strchr (line, ' ')) != (char *) 0) { strncpy (name, ++ptr, sizeof (name)); ptr = strchr (name, ' '); *ptr = '\0'; } if ((ptr = strchr (line, '[')) != (char *) 0) { strncpy (comment, ++ptr, sizeof (comment)); ptr = strchr (comment, ']'); *ptr = '\0'; } if (num_spooldir >= max_spooldir - 1) { expand_spooldirs (); } spooldirs[num_spooldir].state = state; spooldirs[num_spooldir].name = str_dup (name); spooldirs[num_spooldir].comment = str_dup (comment); if (debug != 0) { printf ("NUM=[%d] MAX=[%d] STATE=[%d] ALIAS=[%s] COMMENT=[%s]\n", num_spooldir, max_spooldir, spooldirs[num_spooldir].state, spooldirs[num_spooldir].name, spooldirs[num_spooldir].comment); } num_spooldir++; } } while (!((line[0] == '.') && ((line[1] == '\0') || (line[1] == '\r')))); return (xspooldir_supported); } /* * Need to select a spooldir directory for reading news from and store all * spooldir's in an array for later use when changing spooldir's */ void get_spooldir () { #ifdef NNTP_ABLE char default_alias[32]; int i, set_alias = FALSE; default_alias[0] = '\0'; if (! load_spooldirs ()) { if (! xspooldir_supported) { strcpy (spooldir_alias, "news"); set_tindir (); } return; } /* * default to current spooldir from last session or 1st in spooldirs[] */ if (spooldir_alias[0]) { my_strncpy (default_alias, spooldir_alias, sizeof (default_alias)); } else { my_strncpy (default_alias, spooldirs[0].name, sizeof (default_alias)); } /* * Try to use default spooldir. If that fails go through spooldir list * looking for first available spooldir. */ if (! set_spooldir (spooldir_alias)) { for (i = 0 ; spooldirs[i].name != (char *) 0 ; i++) { if (set_spooldir (spooldirs[i].name)) { set_alias = TRUE; break; } } if (! set_alias) { error_message (txt_cannot_change_spooldir, ""); exit (1); } } /* * And now set tin to act as though it is reading via NNTP */ read_news_via_nntp = TRUE; #endif /* NNTP_ABLE */ } /* * Change to specified spooldir if everythings OK. */ int set_spooldir (name) char *name; { char line[NNTP_STRLEN]; int respcode; if (cmd_line) { sprintf (line, "%s %s...\n", txt_changing_spooldir_to, name); } else { sprintf (line, "%s %s...", txt_changing_spooldir_to, name); } wait_message (line); sprintf (line, "spooldir %s", name); debug_nntp ("set_spooldir", line); put_server (line); respcode = get_respcode (); switch (respcode) { case OK_SPSWITCH: /* Switching to a different spooldir */ my_strncpy (spooldir_alias, name, sizeof (spooldir_alias)); set_tindir (); return TRUE; case OK_SPNOCHANGE: /* Still using same spooldir */ clear_message (); break; default: error_message ("%s", nntp_respcode (respcode)); clear_message (); break; } return FALSE; } P%ѭo#~ TIN-1_22.BCKd[SRC.TIN-1_22]STPWATCH.H;1B*[SRC.TIN-1_22]STPWATCH.H;1+,.// 4-d0@123KPWO56 t7Fg189]VG/HJ/* * Project : tin - a Usenet reader * Module : stpwatch.h * Author : I.Lea * Created : 03-08-93 * Updated : 03-08-93 * Notes : Simple stopwatch routines for timing code using timeb struct * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #ifdef PROFILE #include char msg_tb[1024]; char tmp_tb[1024]; struct timeb beg_tb; struct timeb end_tb; #define LSECS 700000000 #define BegStopWatch(msg) \ { \ strcpy (msg_tb, msg); \ ftime (&beg_tb); \ } #define EndStopWatch() \ { \ ftime (&end_tb); \ } #define PrintStopWatch() \ { \ sprintf (tmp_tb, "%s: Beg=[%ld.%d] End=[%ld.%d] Elapsed=[%d]", \ msg_tb, beg_tb.time, beg_tb.millitm, \ end_tb.time, end_tb.millitm, \ (((end_tb.time - LSECS) * 1000) + end_tb.millitm) - \ (((beg_tb.time - LSECS) * 1000) + beg_tb.millitm)); \ error_message (tmp_tb, ""); \ } #else #define BegStopWatch(msg) #define EndStopWatch() #define PrintStopWatch() #endif /* PROFILE */ *[SRC.TIN-1_22]STRFTIME.3;1+,r. // 4 p-d0@123KPWO 56 et7`189]VG/HJ.TH STRFTIME 3 .SH NAME strftime \- generate formatted time information .SH SYNOPSIS .ft B .nf #include #include .sp size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr); .SH DESCRIPTION The following description is transcribed verbatim from the December 7, 1988 draft standard for ANSI C. This draft is essentially identical in technical content to the final version of the standard. .LP The .B strftime function places characters into the array pointed to by .B s as controlled by the string pointed to by .BR format . The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The .B format string consists of zero or more conversion specifiers and ordinary multibyte characters. A conversion specifier consists of a .B % character followed by a character that determines the behavior of the conversion specifier. All ordinary multibyte characters (including the terminating null character) are copied unchanged into the array. If copying takes place between objects that overlap the behavior is undefined. No more than .B maxsize characters are placed into the array. Each conversion specifier is replaced by appropriate characters as described in the following list. The appropriate characters are determined by the .B LC_TIME category of the current locale and by the values contained in the structure pointed to by .BR timeptr . .TP .B %a is replaced by the locale's abbreviated weekday name. .TP .B %A is replaced by the locale's full weekday name. .TP .B %b is replaced by the locale's abbreviated month name. .TP .B %B is replaced by the locale's full month name. .TP .B %c is replaced by the locale's appropriate date and time representation. .TP .B %d is replaced by the day of the month as a decimal number .RB ( 01 - 31 ). .TP .B %H is replaced by the hour (24-hour clock) as a decimal number .RB ( 00 - 23 ). .TP .B %I is replaced by the hour (12-hour clock) as a decimal number .RB ( 01 - 12 ). .TP .B %j is replaced by the day of the year as a decimal number .RB ( 001 - 366 ). .TP .B %m is replaced by the month as a decimal number .RB ( 01 - 12 ). .TP .B %M is replaced by the minute as a decimal number .RB ( 00 - 59 ). .TP .B %p is replaced by the locale's equivalent of the AM/PM designations associated with a 12-hour clock. .TP .B %S is replaced by the second as a decimal number .RB ( 00 - 61 ). .TP .B %U is replaced by the week number of the year (the first Sunday as the first day of week 1) as a decimal number .RB ( 00 - 53 ). .TP .B %w is replaced by the weekday as a decimal number .RB [ "0 " (Sunday)- 6 ]. .TP .B %W is replaced by the week number of the year (the first Monday as the first day of week 1) as a decimal number .RB ( 00 - 53 ). .TP .B %x is replaced by the locale's appropriate date representation. .TP .B %X is replaced by the locale's appropriate time representation. .TP .B %y is replaced by the year without century as a decimal number .RB ( 00 - 99 ). .TP .B %Y is replaced by the year with century as a decimal number. .TP .B %z is replaced by the time zone name or abbreviation, or by no characters if no time zone is determinable. .TP .B %% is replaced by .BR % . .LP If a conversion specifier is not one of the above, the behavior is undefined. .SH RETURNS If the total number of resulting characters including the terminating null character is not more than .BR maxsize , the .B strftime function returns the number of characters placed into the array pointed to by .B s not including the terminating null character. Otherwise, zero is returned and the contents of the array are indeterminate. .SH NON-ANSI EXTENSIONS If .B SYSV_EXT is defined when the routine is compiled, then the following additional conversions will be available. These are borrowed from the System V .IR cftime (3) and .IR ascftime (3) routines. .TP .B %D is equivalent to specifying .BR %m/%d/%y . .TP .B %e is replaced by the day of the month, padded with a blank if it is only one digit. .TP .B %h is equivalent to .BR %b , above. .TP .B %n is replaced with a newline character (\s-1ASCII LF\s+1). .TP .B %r is equivalent to specifying .BR "%I:%M:%S %p" . .TP .B %R is equivalent to specifying .BR %H:%M: . .TP .B %T is equivalent to specifying .BR %H:%M:%S . .TP .B %t is replaced with a \s-1TAB\s+1 character. .SH SEE ALSO time(2), ctime(3), localtime(3) .SH BUGS This version does not handle multibyte characters or pay attention to the setting of the .B LC_TIME environment variable. .LP It is not clear what is ``appropriate'' for the C locale; the values returned are a best guess on the author's part. .SH AUTHOR .nf Arnold Robbins AudioFAX, Inc. Suite 200 2000 Powers Ferry Road Marietta, GA. 30067 U.S.A. INTERNET: arnold@audiofax.com UUCP: emory!audfax!arnold Phone: +1 404 933 7600 Fax-box: +1 404 618 4581 .fi .SH ACKNOWLEDGEMENTS Thanks to Geoff Clare for helping debug earlier versions of this routine. *[SRC.TIN-1_22]STRFTIME.C;1+,. // 4 -d0@123KPWO 56!t7u189]VG/HJ/* * Project : tin - a Usenet reader * Module : strftime.c * Author : A.Robbins & I.Lea * Created : 01-02-91 * Updated : 15-08-93 * Notes : Relatively quick-and-dirty implemenation of ANSI library * routine for System V Unix systems. * It's written in old-style C for maximal portability. * If target system already has strftime() call the #define * HAVE_STRFTIME can be set to use it. * Example : time (&secs); * tm = localtime (&secs); * num = strftime (buf, sizeof (buf), "%a %d-%m-%y %H:%M:%S", tm); * Copyright : (c) Copyright 1991-93 by Arnold Robbins & Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #ifdef SYSV extern int daylight; #endif #define SYSV_EXT 1 /* stuff in System V ascftime routine */ /* * strftime --- produce formatted time */ #if __STDC__ size_t my_strftime (char *s, size_t maxsize, char *format, struct tm *timeptr) #else size_t my_strftime (s, maxsize, format, timeptr) char *s; size_t maxsize; char *format; struct tm *timeptr; #endif { #ifdef HAVE_STRFTIME return strftime (s, maxsize, format, timeptr); #else char *endp = s + maxsize; char *start = s; char tbuf[100]; int i; /* * various tables, useful in North America */ static char *days_a[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; static char *days_l[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; static char *months_a[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; static char *months_l[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; static char *ampm[] = { "AM", "PM", }; if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0) return 0; if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) return 0; #ifndef DONT_HAVE_TZSET tzset (); #endif #ifdef HAVE_SETTZ settz (); #endif for (; *format && s < endp - 1; format++) { tbuf[0] = '\0'; if (*format != '%') { *s++ = *format; continue; } switch (*++format) { case '\0': *s++ = '%'; goto out; case '%': *s++ = '%'; continue; case 'a': /* abbreviated weekday name */ strcpy(tbuf, days_a[timeptr->tm_wday]); break; case 'A': /* full weekday name */ strcpy(tbuf, days_l[timeptr->tm_wday]); break; #ifdef SYSV_EXT case 'h': /* abbreviated month name */ #endif case 'b': /* abbreviated month name */ strcpy(tbuf, months_a[timeptr->tm_mon]); break; case 'B': /* full month name */ strcpy(tbuf, months_l[timeptr->tm_mon]); break; case 'c': /* appropriate date and time representation */ sprintf(tbuf, "%s %s %2d %02d:%02d:%02d %d", days_a[timeptr->tm_wday], months_a[timeptr->tm_mon], timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, timeptr->tm_year + 1900); break; case 'd': /* day of the month, 01 - 31 */ sprintf(tbuf, "%02d", timeptr->tm_mday); break; case 'H': /* hour, 24-hour clock, 00 - 23 */ sprintf(tbuf, "%02d", timeptr->tm_hour); break; case 'I': /* hour, 12-hour clock, 01 - 12 */ i = timeptr->tm_hour; if (i == 0) i = 12; else if (i > 12) i -= 12; sprintf(tbuf, "%02d", i); break; case 'j': /* day of the year, 001 - 366 */ sprintf(tbuf, "%03d", timeptr->tm_yday + 1); break; case 'm': /* month, 01 - 12 */ sprintf(tbuf, "%02d", timeptr->tm_mon + 1); break; case 'M': /* minute, 00 - 59 */ sprintf(tbuf, "%02d", timeptr->tm_min); break; case 'p': /* am or pm based on 12-hour clock */ if (timeptr->tm_hour < 12) strcpy(tbuf, ampm[0]); else strcpy(tbuf, ampm[1]); break; case 'S': /* second, 00 - 61 */ sprintf(tbuf, "%02d", timeptr->tm_sec); break; case 'w': /* weekday, Sunday == 0, 0 - 6 */ sprintf(tbuf, "%d", timeptr->tm_wday); break; case 'x': /* appropriate date representation */ sprintf(tbuf, "%s %s %2d %d", days_a[timeptr->tm_wday], months_a[timeptr->tm_mon], timeptr->tm_mday, timeptr->tm_year + 1900); break; case 'X': /* appropriate time representation */ sprintf(tbuf, "%02d:%02d:%02d", timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); break; case 'y': /* year without a century, 00 - 99 */ i = timeptr->tm_year % 100; sprintf(tbuf, "%d", i); break; case 'Y': /* year with century */ sprintf(tbuf, "%d", 1900 + timeptr->tm_year); break; #ifdef SYSV_EXT case 'n': /* same as \n */ tbuf[0] = '\n'; tbuf[1] = '\0'; break; case 't': /* same as \t */ tbuf[0] = '\t'; tbuf[1] = '\0'; break; case 'D': /* date as %m/%d/%y */ my_strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr); break; case 'e': /* day of month, blank padded */ sprintf(tbuf, "%2d", timeptr->tm_mday); break; case 'r': /* time as %I:%M:%S %p */ my_strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr); break; case 'R': /* time as %H:%M */ my_strftime(tbuf, sizeof tbuf, "%H:%M", timeptr); break; case 'T': /* time as %H:%M:%S */ my_strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr); break; #endif default: tbuf[0] = '%'; tbuf[1] = *format; tbuf[2] = '\0'; break; } i = strlen(tbuf); if (i) if (s + i < endp - 1) { strcpy(s, tbuf); s += i; } else return 0; } out: if (s < endp && *format == '\0') { *s = '\0'; return (size_t) (s - start); } else return 0; #endif /* HAVE_STRFTIME */ } *[SRC.TIN-1_22]TAGS.;1+,.!// 4! J-d0123KPWO"56 Phj7",yj89]VG/HJ* $1$dua4:[local.src.tin-1_22]active.c,715 backup_active 395,7830 check_for_any_new_groups 428,8407 cmp_group_p 28,780 cmp_notify_p 40,982 find_active_size_index 1297,30039 find_group_index 109,2329 get_active_num 54,1236 if 967,21654 if 979,21995 if 989,22262 if 999,22539 if 1008,22797 if 1017,23045 load_active_size_info 1249,29010 match_group_list 821,17775 my_strpbrk 1403,32034 parse_active_line 137,2694 parse_newsrc_active_line 192,3529 prompt_subscribe_group 758,16546 read_active_times_file 1173,27768 read_attributes_file 935,20988 read_motd_file 1320,30488 read_news_active_file 263,4771 resync_active_file 75,1592 set_default_attributes 880,18958 write_active_times_file 1226,28653 write_attributes_file 1087,24543 $1$dua4:[local.src.tin-1_22]actived.c,13 main 23,849 $1$dua4:[local.src.tin-1_22]amiga.c,290 chmod 43,975 closedir 205,3063 getenv 314,4895 getopt 213,3131 getpid 55,1101 joinpath 105,1733 make_post_cmd 357,5615 opendir 169,2581 pclose 158,2497 popen 137,2162 readdir 190,2862 setenv 345,5429 sleep 126,2016 stat 282,4165 system 265,3820 tputs 66,1211 tzset 89,1436 $1$dua4:[local.src.tin-1_22]art.c,453 artnum_comp 1480,31371 date_comp 1545,32811 do_update 1386,29243 find_base 34,930 find_index_file 1324,28085 from_comp 1525,32281 index_group 127,2640 input_pending 1607,33868 make_threads 392,8016 num_of_arts 81,1747 parse_headers 483,10190 purge_needed 102,2076 read_group 244,4881 read_xindex_file 837,18442 read_xover_file 1134,24038 set_article 1586,33463 subj_comp 1505,31739 valid_artnum 1659,34664 write_xindex_file 638,13959 $1$dua4:[local.src.tin-1_22]curses.c,521 ClearScreen 470,9263 CleartoEOLN 529,10072 CleartoEOS 544,10265 EndInverse 585,10804 EndWin 422,8612 InitScreen 160,3576 InitScreen 256,5910 InitWin 406,8421 MoveCursor 489,9545 MoveCursor 507,9789 Raw 629,11312 RawState(619,11209 ReadCh 701,13099 ReadCh 762,14067 ScreenSize 390,8069 StartInverse 568,10586 ToggleInverse 602,11023 int 30,791 outchar 808,14802 set_keypad_off 453,9005 set_keypad_on 439,8804 set_xclick_off 852,15375 set_xclick_on 842,15281 setup_screen 141,3317 xclick 819,14924 $1$dua4:[local.src.tin-1_22]debug.c,247 debug_nntp 24,584 debug_nntp_respcode 47,887 debug_print_active 218,4045 debug_print_active_hash 266,5674 debug_print_arts 60,1062 debug_print_base 193,3702 debug_print_comment 170,3393 debug_print_header 76,1237 debug_save_comp 126,2458 $1$dua4:[local.src.tin-1_22]envarg.c,50 count_args 19,580 envargs 41,828 main 118,2331 $1$dua4:[local.src.tin-1_22]feed.c,105 does_article_exist 655,15980 feed_articles 35,1215 get_post_proc_type 611,15161 print_file 573,14293 $1$dua4:[local.src.tin-1_22]getline.c,324 getline 90,2469 getline 93,2533 gl_addchar 210,4694 gl_addchar 213,4732 gl_del 266,5792 gl_del 269,5828 gl_fixup 326,6967 gl_fixup 329,7020 gl_kill 289,6144 gl_newline 237,5153 gl_redraw 304,6354 gl_tab 428,9803 gl_tab 431,9862 hist_add 465,10311 hist_init 455,10210 hist_next 512,11149 hist_prev 487,10752 $1$dua4:[local.src.tin-1_22]group.c,546 #define ART_ADJUST(42,1092 #define INDEX2LNUM(49,1322 #define INDEX2SNUM(47,1242 #define SNUM2LNUM(48,1283 bld_sline 1453,32347 clear_note_area 1306,29570 decr_tagged 52,1378 draw_sline 1514,33789 draw_subject_arrow 1230,28463 erase_subject_arrow 1246,28711 find_new_pos 1322,29903 fix_new_highest 1107,26491 group_page 66,1575 mark_screen 1351,30326 prompt_subject_num 1263,28941 set_subj_from_size 1380,30826 show_group_page 1124,26691 show_group_title 1560,34586 toggle_subject_from 1415,31455 update_group_page 1211,28200 $1$dua4:[local.src.tin-1_22]hashstr.c,79 add_string 79,1659 hash_init 97,1987 hash_reclaim 108,2107 hash_str 37,982 $1$dua4:[local.src.tin-1_22]help.c,105 display_info_page 365,5862 show_info_page 216,3443 show_mini_help 400,6641 toggle_mini_help 442,7639 $1$dua4:[local.src.tin-1_22]inews.c,115 get_from_name 289,6300 get_host_name 129,2731 get_user_info 240,5273 submit_file 381,8520 submit_inews 27,666 $1$dua4:[local.src.tin-1_22]init.c,150 char *GetConfigValue 656,19885 char *GetFQDN 648,19814 int create_mail_save_dirs 615,19154 void init_selfinfo 168,7321 void set_tindir 557,17762 $1$dua4:[local.src.tin-1_22]kill.c,309 #define IS_KILLED(26,799 #define IS_READ(25,750 #define SET_HOT(24,712 #define SET_KILLED(23,641 auto_select_articles 618,13805 get_choice 185,4169 get_choice 195,4288 kill_any_articles 476,10433 kill_art_menu 242,5004 read_kill_file 43,1066 unkill_all_articles 454,10121 write_kill_file 138,2933 $1$dua4:[local.src.tin-1_22]lang.c,0 $1$dua4:[local.src.tin-1_22]mail.c,160 read_groups_descriptions 220,4994 read_mail_active_file 22,621 read_mailgroups_file 150,3599 read_newsgroups_file 180,4169 write_mail_active_file 117,2859 $1$dua4:[local.src.tin-1_22]main.c,253 int check_for_any_new_news 395,9294 void main 26,656 int read_cmd_line_groups 546,13628 void read_cmd_line_options 181,3671 void save_or_mail_new_news 422,9816 void show_intro_page 507,11933 void update_index_files 444,10200 void usage 338,6833 $1$dua4:[local.src.tin-1_22]memory.c,465 expand_active 110,2958 expand_active_size 156,4021 expand_art 100,2710 expand_kill 126,3445 expand_save 136,3627 expand_spooldirs 146,3807 free_active_arrays 319,7732 free_active_size_array 437,10089 free_all_arrays 195,4806 free_art_array 250,5840 free_attributes_array 280,6430 free_kill_array 357,8561 free_save_array 377,8898 free_spooldirs_array 416,9710 init_alloc 54,1638 init_screen_array 166,4256 my_malloc 457,10475 my_realloc 471,10663 $1$dua4:[local.src.tin-1_22]misc.c,1124 #define FOLD_TO_UPPER(822,15670 void asfail 21,604 void base_name 581,11196 void cleanup_tmp_files 1637,30831 void copy_fp 54,1150 void create_index_lock_file 1143,21358 void draw_percent_mark 465,8719 char *eat_re 844,16079 int get_arrow_key 1031,19315 void get_author 973,18288 void get_cwd 1604,30480 char *get_val 80,1620 unsigned long hash_groupname 316,5997 long hash_s 871,16560 int invoke_cmd 425,8218 int invoke_editor 92,1853 int invoke_ispell 132,2664 int mail_check 622,11831 void mail_setup 605,11594 void make_group_path 1615,30593 void make_post_process_cmd 1649,31026 long my_atol 797,15404 int my_chdir 296,5753 int my_mkdir 274,5443 int my_stricmp 824,15746 void my_strncpy 887,16755 void parse_from 642,12161 void rename_file 353,6685 void rename_file 396,7757 void set_real_uid_gid 489,9179 void set_tin_uid_gid 535,10199 void shell_escape 160,3155 char *str_dup 412,8069 char *str_str 922,17181 int strfeditor 1305,24648 int strfpath 1410,26599 int strfquote 1183,22287 void tin_done 226,4295 void toggle_inverse_video 1016,19060 int untag_all_articles 901,16896 $1$dua4:[local.src.tin-1_22]newsrc.c,469 auto_subscribe_groups 24,724 backup_newsrc 57,1337 checknewsrc 252,5552 delete_group 571,12079 get_line_unread 1014,21246 mark_group_read 757,15422 parse_seq 818,16521 parse_unread 854,17220 pos_group_in_newsrc 1170,24676 print_newsrc_seq 1044,21743 print_seq 1090,22769 read_newsrc 84,1998 read_newsrc_line 292,6597 reset_newsrc 521,11080 rewrite_newsrc 195,4235 subscribe 449,9814 undel_group 629,12992 update_newsrc 353,7767 write_newsrc 172,3906 $1$dua4:[local.src.tin-1_22]nntplib.c,269 void close_server 718,17117 int get_dnet_socket 485,11888 int get_server 663,15969 int get_tcp_socket 238,5554 char *getserverbyfile 102,2578 int handle_server_response 562,13596 char *nntp_respcode 738,17442 void put_server 612,14797 int server_init 162,3774 $1$dua4:[local.src.tin-1_22]open.c,571 authorization 954,18582 base_comp 665,13293 get_respcode 798,15874 log_user 897,17450 nntp_close 149,3193 nntp_open 36,907 nntp_to_fp 869,17016 open_art_fp 581,11786 open_art_header 476,9638 open_mail_active_fp 164,3374 open_mailgroups_fp 323,6462 open_motd_fp 273,5494 open_newgroups_fp 238,4805 open_news_active_fp 175,3545 open_newsgroups_fp 336,6774 open_overview_fmt_fp 203,4101 open_subscription_fp 299,6023 open_xhdr_fp 630,12728 open_xindex_fp 365,7371 open_xover_fp 405,8164 setup_base 686,13604 stat_article 443,9006 stuff_nntp 818,16149 $1$dua4:[local.src.tin-1_22]os_2.c,409 backslash 69,1101 closedir 141,1874 endpwent 344,4486 fakepwent 250,3138 fgetpwent 355,4586 gethostname 221,2835 getlogin 444,6017 getopt 150,1942 getpwent 279,3749 getpwnam 316,4223 getpwuid 298,4032 getuid 457,6132 joinpath 86,1344 make_post_cmd 204,2629 opendir 125,1764 pclose 118,1715 popen 109,1645 putpwent 435,5941 readdir 133,1829 setpwent 335,4424 sleep 239,3040 tputs 46,840 $1$dua4:[local.src.tin-1_22]page.c,301 art_close 1087,24693 art_open 998,22461 match_header 1233,27199 prompt_response 1097,24812 redraw_page 620,15191 show_cont_header 958,21818 show_first_header 846,19636 show_last_page 1156,25758 show_mime_article 811,18976 show_note_page 637,15514 show_page 49,1671 yank_to_addr 1115,25053 $1$dua4:[local.src.tin-1_22]parsdate_tab.c,727 #define CTYPE(51,1671 Convert(1345,37109 DSTcorrect(1407,38686 #define ENDOF(49,1625 GetTimeInfo(1596,43115 #define HOUR(68,2076 #define IS7BIT(72,2143 LookupWord(1441,39435 RelativeMonth(1421,39000 #define SIZEOF(48,1563 ToSeconds(1322,36645 #define YYBACKUP(342,11466 #define YYRECOVERING(341,11426 #define YYTRANSLATE(159,3604 #define __yy_bcopy(409,12974 __yy_bcopy 416,13224 __yy_bcopy 434,13576 date_error(1314,36577 date_lex(1533,41675 if 968,24956 if 982,25209 if 1005,25827 if 1063,27103 if 1092,27914 if 1097,28031 if 1105,28150 if 1118,28397 if 1122,28461 if 1126,28566 else if 1133,28673 if 1136,28714 if 1140,28768 parsedate(1657,44686 yyparse(449,13785 $1$dua4:[local.src.tin-1_22]post.c,508 #define PRINT_LF(17,564 check_article_to_be_posted 157,4260 crosspost_article 1569,35812 delete_article 1420,32445 find_mail_header 1354,31164 find_reply_to_addr 1702,38849 insert_x_headers 1668,38044 mail_bug_report 1093,25337 mail_to_author 1244,28745 mail_to_someone 958,22301 post_article 591,13775 post_response 755,17474 quick_post_article 366,8838 reread_active_after_posting 1749,39925 setup_check_article_screen 349,8591 update_art_posted_file 106,2930 user_posted_messages 35,1280 $1$dua4:[local.src.tin-1_22]prompt.c,140 continue_prompt 185,3018 prompt_menu_string 89,1539 prompt_num 24,615 prompt_on_off 151,2445 prompt_string 58,1069 prompt_yn 113,1838 $1$dua4:[local.src.tin-1_22]rcfile.c,295 change_rcfile 415,17357 expand_rel_abs_pathname 942,31852 match_boolean 978,32361 match_number 994,32607 match_string 1010,32818 quote_dash_to_space 1035,33200 quote_space_to_dash 1052,33387 read_rcfile 28,651 show_menu_help 968,32206 show_rcfile_menu 825,28484 write_rcfile 251,7605 $1$dua4:[local.src.tin-1_22]save.c,549 add_to_save_list 628,13903 append_to_existing_file 471,10945 check_start_save_any_news 75,1838 create_path 505,11457 create_sub_dir 589,13331 delete_processed_files 1292,28649 get_archive_file 1250,27945 get_first_savefile 832,18457 get_last_savefile 874,19503 post_process_files 916,20550 post_process_sh 1160,26010 post_process_uud 952,21190 print_art_seperator_line 1319,29084 save_art_to_file 285,7120 save_comp 741,16425 save_filename 789,17305 save_regex_arts 422,10043 save_thread_to_file 363,8771 sort_save_list 729,16213 $1$dua4:[local.src.tin-1_22]screen.c,219 center_line 118,2046 clear_message 108,1951 draw_arrow 145,2373 erase_arrow 164,2644 error_message 48,1006 info_message 26,624 perror_message 70,1322 ring_bell 199,3182 show_title 180,2852 wait_message 38,858 $1$dua4:[local.src.tin-1_22]search.c,181 lcase 547,9592 make_lower 416,7567 search_art_body 509,9032 search_article 303,5502 search_author 43,1014 search_body 434,7716 search_group 130,2613 search_subject 218,4111 $1$dua4:[local.src.tin-1_22]select.c,494 int add_group 986,23282 void catchup_group 1071,24823 int choose_new_group 939,22449 void draw_group_arrow(926,22272 void erase_group_arrow 920,22175 void goto_next_group_on_screen 1268,29039 void group_selection_page 730,18019 void next_unread_group 1092,25354 int prompt_group_num 883,21601 int reposition_group 1022,23941 void selection_index 30,767 void set_groupname_len 1147,26516 void strip_line 1288,29327 void toggle_my_groups 1188,27338 void yank_active_file 932,22366 $1$dua4:[local.src.tin-1_22]sigfile.c,164 # define S_ISDIR(17,606 add_signature 32,800 if 193,4123 open_random_sig 102,2358 if ((dirp = opendir 183,3931 pick = rand 187,4004 thrashdir 133,2875 $1$dua4:[local.src.tin-1_22]signal.c,994 void art_resize 606,9529 void art_suspend 426,7160 void group_resize 677,10572 void group_suspend 506,8211 void help_resize 698,10871 void help_suspend 526,8474 void main_resize 623,9792 void main_suspend 446,7417 void page_resize 714,11104 void page_suspend 546,8734 void rcfile_suspend 586,9261 void select_resize 635,9959 void select_suspend 466,7677 void set_alarm_clock_off 155,3049 void set_alarm_clock_on 147,2952 void set_alarm_signal 129,2620 void set_signal_handlers 69,1383 void set_signals_art 325,5935 void set_signals_group 339,6099 void set_signals_help 353,6269 void set_signals_page 367,6436 void set_signals_select 381,6603 void set_signals_spooldir 395,6776 void set_signals_thread 409,6955 int set_win_size 260,4782 void (*sigdisp(36,860 t_sigtype (*sigdisp(60,1260 void _CDECL signal_handler 163,3151 void spooldir_resize 656,10265 void spooldir_suspend 486,7941 t_sigtype 62,1304 void thread_resize 735,11426 void thread_suspend 566,8994 $1$dua4:[local.src.tin-1_22]spooldir.c,219 draw_spooldir_arrow 477,10462 erase_spooldir_arrow 470,10358 get_spooldir 573,12610 load_spooldirs 487,10616 prompt_spooldir_num 432,9763 set_spooldir 628,13712 show_spooldir_page 342,7855 spooldir_index 27,759 $1$dua4:[local.src.tin-1_22]strftime.c,42 my_strftime 36,1166 my_strftime 39,1251 $1$dua4:[local.src.tin-1_22]thread.c,565 #define INDEX2LNUM(19,591 #define INDEX2TNUM(17,511 #define TNUM2LNUM(18,552 bld_tline 39,1017 choose_response 1043,21793 draw_thread_arrow 753,17183 draw_tline 111,2405 erase_thread_arrow 770,17448 new_responses 822,18266 next_response 977,20935 next_thread 1001,21220 next_unread 1064,22102 num_of_responses 898,19643 prev_response 1019,21483 prev_unread 1096,22636 prompt_thread_num 787,17693 show_thread 162,3327 show_thread_page 649,14909 stat_thread 922,19952 update_thread_page 731,16707 which_response 874,19339 which_thread 851,18985 $1$dua4:[local.src.tin-1_22]vms.c,123 getlogin 8,95 getopt 23,255 make_post_cmd 125,1411 pclose 115,1343 popen 106,1273 tputs 83,1011 void tzset(120,1384 $1$dua4:[local.src.tin-1_22]wildmat.c,72 DoMatch(63,2478 main(159,4307 int wildmat(134,3949 wildmat(137,3992 $1$dua4:[local.src.tin-1_22]win32.c,439 #define ERR(37,818 access 387,5488 chdir 359,5231 chmod 407,5680 closedir 130,2500 fputc 524,7397 fputs 504,7128 getcwd 374,5368 getegid 343,5138 geteuid 349,5170 getgid 317,4998 getopt 436,5963 getpid 302,4769 getuid 323,5029 mkdir 272,4505 opendirx 58,1143 pclose 287,4635 pipe 241,4185 popen 150,2728 printf 486,6877 readdir 93,1749 setgid 329,5060 setuid 336,5099 sleep 230,4103 tputs 418,5756 umask 261,4422 $1$dua4:[local.src.tin-1_22]xindex.c,105 static void find_index_file 108,2771 static unsigned long hash_groupname 149,3395 void xindex 48,1406 $1$dua4:[local.src.tin-1_22]xmotd.c,20 void xmotd(39,1028 $1$dua4:[local.src.tin-1_22]xoverview.c,24 void xoverview 43,1187 $1$dua4:[local.src.tin-1_22]xref.c,58 mark_all_xref_read 54,1208 overview_xref_support 22,630 $1$dua4:[local.src.tin-1_22]xuser.c,19 void xuser 29,865 *[SRC.TIN-1_22]THREAD.C;3+,.-// 4--U-d0@123KPWO.56 789]VG/HJ/* * Project : tin - a Usenet reader * Module : thread.c * Author : I.Lea * Created : 01-04-91 * Updated : 11-07-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #define INDEX2TNUM(i) ((i) % NOTESLINES) #define TNUM2LNUM(i) (INDEX_TOP + (i)) #define INDEX2LNUM(i) (TNUM2LNUM(INDEX2TNUM(i))) #define MARK_OFFSET 8 extern int index_point; int threaded_on_subject; static int top_thread = 0; static int thread_index_point = 0; static int thread_basenote = 0; static int thread_respnum = 0; static int first_thread_on_screen = 0; static int last_thread_on_screen = 0; #if __STDC__ static int bld_tline (int l, int i); #else static int bld_tline (); #endif static int bld_tli&M2 ~ TIN-1_22.BCKd[SRC.TIN-1_22]THREAD.C;3-oVne (l, i) int l; int i; { #ifndef INDEX_DAEMON char mark; char from[LEN]; char new_resps[8]; char lines[8]; char *spaces = "XXX"; int j; int len_from; int len_subj = 0; int off_subj = 0; int off_both = 0; if (! draw_arrow_mark) { off_subj = 2; off_both = 5; } if (threaded_on_subject) { len_from = (max_subj+max_from+off_both) - 8; spaces = ""; } else { if (show_author != SHOW_FROM_NONE) { len_from = max_from - 3; len_subj = (max_subj+off_subj) - 5; spaces = " "; } else { len_from = 0; len_subj = (max_from+max_subj+off_subj) - 5; spaces = ""; } } j = INDEX2TNUM(l); if (arts[i].tagged) { sprintf (new_resps, "%3d", arts[i].tagged); } else { if (arts[i].unread == ART_UNREAD) { mark = (arts[i].hot ? hot_art_mark : unread_art_mark); } else if (arts[i].unread == ART_WILL_RETURN) { mark = return_art_mark; } else { mark = READ_ART_MARK; } sprintf (new_resps, " %c", mark); } from[0] = '\0'; if (threaded_on_subject || show_author != SHOW_FROM_NONE) { get_author (TRUE, i, from); } if (arts[i].lines != -1) { sprintf (lines, "%4d", arts[i].lines); } else { strcpy (lines, " ?"); } sprintf (screen[j].col, " %4d%3s [%s] %-*.*s%s%-*.*s", l, new_resps, lines, len_subj, len_subj, arts[i].subject, spaces, len_from, len_from, from); #endif return(0); } static int draw_tline (i, full) int i; int full; { #ifndef INDEX_DAEMON int j, tlen, x; int k = MARK_OFFSET; char *s; j = INDEX2TNUM(i); if (full) { s = screen[j].col; tlen = strlen (s); x = 0; if (slow_speed_terminal) { strip_line (s, tlen); CleartoEOLN (); } } else { tlen = 3; s = &screen[j].col[6]; x = 6; } MoveCursor(INDEX2LNUM(i), x); #ifdef VMS sys_fwrite (s, tlen, 1, stdout); #else fwrite (s, 1, tlen, stdout); #endif /* it is somewhat less efficient to go back and redo that art mark * if hot, but it is quite readable as to what is happening */ if (screen[j].col[k] == hot_art_mark) { MoveCursor (INDEX2LNUM(i), k); ToggleInverse (); fputc (screen[j].col[k], stdout); ToggleInverse (); } MoveCursor(INDEX2LNUM(i)+1, 0); #endif return(0); } /* * show current thread. If threaded on Subject: show * * If threaded on Archive-name: show * */ int show_thread (respnum, group, group_path) int respnum; char *group; char *group_path; { int ret_code = TRUE; #ifndef INDEX_DAEMON int ch; int i, index, n; int scroll_lines; int flag; thread_respnum = respnum; thread_basenote = which_thread (thread_respnum); top_thread = num_of_responses (thread_basenote) + 1; if (top_thread <= 0) { info_message (txt_no_resps_in_thread); return FALSE; } if (arts[thread_respnum].archive != (char *) 0) { threaded_on_subject = FALSE; } else { threaded_on_subject = TRUE; } thread_index_point = top_thread; if (space_mode) { i = new_responses (thread_basenote); if (i) { for (n=0, i = base[thread_basenote]; i >= 0 ; i = arts[i].thread, n++) { if (arts[i].unread == ART_UNREAD) { if (arts[i].thread == ART_EXPIRED) { arts[i].unread = ART_READ; } else { thread_index_point = n; } break; } } } } if (thread_index_point < 0) { thread_index_point = 0; } show_thread_page (); while (TRUE) { set_xclick_on (); ch = ReadCh (); if (ch >= '0' && ch <= '9') { /* 0 goes to basenote */ if (top_thread == 1) { info_message (txt_no_responses); } else { prompt_thread_num (ch); } continue; } switch (ch) { case ESC: /* common arrow keys */ #ifdef HAVE_KEY_PREFIX case KEY_PREFIX: #endif switch (get_arrow_key ()) { case KEYMAP_UP: goto thread_up; case KEYMAP_DOWN: goto thread_down; case KEYMAP_LEFT: goto thread_done; case KEYMAP_RIGHT: goto thread_read_article; case KEYMAP_PAGE_UP: goto thread_page_up; case KEYMAP_PAGE_DOWN: goto thread_page_down; case KEYMAP_HOME: if (thread_index_point != 0) { if (0 < first_thread_on_screen) { #ifndef USE_CLEARSCREEN erase_thread_arrow (); #endif thread_index_point = 0; show_thread_page (); } else { erase_thread_arrow (); thread_index_point = 0; draw_thread_arrow (); } } break; case KEYMAP_END: goto end_of_thread; case KEYMAP_MOUSE: if (xrow < INDEX2LNUM(first_thread_on_screen)) { goto thread_page_up; } if (xrow > INDEX2LNUM(last_thread_on_screen-1)) { goto thread_page_down; } erase_thread_arrow (); thread_index_point = xrow-INDEX2LNUM(first_thread_on_screen)+first_thread_on_screen; draw_thread_arrow (); if (xmouse == 1) { goto thread_tab_pressed; } if (xmouse == 2) { goto thread_read_article; } break; } break; case '$': /* show last page of threads */ end_of_thread: if (thread_index_point < top_thread - 1) { if (top_thread > last_thread_on_screen) { #ifndef USE_CLEARSCREEN erase_thread_arrow (); #endif thread_index_point = top_thread - 1; show_thread_page (); } else { erase_thread_arrow (); thread_index_point = top_thread - 1; draw_thread_arrow (); } } break; case '\r': case '\n': /* read current article within thread */ thread_read_article: n = choose_response (thread_basenote, thread_index_point); n = show_page (n, &thread_index_point, group, group_path); if (n != -5) { if (n == thread_basenote) { show_thread_page (); } else { index_point = n; goto thread_done; } } break; case '\t': thread_tab_pressed: space_mode = TRUE; if (thread_index_point == 0) { n = thread_respnum; } else { n = choose_response (thread_basenote, thread_index_point); } index = thread_index_point; for (i = n ; i != -1 ; i = arts[i].thread) { if (arts[i].unread == ART_UNREAD) { n = show_page (i, &thread_index_point, group, group_path); break; } index++; } if (n == -5) { goto thread_tab_pressed; } else { if (n == thread_basenote) { show_thread_page (); } else { index_point = n; goto thread_done; } } break; case ' ': /* page down */ case ctrl('D'): case ctrl('F'): /* vi style */ thread_page_down: if (thread_index_point + 1 == top_thread) { if (0 < first_thread_on_screen) { # ifndef USE_CLEARSCREEN erase_thread_arrow (); # endif thread_index_point = 0; show_thread_page (); } else { erase_thread_arrow (); thread_index_point = 0; draw_thread_arrow (); } break; } erase_thread_arrow (); scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2); thread_index_point = ((thread_index_point + scroll_lines) / scroll_lines) * scroll_lines; if (thread_index_point >= top_thread) { thread_index_point = (top_thread / scroll_lines) * scroll_lines; if (thread_index_point < top_thread - 1) { thread_index_point = top_thread - 1; } } if (thread_index_point < first_thread_on_screen || thread_index_point >= last_thread_on_screen) { show_thread_page (); } else { draw_thread_arrow (); } break; case ctrl('L'): /* redraw screen */ case ctrl('R'): case ctrl('W'): #ifndef USE_CLEARSCREEN ClearScreen (); #endif set_xclick_off (); show_thread_page (); break; case ctrl('N'): case 'j': /* line down */ thread_down: if (thread_index_point + 1 >= top_thread) { if (_hp_glitch) { erase_thread_arrow (); } if (0 < first_thread_on_screen) { thread_index_point = 0; show_thread_page (); } else { erase_thread_arrow (); thread_index_point = 0; draw_thread_arrow (); } break; } if (thread_index_point + 1 >= last_thread_on_screen) { #ifndef USE_CLEARSCREEN erase_thread_arrow (); #endif thread_index_point++; show_thread_page (); } else { erase_thread_arrow (); thread_index_point++; draw_thread_arrow (); } break; case ctrl('P'): case 'k': /* line up */ thread_up: if (thread_index_point == 0) { if (_hp_glitch) { erase_thread_arrow (); } if (top_thread > last_thread_on_screen) { thread_index_point = top_thread - 1; show_thread_page (); } else { erase_thread_arrow (); thread_index_point = top_thread - 1; draw_thread_arrow (); } break; } if (_hp_glitch) { erase_thread_arrow (); } if (thread_index_point <= first_thread_on_screen) { thread_index_point--; show_thread_page (); } else { erase_thread_arrow (); thread_index_point--; draw_thread_arrow (); } break; case 'b': /* page up */ case ctrl('U'): case ctrl('B'): /* vi style */ thread_page_up: if (thread_index_point == 0) { if (_hp_glitch) { erase_thread_arrow (); } if (top_thread > last_thread_on_screen) { thread_index_point = top_thread - 1; show_thread_page (); } else { erase_thread_arrow (); thread_index_point = top_thread - 1; draw_thread_arrow (); } break; } #ifndef USE_CLEARSCREEN clear_message (); #endif erase_thread_arrow (); scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2); if ((n = thread_index_point % scroll_lines) > 0) { thread_index_point = thread_index_point - n; } else { thread_index_point = ((thread_index_point - scroll_lines) / scroll_lines) * scroll_lines; } if (thread_index_point < 0) { thread_index_point = 0; } if (thread_index_point < first_thread_on_screen || thread_index_point >= last_thread_on_screen) show_thread_page (); else draw_thread_arrow (); break; case 'c': /* catchup thread but ask for confirmation */ case 'K': /* mark thread as read immediately */ if (ch == 'c') { if (confirm_action && !prompt_yn (cLINES, txt_mark_thread_read, 'y')) { break; } } for (i = (int) base[thread_basenote] ; i != -1 ; i = arts[i].thread) { if (arts[i].unread != ART_READ) { mark_all_xref_read (&arts[i], group_path); } arts[i].unread = ART_READ; } goto thread_done; case 'd': /* toggle display of subject & subj/author */ if (! threaded_on_subject) { toggle_subject_from (); show_thread_page (); } break; case 'h': /* help */ show_info_page (HELP_INFO, help_thread, txt_thread_com); show_thread_page (); break; case 'H': /* toggle mini help menu */ toggle_mini_help (THREAD_LEVEL); show_thread_page(); break; case 'I': /* toggle inverse video */ toggle_inverse_video (); show_thread_page (); break; case 'q': /* return to previous level */ goto thread_done; case 'Q': /* quit */ ret_code = -2; goto thread_done; case 't': /* tag/untag art for mailing/piping/printing/saving */ n = choose_response (thread_basenote, thread_index_point); if (n < 0) break; if (arts[n].tagged) { arts[n].tagged = 0; info_message (txt_untagged_art); } else { arts[n].tagged = ++num_of_tagged_arts; info_message (txt_tagged_art); } bld_tline (thread_index_point, n); draw_tline (thread_index_point, FALSE); if (thread_index_point + 1 < top_thread) goto thread_down; draw_thread_arrow (); break; case 'R': /* bug/gripe/comment mailed to author */ mail_bug_report (); #ifndef USE_CLEARSCREEN ClearScreen (); #endif show_thread_page (); break; case 'v': /* version */ info_message (cvers); break; case 'z': /* mark article as unread */ case 'Z': /* mark thread as unread */ n = choose_response (thread_basenote, thread_index_point); if (ch == 'z') { if (arts[n].unread == ART_READ) { if (arts[n].hot) { num_of_hot_arts++; } } arts[n].unread = ART_UNREAD; } else { for (i = (int) base[thread_basenote] ; i != -1; i = arts[i].thread) { if (arts[n].unread == ART_READ) { if (arts[n].hot) { num_of_hot_arts++; } } arts[i].unread = ART_UNREAD; } } bld_tline (thread_index_point, n); draw_tline (thread_index_point, FALSE); info_message (ch == 'z' ? txt_art_marked_as_unread : txt_thread_marked_as_unread); draw_thread_arrow (); break; case '*': /* mark article as selected */ case '.': /* toggle article as selected */ n = choose_response (thread_basenote, thread_index_point); if (n < 0) break; if (ch == '.' && arts[n].hot == 1) flag = 0; else flag = 1; arts[n].hot = flag; /* update_thread_page (); */ bld_tline (thread_index_point, n); draw_tline (thread_index_point, FALSE); if (thread_index_point + 1 < top_thread) goto thread_down; draw_thread_arrow (); #if 0 info_message (flag ? txt_art_marked_as_selected : txt_art_marked_as_deselected) #endif break; case '@': /* reverse selections */ for (i = (int) base[thread_basenote] ; i != -1 ; i = arts[i].thread) { arts[i].hot = (arts[i].hot ? 0 : 1); } update_thread_page (); break; case '~': /* undo selections */ for (i = (int) base[thread_basenote] ; i != -1 ; i = arts[i].thread) { arts[i].hot = 0; } update_thread_page (); break; default: info_message (txt_bad_command); } } thread_done: set_xclick_off (); clear_note_area (); #endif /* INDEX_DAEMON */ return (ret_code); } void show_thread_page () { #ifndef INDEX_DAEMON extern int index_point; int i, j; static int index = 0; set_signals_thread (); ClearScreen (); if (threaded_on_subject) { sprintf (msg, "Thread (%.*s)", cCOLS-23, arts[thread_respnum].subject); } else { sprintf (msg, "List Thread (%d of %d)", index_point+1, top_base); } show_title (msg); MoveCursor (INDEX_TOP, 0); if (thread_index_point > top_thread - 1) { thread_index_point = top_thread - 1; } if (NOTESLINES <= 0) { first_thread_on_screen = 0; } else { first_thread_on_screen = (thread_index_point / NOTESLINES) * NOTESLINES; if (first_thread_on_screen < 0) { first_thread_on_screen = 0; } } last_thread_on_screen = first_thread_on_screen + NOTESLINES; if (last_thread_on_screen >= top_thread) { last_thread_on_screen = top_thread; first_thread_on_screen = (top_thread / NOTESLINES) * NOTESLINES; if (first_thread_on_screen == last_thread_on_screen || first_thread_on_screen < 0) { if (first_thread_on_screen < 0) { first_thread_on_screen = 0; } else { first_thread_on_screen = last_thread_on_screen - NOTESLINES; } } } if (top_thread == 0) { first_thread_on_screen = 0; last_thread_on_screen = 0; } index = choose_response (thread_basenote, first_thread_on_screen); assert(first_thread_on_screen != 0 || index == thread_respnum); for (j=0, i = first_thread_on_screen; j < NOTESLINES && i < last_thread_on_screen; i++, j++) { bld_tline (i, index); draw_tline (i, TRUE); if ((index = next_response (index)) == -1) { break; } } #ifndef USE_CLEARSCREEN CleartoEOS (); #endif show_mini_help (THREAD_LEVEL); if (last_thread_on_screen == top_thread) { info_message (txt_end_of_thread); } draw_thread_arrow (); #endif /* INDEX_DAEMON */ } void update_thread_page () { #ifndef INDEX_DAEMON register int i, j, index; index = choose_response (thread_basenote, first_thread_on_screen); assert(first_thread_on_screen != 0 || index == thread_respnum); for (j=0, i = first_thread_on_screen; j < NOTESLINES && i < last_thread_on_screen; ++i, ++j) { bld_tline (i, index); draw_tline (i, FALSE); if ((index = next_response (index)) == -1) { break; } } draw_thread_arrow(); #endif /* INDEX_DAEMON */ } void draw_thread_arrow () { MoveCursor (INDEX2LNUM(thread_index_point), 0); if (draw_arrow_mark) { fputs ("->", stdout); fflush (stdout); } else { StartInverse (); draw_tline (thread_index_point, TRUE); EndInverse (); } MoveCursor (cLINES, 0); } void erase_thread_arrow () { MoveCursor (INDEX2LNUM(thread_index_point), 0); if (draw_arrow_mark) { fputs (" ", stdout); } else { if (_hp_glitch) { EndInverse (); } draw_tline (thread_index_point, TRUE); } fflush (stdout); } int prompt_thread_num (ch) int ch; { int num; clear_message (); if ((num = prompt_num (ch, txt_read_art)) == -1) { clear_message (); return FALSE; } if (num >= top_thread) num = top_thread - 1; if (num >= first_thread_on_screen && num < last_thread_on_screen) { erase_thread_arrow (); thread_index_point = num; draw_thread_arrow (); } else { #ifndef USE_CLEARSCREEN erase_thread_arrow (); #endif thread_index_point = num; show_thread_page (); } return TRUE; } /* * Return the number of unread articles there are within a thread */ int new_responses (thread) int thread; { int i; int sum = 0; for (i = (int) base[thread]; i >= 0; i = arts[i].thread) { if (arts[i].unread != ART_READ) { sum++; } } return sum; } /* * Which base note (an index into base[]) does a respnum * (an index into arts[]) corresponsd to? * * In other words, base[] points to an entry in arts[] which is * the head of a thread, linked with arts[].thread. For any q: arts[q], * find i such that base[i]->arts[n]->arts[o]->...->arts[q] * * Note that which_thread() can return -1 if in show_read_only mode and * the article of interest has been read as well as all other articles in * the thread, thus resulting in no base[] entry for it. */ int which_thread (n) int n; { register int i, j; for (i = 0; i < top_base; i++) { for (j = (int) base[i] ; j >= 0 ; j = arts[j].thread) { if (j == n) { return i; } } } sprintf (msg, "%d", n); error_message (txt_cannot_find_base_art, msg); return -1; } /* * Find how deep in a thread a response is. Start counting at zero */ int which_response (n) int n; { int i, j; int num = 0; i = which_thread (n); assert(i >= 0); for (j = (int) base[i]; j != -1; j = arts[j].thread) if (j == n) break; else num++; return num; } /* * Given an index into base[], find the number of responses for * that basenote */ int num_of_responses (n) int n; { int i; int oldi = -3; int sum = 0; assert (n < top_base); for (i = (int) base[n]; i != -1; i = arts[i].thread) { assert (i != -2); assert (i != oldi); oldi = i; sum++; } return sum - 1; } /* * Given an index into base[], return relevant statistics */ int stat_thread (n, sbuf) int n; struct t_art_stat *sbuf; { int i; sbuf->total = 0; sbuf->unread = 0; sbuf->seen = 0; sbuf->hot_total = 0; sbuf->hot_unread= 0; sbuf->hot_seen = 0; for (i = (int) base[n]; i != -1; i = arts[i].thread) { ++sbuf->total; if (arts[i].unread == ART_UNREAD) ++sbuf->unread; else if (arts[i].unread == ART_WILL_RETURN) ++sbuf->seen; if (arts[i].hot) { ++sbuf->hot_total; if (arts[i].unread == ART_UNREAD) ++sbuf->hot_unread; else if (arts[i].unread == ART_WILL_RETURN) ++sbuf->hot_seen; } #if 0 if (arts[i].killed) { ++sbuf->killed; } #endif } if (sbuf->hot_unread) sbuf->art_mark = hot_art_mark; else if (sbuf->unread) sbuf->art_mark = unread_art_mark; else if (sbuf->seen) sbuf->art_mark = return_art_mark; else sbuf->art_mark = READ_ART_MARK; return(sbuf->total); } /* * Find the next response. Go to the next basenote if there * are no more responses in this thread */ int next_response (n) int n; { int i; if (arts[n].thread >= 0) { return arts[n].thread; } i = which_thread (n) + 1; if (i >= top_base) { return -1; } return (int) base[i]; } /* * Given a respnum (index into arts[]), find the respnum of the * next basenote */ int next_thread (n) int n; { int i; i = which_thread (n) + 1; if (i >= top_base) return -1; return (int) base[i]; } /* * Find the previous response. Go to the last response in the previous * thread if we go past the beginning of this thread. */ int prev_response (n) int n; { int resp; int i; resp = which_response (n); if (resp > 0) return choose_response (which_thread (n), resp-1); i = which_thread (n) - 1; if (i < 0) return -1; return choose_response (i, num_of_responses (i)); } /* * return response number n from thread i */ int choose_response (i, n) int i; int n; { int j; j = (int) base[i]; while (n-- > 0 && arts[j].thread >= 0) { j = arts[j].thread; } return j; } /* * Find the next unread response in this group. If no response is found * from current point to the end restart from beginning of articles. */ int next_unread (n) int n; { int cur_base_art = n; while (n >= 0) { if ((arts[n].unread == ART_UNREAD || arts[n].unread == ART_WILL_RETURN) && arts[n].thread != ART_EXPIRED) { return n; } n = next_response (n); } n = base[0]; while (n != cur_base_art) { if ((arts[n].unread == ART_UNREAD || arts[n].unread == ART_WILL_RETURN) && arts[n].thread != ART_EXPIRED) { return n; } n = next_response (n); } return -1; } /* * Find the previous unread response in this thread */ int prev_unread (n) int n; { while (n >= 0) { if (arts[n].unread == ART_UNREAD && arts[n].thread != ART_EXPIRED) { return n; } n = prev_response (n); } return -1; } e*[SRC.TIN-1_22]TIN.1;1+,s.u// 4us-d0@123KPWOv56`Ft7Pa189]VG/HJ$.ev 1 .if t .lt 6.85i .if n .lt 7.2i .ev .if t .po .45i .if t .ll 6.85i .if n .ll 7.2i .if \n(mo=1 .ds mo January .if \n(mo=2 .ds mo February .if \n(mo=3 .ds mo March .if \n(mo=4 .ds mo April .if \n(mo=5 .ds mo May .if \n(mo=6 .ds mo June .if \n(mo=7 .ds mo July .if \n(mo=8 .ds mo August .if \n(mo=9 .ds mo September .if \n(mo=10 .ds mo October .if \n(mo=11 .ds mo November .if \n(mo=12 .ds mo December .TH TIN 1 "Version 1.2 PL2" "" "LOCAL" .ds ]W \*(mo \n(dy, 19\n(yr .nh .SH NAME tin, rtin, cdtin, tind \- A Netnews reader .SH SYNOPSIS .B tin/rtin/cdtin/tind [ .I options ] [ .I newsgroups ] .SH DESCRIPTION .I Tin is a full-screen easy to use Netnews reader. It can read news locally (i.e. \fI/usr/spool/news\fP) or remotely (\fIrtin\fP or \fItin -r\fP option) via a NNTP (Network News Transport Protocol) server. \fICdtin\fP can read news locally and news archived on CD-ROM. It will automatically utilize nov (news overview) style index files if available locally or via the nntp xover command. .PP .I Tin has five separate levels of operation: Group selection level, Spooldir selection level, Group level, Thread level and Article level. Use the 'h' (help) command to view a list of the commands available at a particular level. .PP On startup \fItin\fP will show a list of the newsgroups found in \fI$HOME/.newsrc\fP. An arrow '->' or highlighted bar will point to the first newsgroup. Move to a group by using the terminal arrow keys (terminal dependent) or 'j' and 'k'. Use PgUp/PgDn (terminal dependent) or Ctrl-U and Ctrl-D to page up/down. Enter a newsgroup by pressing RETURN. .PP The TAB key advances to the next newsgroup with unread articles and enters it. .SH OPTIONS .TP 12 .B -c create/update index files for every group in \fI$HOME/.newsrc\fP or file specified by -f option and mark all articles as read. .TP .BI -f " file" use the specified file of subscribed to newsgroups in place of \fI$HOME/.newsrc\fP. .TP .B -h help listing all command line options. .TP .B -H brief introduction to \fItin\fP that is also shown the first time it is started. .TP .BI -I " dir" directory in which to store newsgroup index files. Default is \fI$HOME/.tin/.index\fP. .TP .BI -m " dir" mailbox directory to use. Default is \fI$HOME/Mail\fP. .TP .BI -M " user" mail unread articles to specified user for later reading. For more information read section Automatic Mailing and Saving New News. .TP .BI -n Only load groups from the active file that are also subscribed to in the users \fI.newsrc\fP. This allows a noticeable speedup when connecting via a slow line. .TP .BI -p " program" print program with options. .TP .BI -q quick start without checking for new newsgroups. .TP .BI -P purge group index files of articles that no longer exist. Care should be taken when using this command as it stats each and every article in each group that is accessed. On a low speed connection this can have an undisirable effect and it also knocks the hell out of your filesystem. .TP .B -r read news remotely from the default NNTP server specified in the environment variable NNTPSERVER or contained in the file \fI/etc/nntpserver\fP. .TP .B -R read news saved by -S option (not yet implemented). .TP .BI -s " dir" save articles to directory. Default is \fI$HOME/News\fP. .TP .B -S save unread articles for later reading by -R option. For more information read section Automatic Mailing and Saving New News. .TP .B -u create/update index files for every group in \fI$HOME/.newsrc\fP or file specified by -f option. This option is disabled if \fItin\fP retreives its index files via a NNTP server. .TP .B -U start \fItin\fP in the background to update index files while reading news in the foreground. This option is disabled if \fItin\fP retreives its index files via a NNTP server. .TP .B -v verbose mode for -c -M -S -u and -Z options. .TP .B -w quick mode to post an article and then exit. .TP .B -z only start \fItin\fP if there is any new/unread news. If there is news \fItin\fP will position cursor at first group with unread news. Useful for putting in login file. .TP .B -Z check if there is any new/unread news and exit with appropriate status. If -v option is specified the number of unread articles in each group is printed. An exit code 0 indicates no news, 1 that an error occurred and 2 that new/unread news exists. Useful for writing scripts. .PP \fITin\fP can also dynamically change its options by the 'M' menu command. Any changes are written to \fI$HOME/.tin/tinrc\fP. .PP The index daemon version, \fItind\fP, only supports the -f, -h, -I and -v options. .SH "INDEX FILES" In order to keep track of threads, \fItin\fP maintains an index for each newsgroup. There are a number of methods in which index files can be created and updated. .PP The simplest method is that each user creates/updates there own index files that are stored in \fI$HOME/.tin/.index\fP. This has the advantage that any user can compile and install \fItin\fP, but the disadvantage is that each user is going to be creating duplicate files and using precious disk space. A good way to keep index files updated is by doing a \fItin -U\fP that will update index files in the background while you are reading news in the foreground. You can also update index files via the system batcher cron with the -u option: .RS .nf .ft CW 30 6 * * * /usr/local/bin/tin -u .ft P .fi .RE .PP A slightly better method is to set \fItin\fP setuid news and have all index files created and updated in the news spool directory (i.e. \fI/usr/spool/news/.index\fP). This has the advantage that there will only be one copy of the index files on each machine on your network, but the disadvantage is that you will have \fItin\fP running setuid news. .PP A better method is to install the \fItind\fP index file updating daemon and have it create and update index files for all groups in your active file at regular intervals in the news spool directory (i.e. \fI/usr/spool/news/.index\fP). This has the advantage that there will only be one copy of the index files on each machine on your network and \fItin\fP must not be setuid news, but the disadvantage is that you will have to have news permissions to install \fItind\fP and root permissions to install an entry in the cron batcher system to have \fItind\fP regularly update index files. .PP The best method is to install the \fItind\fP index file updating daemon on your NNTP server and have it create and update index files for all groups in your active file at regular intervals in the news spool directory (i.e. \fI/usr/spool/news/.index\fP). This has the advantage that there will only be one copy of the index files on the NNTP server for the whole of your network, but the disadvantage is that you will have to install my NNTP server patches to allow \fItin\fP to retreive index file from your NNTP server and and you must install an entry in the cron batcher system to have \fItind\fP regularly update index files (note that this is the method we use on our network of 40-50 machines and have not had any problems). .PP Entering a group the first time tends to be slow because the index file must be built from scratch unless the \fItind\fP update daemon is being used. To alleviate the slowness start \fItin\fP to create all index files for the groups you subscribe to with \fItin -u -v\fP and go for a coffee. Subsequent readings of a group will cause incremental updating of the index file. .PP If reading news remotely and locally updating index files operation will be somewhat slower because the articles must be retreived from the NNTP server. .SH "NEWS ADMINISTRATION" Maintaining Netnews on large networks of machines can be a pretty time consuming job as I discovered when I was given the job of maintaining our news system and news users. .PP \fITin\fP is a News User Agent and so most of the users were always asking questions or doing things that could be frowned upon by there departments. To releive news admins (and especially me) of this features have been added to make life easier for news adminstrators. .PP When a user starts \fItin\fP it is possible to inform them of any important changes/information concerning the news system by displaying a message of the day (motd) file. The motd file should be created in your news lib directory (i.e. \fI/usr/lib/news/motd\fP) and should have file permissions set to 0644. The motd file will only be displayed if its contents is newer than the last time the user started \fItin\fP. If reading news via NNTP my XMOTD patch will have to have been applied to your NNTP server. .PP A user starting \fItin\fP for the first time can be automatically subscribed to a list of newsgroups that are deemed appropriate by the news administrator. At our site the subscriptions file has 125 groups (our active file contains over 400 groups with many only being marginally interesting to most people). The subscriptions file should be created in your news lib directory (i.e. \fI/usr/lib/news/subscriptions\fP) and should have file permissions set to 0644. If reading news via NNTP my LIST SUBSCRIPTIONS patch will have to have been applied to your NNTP server. .PP If my NNTP XUSER'b~ TIN-1_22.BCKsd[SRC.TIN-1_22]TIN.1;1uX| patch has been applied to your NNTP server you will be able to log the username and machine to your NNTP logfile for usage statistics. .SH "SCREEN FORMAT" \fITin\fP has five separate levels of operation: Group selection level, Spooldir selection level, Group level, Thread level and Article level. .PP At the Group Selection level the title displays the number of subscribed groups. The newsgroups are displayed on the left of the screen with the number of unread articles displayed on the same line in the middle of the screen. .RS .nf .ft CW .in +.5i .ta +\w'1 'u +\w'news.software.readers 'u .ti -.5i .ft P i.e., .ft CW 1 alt.sources 10 2 comp.sources.misc 3 3 news.software.readers 12 .in -.5i .ft P .fi .RE .PP At the Group level the title contains the name of the group, the number of conversation threads and total number of articles i.e., alt.sources (7 23). If the group has been setup not to thread articles (i.e., alt.sources is in \fI$(HOME)/.tin/unthread\fP) the title will be alt.sources (U 23). There are two possible display formats as shown below: .RS .nf .ft CW .in +.5i .ta +\w'1 'u +\w'+ 'u +\w'3 'u +\w'This question has 'u .ti -.5i .ft P i.e., .ft CW 1 + 3 Bnews sources? iain@anl433.uucp 2 1 This question has ether@net .ti -.5i .ft P or .ft CW .ta +\w'1 'u +\w'+ 'u +\w'3 'u +\w'This question has a long subject line 'u .ti -.5i .ft P i.e., .ft CW 1 + 3 Bnews sources? 2 1 This question has a longer subject line .in -.5i .ft P .fi .RE .PP At the Article level the page header has the following format: .RS .nf .ft CW .in +.5i .ta \w' 'u
.ti -.5i .ft P i.e., .ft CW .ta \w'24 Jul 15:20:03 GMT 'u +\w'Bnews sources? 'u 24 Jul 15:20:03 GMT alt.sources Thread 1 of 2 Article 452 Bnews sources? 3 responses iain@anl433.uucp Organization name
.in -.5i .ft P .fi .RE .SH "COMMON MOVING KEYS" This table shows the common keys/commands for moving at all five levels within \fItin\fP. .RS .nf .ta \w'Beginning of list/article 'u +\w'ansi/at386/vt100 'u ansi/at386/vt100 Other Terminals Beginning of list/article \fBHome\fP \fB1\fP (\fB^R\fP or \fBg\fP at article level) End of list/article \fBEnd\fP \fB$\fP (also \fBG\fP at article level) Page Up \fBPgUp\fP \fB^U\fP or \fB^B\fP or \fBb\fP Page Down \fBPgDn\fP \fB^D\fP or \fB^F\fP or \fB\fP Line Up \fBUp arrow\fP \fBk\fP (not at article level) Line Down \fBDown arrow\fP \fBj\fP (not at article level) .fi .RE .SH "COMMON EDITING COMMANDS" An emacs style editing package allows the easy editing of input strings. An history list allows the easy reuse of previously entered strings. The following commands are available when editing a string: .PP .TP 10 \fB^A,^E\fP move to beginning or end of line, respectively. .TP \fB^F,^B\fP nondestructive move forward or back one location, respectively. .TP \fB^D\fP delete the character currently under the cursor, or send EOF if no characters in the buffer. .TP \fB^H,\fP delete character left of the cursor. .TP \fB^K\fP delete from cursor to end of line. .TP \fB^P,^N\fP move through history, previous and next, respectively. .TP \fB^L,^R\fP redraw the current line. .TP \fB\fP places line on history list if nonblank, appends newline and returns to the caller. .TP \fB\fP aborts the present editing operation. .SH "NEWSGROUP SELECTION COMMANDS" .TP 10 \fB4\fP Select group 4. .TP \fB^K\fP Delete current group from \fI$HOME/.newsrc\fP file. .TP \fB^L\fP Redraw page. .TP \fB^R\fP Reset \fI$HOME/.newsrc\fP file. .TP \fB\fP Read current group. .TP \fB\fP View next group with unread news. Will wrap around to the beginning of the group selection list looking for unread groups. .TP \fBB\fP Mail a bug report or comment to the author. This is the best way of getting bugs fixed and features added/changed. .TP \fBc\fP Mark current group as all read with confirmation and goto next group in group selection list. .TP \fBC\fP Mark current group as all read and goto next unread group in group selection list. .TP \fBd\fP Toggle display to show just the groupname or the groupname and the groups description. .TP \fBg\fP Choose a new group by name. The position of the group within the group list will also be asked for. By entering '1' the new group will be the first group in the displayed list, by entering '8' the group will be the eighth group in the list etc. By entering '$' the group will be the last group displayed. .TP \fBh\fP Help screen of newsgroup selection commands. .TP \fBH\fP Toggle the display of help mini menu at the bottom of the screen. .TP \fBI\fP Toggle inverse video. .TP \fBl\fP List and allow selection of the available spool directories. This feature requires a special library to be linked with \fItin\fP to create \fIcdtin\fP which can then read news from an active news feed and also from multiple CD-ROMs. .TP \fBm\fP Move the current group within the group selection list. By entering '1' the group will become the first displayed group in the list, by entering '8' the eighth group in the list etc. By entering '$' the group will be the last group displayed. .TP \fBM\fP User configurable options menu (for more information see section Options Menu). .TP \fBq\fP Quit \fItin\fP. .TP \fBQ\fP Quit \fItin\fP. .TP \fBr\fP Toggle display of all subscribed to groups and just the subscribed to groups containing unread articles. Command has no effect if groups were read from the command line when \fItin\fP was started. .TP \fBs\fP Subscribe to current group. .TP \fBS\fP Subscribe to groups matching user specified pattern. .TP \fBu\fP Unsubscribe to current group. .TP \fBU\fP Unsubscribe to groups matching user specified pattern. .TP \fBv\fP Print \fItin\fP version information. .TP \fBw\fP Post an article to current group. .TP \fBW\fP List articles posted by user. The date posted, the newsgroup and the subject are listed. .TP \fBy\fP The first time this command is called it will yank in all groups from \fI$LIBDIR/active\fP that are not in \fI$HOME/.newsrc\fP. After any groups have been subscribed/unsubscribed to, this command if pressed again will reread \fI$HOME/.newsrc\fP and display only the subscribed groups. .TP \fBY\fP Reread the active file to see if any new news has arrived since starting \fItin. .TP \fBz\fP Mark all articles in the current group as unread. .TP \fBZ\fP Undelete previously deleted group by ^K command from \fI$HOME/.newsrc\fP. .TP \fB/\fP Group forward search. .TP \fB?\fP Group backward search. .SH "SPOOL DIRECTORY SELECTION COMMANDS" .TP 10 \fB4\fP Select spool directory 4. .TP \fB^L\fP Redraw page. .TP \fB\fP Read news from selected spool directory. .TP \fBB\fP Mail a bug report or comment to the author. This is the best way of getting bugs fixed and features added/changed. .TP \fBh\fP Help screen of spool directory selection commands. .TP \fBH\fP Toggle the display of help mini menu at the bottom of the screen. .TP \fBI\fP Toggle inverse video. .TP \fBq\fP Return to previous level. .TP \fBQ\fP Quit \fItin. .TP \fBv\fP Print \fItin\fP version information. .SH "GROUP INDEX COMMANDS" .TP 10 \fB4\fP Select article 4. .TP \fB^K\fP Kill current article (for more information read section Kill Article Menu). .TP \fB^L\fP Redraw page. .TP \fB\fP Read current article. .TP \fB\fP View next unread article or group. .TP \fBa\fP Author forward search. .TP \fBA\fP Author backward search. .TP \fBc\fP Mark all articles as read with confirmation. .TP \fBC\fP Mark all articles as read and goto next group with unread news. .TP \fBd\fP Toggle display to show just the subject or the subject and author. .TP \fBg\fP Choose a new group by name. .TP \fBh\fP Help screen of group index commands. .TP \fBH\fP Toggle the display of help mini menu at the bottom of the screen. .TP \fBI\fP Toggle inverse video. .TP \fBK\fP Mark article/thread as read and advance to next unread article/thread. .TP \fBl\fP List the author of each response in current thread and enter thread selection level. .TP \fBm\fP Mail current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to someone. .TP \fBM\fP User configurable options menu (for more information see section Options Menu). .TP \fBn\fP Go to next group. .TP \fBN\fP Go to next unread article. .TP \fBo\fP Output current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to printer. .TP \fBp\fP Go to previous group. .TP \fBP\fP Go to previous unread article. .TP \fBq\fP Return to previous level. .TP \fBQ\fP Quit \fItin. .TP \fBs\fP Save current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to file / files / mailbox. To save to a mailbox enter '=' or '=mailbox' when asked for filename to save to. To save in / format enter '+filename'. Environment variables are allowed within a filename (i.e. \fI$SOURCES/dir/filename\fP). .TP \fBt\fP Tag current article / thread for mailing ('m') / piping ('|') / printing ('o') / saving ('s') / crossposting ('x'). .TP \fBu\fP Toggle display to show all articles as unthreaded or threaded. .TP \fBU\fP Untag all articles that were tagged. .TP \fBv\fP Print \fItin\fP version information. .TP \fBw\fP Post an article to current group. .TP \fBW\fP List articles posted by user. The date posted, the newsgroup and the subject are listed. .TP \fBx\fP Crosspost already posted current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to another newsgroup(s). Useful for reposting from global to local newsgroups. .TP \fBX\fP Mark all unread articles that have not been selected as read, redo screen to reflect changes and put index at the first thread to begin reading. Pressing 'X' again will toggle back to the way it was before. See '~' command for clearing the toggle effect. .TP \fBz\fP Mark current article as unread. .TP \fBZ\fP Mark current thread as unread. .TP \fB/\fP Search forward for specified subject. .TP \fB?\fP Search backward for specified subject. .TP \fB-\fP Show last message. .TP \fB|\fP Pipe current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles into command. .TP \fB*\fP Select current thread for later processing. .TP \fB\.\fP Toggle selection of current thread. If at least one unread art, but not all unread arts, in thread is selected, then all unread arts become selected. .TP \fB@\fP Reverse all selections on all articles. .TP \fB~\fP Undo all selections on all articles. It clears the toggle effect of 'X' command. Thus after first doing a 'X', one can then do '~' to reset articles. Thus, one can iteratively whittle down uninteresting threads. .TP \fB+\fP Perform auto-selection on current group. .TP \fB;\fP For each thread in current group, if it at least one unread art is selected, all unread arts become selected. This is useful for auto-selection on author where reader wants to see entire thread. .TP \fB=\fP Prompts for a pattern with which to match on. All threads whose subjects match the pattern will be selected. A pattern of "*" will match all subjects. Entering just will cause the previous pattern to be used. .SH "THREAD LISTING COMMANDS" .TP 10 \fB4\fP Select article 4 within thread. .TP \fB^L\fP Redraw page. .TP \fB\fP Read current article within thread. .TP \fB\fP View next unread article within thread. .TP \fBB\fP Mail a bug report or comment to the author. This is the best way of getting bugs fixed and features added/changed. .TP \fBc\fP Mark thread as read after confirmation and return to previous level. .TP \fBd\fP Toggle display to show just the subject or the subject and author. .TP \fBh\fP Help screen of thread listing commands. .TP \fBH\fP Toggle the display of help mini menu at the bottom of the screen. .TP \fBI\fP Toggle inverse video. .TP \fBK\fP Mark thread as read and return to previous level. .TP \fBq\fP Return to previous level. .TP \fBQ\fP Quit \fItin. .TP \fBr\fP Toggle display to show all articles or only unread articles. .TP \fBB\fP Mail a bug report or comment to the author. This is the best way of getting bugs fixed and features added/changed. .TP \fBt\fP Tag current article for mailing ('m') / piping ('|') / printing ('o') / saving ('s') / crossposting ('x'). .TP \fBT\fP Return to group index level. .TP \fBv\fP Print \fItin\fP version information. .TP \fBz\fP Mark current article in thread as unread. .TP \fBZ\fP Mark all articles in thread as unread. .SH "ARTICLE VIEWER COMMANDS" .TP 10 \fB0\fP Read the base article in this thread. .TP \fB4\fP Read response 4 in this thread. .TP \fB^H\fP Show all of the articles mail header. .TP \fB^K\fP Kill current article (for more information read section Kill Article Menu). .TP \fB^L\fP Redraw page. .TP \fB\fP Goto next base article. .TP \fB\fP Goto next unread article. .TP \fBa\fP Author forward search. .TP \fBA\fP Author backward search. .TP \fBc\fP Mark all articles as read with confirmation and return to group selection level. .TP \fBC\fP Mark current group as all read and goto next unread group in group selection list. .TP \fBd\fP Toggle rot-13 decoding for this article. .TP \fBD\fP Delete current article. It must have been posted by the same user. The cancel message can be seen in the newsgroup 'control'. .TP \fBf\fP Post a followup to the current article with a copy of the article included. .TP \fBF\fP Post a followup to the current article. .TP \fBh\fP Help screen of article page commands. .TP \fBH\fP Toggle the display of help mini menu at the bottom of the screen. .TP \fBI\fP Toggle inverse video. .TP \fBk\fP Mark article as read and advance to next unread article. .TP \fBK\fP Mark thread as read and advance to next unread thread. .TP \fBm\fP Mail current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to someone. .TP \fBM\fP User configurable options menu (for more information see section Options Menu). .TP \fBn\fP Go to the next article. .TP \fBN\fP Go to the next unread article. .TP \fBo\fP Output current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to printer. .TP \fBo\fP Output article/thread/tagged articles to printer. .TP \fBp\fP Go to the previous article. .TP \fBP\fP Go to the previous unread article. .TP \fBq\fP Return to previous level. .TP \fBQ\fP Quit \fItin. .TP \fBr\fP Reply through mail to the author of the current article with a copy of the article included. .TP \fBR\fP Reply through mail to the author of the current article. .TP \fBs\fP Save current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to file / files / mailbox. To save to a mailbox enter '=' or '=mailbox' when asked for filename to save to. To save in / format enter '+filename'. Environment variables are allowed within a filename (i.e. \fI$SOURCES/dir/filename\fP). .TP \fBt\fP Return to group selection level. .TP \fBT\fP Tag current article for mailing ('m') / piping ('|') / printing ('o') / saving ('s') / crossposting ('x'). .TP \fBv\fP Print \fItin\fP version information. .TP \fBw\fP Post an article to current group. .TP \fBW\fP List articles posted by user. The date posted, the newsgroup and the subject are listed. .TP \fBx\fP Crosspost already posted current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to another newsgroup(s). Useful for reposting from global to local newsgroups. .TP \fBz\fP Mark article as unread. .TP \fB/\fP Article forward search. .TP \fB?\fP Article backward search .TP \fB|\fP Pipe current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles into command. .TP \fB<\fP Goto the first article in the current thread. .TP \fB>\fP Goto the last article in the current thread. .TP \fB*\fP Select current thread for later processing. .TP \fB\.\fP Toggle selection of current article. .TP \fB@\fP Reverse article selections. .TP \fB~\fP Undo all selections on current thread. .SH "GLOBAL OPTIONS MENU" This menu is accessed by pressing 'M' at all levels. It allows the user to customize the behaviour of \fItin\fP. The options are saved to the file \fI$HOME/.tin/tinrc\fP. Use to toggle the required option and to set. .TP 4 \fBAuto save\fP Automatically save articles/threads by ``Archive-name:'' line in article header and post process them if process type is not set to None. .TP \fBEditor offset\fP Set ON if the editor used for posting, follow-ups and bug reports has the capability of starting and positioning the cursor at a specified line within a file. .TP \fBMark saved read\fP Allows saved articles/threads to be automatically marked as read. .TP \fBConfirm Command\fP Allows certain commands (i.e. 'c' catchup) that require user confirmation to be executed immediately if set OFF. .TP \fBDraw arrow\fP Allows groups/articles to be selected by an arrow '->' if set ON or by an highlighted bar if set OFF. .TP \fBPrint header\fP This allows the complete mail header or only the ``Subject:'' and ``From:'' fields to be output when printing articles. .TP \fBGoto 1st unread\fP This allows the cursor to be placed at the first / last unread article upon entering a newsgroup with unread news. .TP \fBScroll full page\fP If set ON scrolling of groups/articles will be a full page at a time, otherwise half a page at a time. .TP \fBCatchup on quit\fP If set ON the user is asked when quitting if all groups read during the current session should be marked read. .TP \fBThread articles\fP If set ON articles will be threaded in all groups (default), otherwise articles will be shown unthreaded. Threading/unthreading is possible on a per group basis by setting the group attribute variable 'thread_arts' to ON/OFF in the file \fI$HOME/.tin/attributes\fP. .TP \fBShow only unread\fP If set ON show only new/unread articles, otherwise show all articles. .TP \fBShow description\fP If set ON show a short descriptive text for each displayed newsgroup. The text used is taken from the \fI$LIBDIR/newsgroups\fP file. .TP \fBShow Author\fP If set 'None' only the ``Subject:'' line will be displayed. If set 'Addr' ``Subject:'' line & the address part of the ``From:'' line are displayed. If set 'Name' ``Subject:'' line & the authors full name part of the ``From:'' line are displayed. If set 'Both' ``Subject:'' line & all of the ``From:'' line are displayed. .TP \fBProcess type\fP This specifies the default type of post processing to perform on saved articles. The following types of processing are allowed: .in +.5i .ti -\w'\(em'u \(emnone. .ti -\w'\(em'u \(emunpacking of multi-part shell archives. .ti -\w'\(em'u \(emunpacking of multi-part uuencoded files. .ti -\w'\(em'u \(emunpacking of multi-part uuencoded files, which produce a *.zoo archive whose contents is listed. .ti -\w'\(em'u \(emunpacking of multi-part uuencoded files, which produce a *.zoo archive whose contents is extracted. .ti -\w'\(em'u \(emunpacking of multi-part uuencoded files, which produce a *.zip archive whose contents is listed. .ti -\w'\(em'u \(emunpacking of multi-part uuencoded files, which produce a *.zip archive whose contents is extracted. .ti -\w'\(em'u \(emunpacking of multi-part uuencoded files, which produce a *.lha archive whose contents is listed (AmigaDOS version only). .ti -\w'\(em'u \(emunpacking of multi-part uuencoded files, which produce a *.lha archive whose contents is extracted (AmigaDOS version only). .in -.5i .TP \fBSort articles by\fP This specifies how articles should be sorted. The following sort types are allowed: .in +.5i .ti -\w'\(em'u \(emdon't sort articles (default). .ti -\w'\(em'u \(emsort articles by ``Subject:'' field (ascending & descending). .ti -\w'\(em'u \(emsort articles by ``From:'' field (ascending & descending). .ti -\w'\(em'u \(emsort articles by ``Date:'' field (ascending & descending). .in -.5i .TP \fBSave directory\fP The directory where articles/threads are to be saved. Default is \fI$HOME/News\fP. .TP \fBMail directory\fP The directory where articles/threads are to be saved in mailbox format. This feature is mainly for use with the Elm mail program. It allows the user to save articles/threads/groups simply by giving '=' as the filename to save to. .TP \fBPrinter\fP The printer program with options that is to be used to print articles. Default is lpr for BSD machines and lp for SysV machines. .SH "TINRC CONFIGURABLE VARIABLES" The following variables are user configurable by editing \fI$HOME/.tin/tinrc\fP directly. It is hoped to eventually provide a menu to allow the setting of the most common variables. .TP 4 \fBbatch_save\fP If set ON articles/threads will be saved in batch mode when save -S or mail -M is specified on the command line. Default is OFF. .TP 4 \fBbeginner_level\fP If set ON a mini menu of the most useful commands will be displayed at the bottom of the screen for each level. Default is ON. .TP 4 \fBdisplay_reading_prompt\fP The prompt ``Reading...'' will be displayed when reading an article from a NNTP server to provide feedback to the user. Default is ON. .TP 4 \fBforce_screen_redraw\fP Specifies whether a screen redraw should always be done after certain external commands. Default is OFF. .TP 4 \fBgroupname_max_length\fP Maximum length of the names of newsgroups to be displayed so that more of the newgroup description can be displayed. Default is 132. .TP 4 \fBdefault_sigfile\fP The path that specifies the signature file to use when posting, following upto or replying to an article. If the path is a directory then the signature will be randomly generated from files that are in the specified directory. Default is \fI$HOME/.Sig\fP. .TP 4 \fBeditor_format\fP The format string used to create the editor start command with parameters. Default is '%E +%N %F' (i.e., /bin/vi +7 .article). .TP 4 \fBhot_art_mark\fP The character used to show that an article/thread is auto-selected (hot). Default is '*'. .TP 4 \fBquote_chars\fP The character used in quoting included text to article followups and mail replys. The '_' character represents a blank character and is replaced with ' ' when read. Default is ':_'. .TP 4 \fBreread_active_file_secs\fP The news active file is reread at regular intervals to show if any new news has arrived. Default is 300 seconds. .TP 4 \fBreturn_art_mark\fP The character used to show that an article will return. Default is '-'. .TP 4 \fBsave_to_mmdf_mailbox\fP Allows articles to be saved to a MMDF style mailbox instead of mbox format. Default is OFF unless reading news on SCO Unix which uses MMDF by default. .TP 4 \fBshow_last_line_prev_page\fP The last line of the previous page will be displayed as the first line of next page. Default is OFF. .TP 4 \fBslow_speed_terminal\fP Strips the blanks from the end of each line therefore speeding up the display when reading on a slow terminal or via modem. Default is OFF. .TP 4 \fBtab_after_X_selection\fP If enabled will automatically goto the first unread article after having selected all hot articles and threads with the 'X' command at group index level. Default is OFF. .TP 4 \fBtab_goto_next_unread\fP If enabled pressing TAB at the article viewer level will goto the next unread article immediately instead of first paging through the current one. Default is ON. .TP 4 \fBunread_art_mark\fP The character used to show that an article has not been read. Default is '+'. .TP 4 \fBuse_builtin_inews\fP Allows the builtin NNTP inews to be enabled/disabled. Default is ON (enabled). .TP 4 \fBuse_keypad\fP Allows the scroll keys on the keypad to be enabled/disabled on supported terminals. Default is OFF. .SH "GROUP ATTRIBUTES" \fITin\fP allows certain attributes to be set on a per group basis. These group attributes are read from the file \fI$HOME/.tin/attributes\fP. A later version will provide a menu interface to set all the attributes. At present you will have to edit the file with your editor :-(. The following group attributes are available: .RS .nf .ft CW newsgroup=alt.sources maildir=/usr/iain/Mail/sources savedir=/usr/iain/News/alt.sources sigfile=/usr/iain/.funny_sig organization=Wacky Bits Inc. followup_to=alt.sources.d printer=/usr/local/bin/a2ps -nn | /bin/lpr auto_save=ON batch_save=OFF delete_tmp_files=ON show_only_unread=OFF thread_arts=ON show_author=1 sort_art_type=5 post_proc_type=1 .ft P .fi .RE Note that the ''newsgroup='' line has to be specified before the attributes are specified for that group. .PP All attributes are set to a reasonable default so you only have to specify the attribute that you want to change (i.e., savedir). .PP All toggle attributes are set by specifying ON/OFF. .PP The show_author attribute is specified by a number from the following range: 0=none, 1=username, 2=network address, 3=both. .PP The sort_art_type attribute is specified by a number from the following range: 0=none, 1=subject descending, 2=subject ascending, 3=from descending, 4=from ascending, 5=date descending, 6=date ascending. .PP The post_proc_type attribute is specified by a number from the following range: 0=none, 1=unshar, 2=uudecode, 3=uudecode & list zoo archive, 4=uudecode & extract zoo archive, 5=uudecode & list zip archive, 6=uudecode & extract zip archive. (note: if running on AmigaDOS the zoo options are replaced by there corresponding lha archiver options). .SH "AUTOMATIC KILL AND SELECTION" When there is a subject or an author which you are either very interested in, or find completely uninteresting, you can easily instruct \fItin\fP to \fIauto-select\fP or \fIauto-kill\fP articles with specific subjects or from specific authors. These instructions are stored in a \fIkill file\fP. .PP This menu is accessed by pressing '^K' at the group and page levels. It allows the user to kill or select an article that matches the current ``Subject:'' line, ``From:'' line or a string entered by the user. The user entered string can be applied to the ``Subject:'' or ``From:'' lines of an article. The kill description can be limited to the current newsgroup or it can apply to all newsgroups. Once entered the user can abort the command and not save the kill description, edit the kill file or save the kill description. .PP On starting \fItin\fP the users killfile \fI$HOME/.tin/kill\fP is read and on entering a newsgroup any kill or select descriptions are applied. .PP Articles that match a kill description are marked killed and are not displayed. Articles that match an auto-select description are marked with a ''*'' when displayed. .SH "POSTING ARTICLES" \fITin\fP allows posting of articles, follow-up to already posted articles and replying direct through mail to the author of an article. .PP Use the 'w' command to post an article to a newsgroup. After entering the post subject the default editor (i.e. vi) or the editor specified by the $VISUAL environment variable will be started and the article can be entered. To crosspost articles simply add a comma and the name of the newsgroup(s) to the end of the ``Newsgroups:'' line at the beginning of the article. After saving and exiting the editor you are asked if you wish to a)bort posting the article, e)dit the article again or p)ost the article to the specified newsgroup(s). .PP Use the 'W' command to display a history of the articles you have posted. The date the article was posted, which newsgroups the article was posted to and the articles subject line are displayed. .PP Use the 'f' / 'F' command to post a follow-up article to an already posted article. The 'f' command will copy the text of the original article into the editor. The editing procedure is the same as when posting an article with the 'w' command. .PP Use the 'r' / 'R' command to reply direct through mail to the author of an already posted article. The 'r' command will copy the text of the original article into the editor. The editing procedure is the same as when posting an article with the 'w' command. After saving and exiting the editor you are asked if you wish to a)bort sending the article, e)dit the article again or s)end the article to the author. .SH "CUSTOMIZING THE ARTICLE QUOTE STRING" When posting a followup to an article or replying direct to the author of an article via email the text of the article can be quoted. The beginning of the quoted text can contain information about the quoted article (i.e., Name and the Message Id of the article). To allow for different situations certain information from the article can be used in the quoted string. The following variables are expanded if found in the tinrc variables 'mail_quote_format=' or 'news_quote_format=': .RS .nf .ta \w'%A 'u +\w'Address'u \fB%A\fP Address (Email) \fB%D\fP Date \fB%F\fP Full address (%N (%A)) \fB%G\fP Groupname \fB%M\fP Message Id \fB%N\fP Name of user .fi .RE i.e., .RS .nf .ft CW mail_quote_format=On %D in %G you wrote: news_quote_format=In %M, %F wrote: .ft P .fi .RE would expand when used to: .RS .nf .ft CW On 21 Jul 1992 09:45:51 -0400 in alt.sources you wrote: In , Iain Lea (iain@erlm.siemens.de) wrote: .ft P .fi .RE .SH "MAILING PIPING PRINTING REPOSTING AND SAVING ARTICLES" The command interface to mail ('m'), pipe ('|'), print ('o'), crosspost ('x') and save ('s') articles is the same for ease of use. .PP The initial command will ask you to select which a)rticle, t)hread, h)ot (auto selected) r)egex pattern, t)agged articles you wish to mail, pipe etc. .PP Tagged articles must have already been tagged with the 'T' command. All tagged articles can be untagged by the 'U' untag command. .PP If regex pattern matching is selected you are asked to enter a regular expression (i.e. to match all articles subject lines containing 'net News' you must enter '*net News*'). Any articles that match the entered expression will be mailed, piped etc. .PP To save articles to a mailbox with the name of the current newsgroup (i.e. Alt.sources) enter '=' or '=' when asked for the save filename. .PP To save articles in / format enter '+'. .PP When saving articles you can specify whether the saved files should be post processed (i.e. unshar shell archive, uudecode multiple parts etc). A default process type can be set by the 'Process type:' in the 'M' options menu. .SH "AUTOMATIC MAILING AND SAVING NEW NEWS" \fITin\fP allows new/unread news articles to be mailed (-M option)/saved (-S option) in batch mode for later reading. Useful when going on holiday and you don't want to return and find that expire has removed a whole load of unread articles. Best to run from crontab everyday while away, after which you will be mailed a report of which articles were mailed/saved from which newsgroups and the total number of articles mailed/saved. Articles are saved in a private news structure under your directory (default is \fI$HOME/News\fP). Be careful of using this option if you read a lot of groups because you could overflow your filesystem. If you only want to save a few groups it would be best to backup your full \fI$HOME/.newsrc\fP and create a new one that only contains the newsgroups you want to mail/save. Saved news can be read later by \fItin -R\fP. .PP .TP 2i \fItin -M iain -c -f newsrc.mail\fP (ma(C~ TIN-1_22.BCKsd[SRC.TIN-1_22]TIN.1;1uFQil any unread articles in newgroups specified in file newsrc.mail) .PP .TP 2i \fItin -S -c -f newsrc.save\fP (save any unread articles in newgroups specified in file newsrc.save) .PP .TP 2i \fItin -R\fP (read any articles saved by \fItin -S\fP) .SH SIGNATURES \fITin\fP will recognize a signature in either \fI$HOME/.signature\fP or \fI$HOME/.Sig\fP. If \fI$HOME/.signature\fP exists, then the signature will be pulled into the editor for mail commands. A signature in \fI$HOME/.signature\fP will not be pulled into the editor for posting commands since \fIinews\fP will append the signature itself. .PP A signature in \fI$HOME/.Sig\fP will be pulled into the editor for both posting and mailing commands. .PP The following is an example of a \fI$HOME/.Sig\fP file: .RS .nf .ft CW NAMES Iain Lea iain.lea@erlm.siemens.de SNAIL Bruecken Strasse 12, 8500 Nuernberg 90, Germany PHONE +49-911-331963 (home) +49-911-3089-407 (work) .ft P .fi .RE \fITin\fP also has the capability to generate random signatures on a per newsgroup basis if so desired. The way to accomplish this is to specify the default signature or the group attribute sigfile as a directory. If for example the sigfile path is \fI/usr/iain/.sigs\fP and \fI.sigs\fP is a directory then \fItin\fP will select a random signature from any file that is in the directory \fI.sigs\fP (note: one signature per numbered file). A random signature can also consist of a fixed part signature that can contain your name, address etc. followed by the random sig. The fixed part of the random sig is read from the file \fI$HOME/.sigfixed\fP. .SH "ENVIROMENT VARIABLES" .TP \fBTINRC\fP Define this variable if you want to specify command line options that \fItin\fP should be started with to save typing them each time it is started. The contents of the environment variable are added to the front of the command line options before it is parsed therefore allowing an option specified on the command line to override the same option specified in the environment. .TP \fBTIN_HOMEDIR\fP Define this variable if you do not want the .tin directory in \fI$HOME/.tin\fP. (i.e. if you want all tin's private files in \fI/tmp/.tin\fP you would set TINDIR to \fI/tmp\fP. .TP \fBTIN_INDEXDIR\fP Define this variable if you do not want the .index directory in \fI$HOME/.tin/.index\fP. (i.e. if you want all tin's index files in \fI/tmp/.index\fP you would set TIN_INDEXDIR to \fI/tmp\fP. .TP \fBTIN_LIBDIR\fP Define this variable if you want to override the LIBDIR path that was compiled into the tin binary via the Makefile. .TP \fBTIN_SPOOLDIR\fP Define this variable if you want to override the SPOOLDIR path that was compiled into the tin binary via the Makefile. .TP \fBTIN_NOVROOTDIR\fP Define this variable if you want to override the NOVROOTDIR path that was compiled into the tin binary via the Makefile. .TP \fBTIN_ACTIVEFILE\fP Define this variable if you want to override the LIBDIR/active path that was compiled into the tin binary via the Makefile. .TP \fBNNTPSERVER\fP The default NNTP server to remotely read news from. This variable only needs to be set if the -r command line option is specified and the file \fI/etc/nntpserver\fP does not exist. .TP \fBDISTRIBUTION\fP Set the article header field ``Distribution:'' to the contents of the variable instead of the system default. .TP \fBORGANIZATION\fP Set the article header field ``Organization:'' to the contents of the variable instead of the system default. This variable has precedence over the file \fI$HOME/.tin/organization\fP that may also contain an organization string. If reading news on an Apollo DomainOS machine the environment variable NEWSORG has to be used instead of ORGANIZATION. .TP \fBREPLYTO\fP Set the article header field ``Reply-To:'' to the return address specified by the variable. This is useful if the machine is not registered in the UUCP mail maps or if you wish to receive replies at a different machine. This variable has precedence over the file \fI$HOME/.tin/replyto\fP that may also contain a return address. .TP \fBADD_ADDRESS\fP This can contain an address to append to the return address when replying directly through mail to somebody whose mail address is not directly recognized by the local host. For example say the return address is \fIuser@bigvax\fP, but \fIbigvax\fP is not recognized by your host, so therefore the mail will not reach \fIuser\fP. But the host \fIlittevax\fP is known to recognize your host and \fIbigvax\fP, so if ADD_ADDRESS is set (i.e. 'setenv ADD_ADDRESS @littevax' for csh or 'set ADD_ADDRESS @littevax' and 'export ADD_ADDRESS' for sh) the address \fIuser@bigvax@littlevax\fP will be used and the mail will reach \fIuser@bigvax\fP. This variable has precedence over the file \fI$HOME/.tin/add_address\fP that may also contain an address. .TP \fBBUG_ADDRESS\fP If the 'B' command bug report mail address is not correct this variable should be set to the correct mail address. This variable has precedence over the file \fI$HOME/.tin/bug_address\fP that may also contain a mail address. .TP \fBMAILER\fP This variable has precedence over the default mailer that is used in all mailing operations within \fItin\fP (i.e. replying 'rR', and bug reports 'B'). .TP \fBVISUAL\fP This variable has precedence over the default editor (i.e. vi) that is used in all editing operations within \fItin\fP (i.e. posting 'w', replying 'rR', follow-ups 'fF' and bug reports 'B'). .TP \fBAUTOSUBSCRIBE\fP \fItin\fP interprets this variable similarly to rn. It contains a list of patterns, separated by commas and possibly prefixed with exclamation points. A new group is checked against the list of patterns; if it matches, \fItin\fP subscribes the user to the group without further query. An exclamation point negates the meaning of a match on this pattern, and can be used to cancel certain matches. For example, setting \fIAUTOSUBSCRIBE=comp.os.unix.*,talk.*,!talk.politics.*\fP will automatically subscribe the user to all new groups in the comp.os.unix hierarchy, and all talk groups other than talk.politics groups (which will be queried for as usual.) .TP \fBAUTOUNSUBSCRIBE\fP \fItin\fP interprets this variable similarly to rn. It is handled like the \fIAUTOSUBSCRIBE\fP variable, but groups matching the list are unsubscribed from without further query. For example, setting \fIAUTOUNSUBSCRIBE=alt.flame.*,u*,!uk.*\fP will automatically unsubscribe the user from all new alt.flame groups and all groups starting with u (university groups) other than UK groups (which will be queried for as usual.) .SH "TIPS AND TRICKS" .PP Tin can be pretty much be navigated by using the four cursor keys. The left arrow key goes up a level, the right arrow key goes down a level, the up arrow key goes up a line (page at article viewer level) and the down arrow key goes down a line (page at article viewer level). .PP The following newsgroups provide useful information concerning news software: .in +.5i .ti -\w'\(em'u \(emnews.software.readers (info. about news user agents tin,rn,nn,vn etc.) .ti -\w'\(em'u \(emnews.software.nntp (info. about NNTP) .ti -\w'\(em'u \(emnews.software.b (info. about news transport agents Bnews,Cnews and INN) .ti -\w'\(em'u \(emnews.answers (Frequently Asked Questions (FAQ) about many different themes) .PP Many prompts (i.e. 'Mark everything as read? (y/n): y') within \fItin\fP offer a default choice that the cursor is positioned on. By pressing the default value is taken. .PP Many prompts (i.e. 'Post subject []>') within \fItin\fP can be aborted by pressing ESC. .PP When \fItin\fP is run in an xterm window it will resize itself each time the xterm is resized. .PP \fITin\fP will reread the active file at set intervals to show any newly arrived news. .SH XTERM BUTTONS .PP If the environment variable \fITERM\fP is set to \fIxterm\fP, then button pressing can be used to select groups and articles. .PP In the group selection menu, if the mouse is pointing before the groups listing region the previous page is selected (just like b). If the mouse is pointing after the groups listing region the next page is selected (just like space). If the mouse is pointing at a group then: .TP 15 left button moves to the group pointed at. .TP other buttons moves to and selects the group pointed at. Just like . .PP In the article menu, if the mouse is pointing before the article listing region the previous page is selected (just like b). If the mouse is pointing after the article listing region the next page is selected (just like space). If the mouse is pointing at an article then: .TP 15 left button moves to the article pointed at. .TP centre button reads next unread article from that pointed at. Just like . .TP right button reads article pointed at. Just like . .PP In the thread menu, if the mouse is pointing before the article listing region the previous page is selected (just like b). If the mouse is pointing after the article listing region the next page is selected (just like space). If the mouse is pointing at an article then: .TP 15 left button moves to the article pointed at. .TP centre button reads next unread article from that pointed at. Just like . .TP right button reads article pointed at. Just like . .PP In the spool selection menu, if the mouse is pointing before the spool listing region the previous page is selected (just like b). If the mouse is pointing after the spool listing region the next page is selected (just like space). If the mouse is pointing at a spool selection then: .TP 15 left button moves to the spool pointed at. .TP other buttons moves to and selects the spool pointed at. Just like . .PP In other menus and areas button pressing reverts back to usual cut and paste of xterm, but after one click of any button. .SH FILES .TP 2i \fI$HOME/.newsrc\fP subscribed to newgroups. .TP 2i \fI$HOME/.newsauth\fP ``nntpserver password'' pairs for NNTP servers that require authorization. .TP 2i \fI$HOME/.tin/tinrc\fP options. .TP 2i \fI$HOME/.tin/attributes\fP contains user specified group attributes. .TP 2i \fI$HOME/.tin/.index\fP newsgroups index files directory. .TP 2i \fI$HOME/.tin/.mailidx\fP mailgroups index files directory. .TP 2i \fI$HOME/.tin/.saveidx\fP saved newsgroups index files directory. .TP 2i \fI$HOME/.tin/active.mail\fP active file of users mailgroups. .TP 2i \fI$HOME/.tin/active.save\fP active file of users saved newsgroups. .TP 2i \fI$HOME/.tin/add_address\fP address to add to when replying through mail. .TP 2i \fI$HOME/.tin/bug_address\fP address to send bug reports to. .TP 2i \fI$HOME/.tin/kill\fP article kill and auto-selection file. .TP 2i \fI$HOME/.tin/organization\fP string to replace default organization. .TP 2i \fI$HOME/.tin/posted\fP history of articles posted by user. .TP 2i \fI$HOME/.tin/replyto\fP host address to use in ``Reply-To:'' mail header. .TP 2i \fI$HOME/.signature\fP signature. .TP 2i \fI$HOME/.Sig\fP signature. .TP 2i \fI$HOME/.sigfixed\fP fixed part of a randomly generated signature. .TP 2i \fI/usr/lib/news/motd\fP News message of the day file. .TP 2i \fI/usr/lib/news/newsgroups\fP Short description of all newsgroups. .TP 2i \fI/usr/lib/news/subscriptions\fP List of newsgroups to subscribe first time user to. .SH BUGS There are bugs somewhere among the creeping featurism. Any bugs found should be reported by the 'B' (bug report) command. .PP Coredumps when setting certain toggle options from the options menu at article viewer level. .PP Coredumps when killing last article in a thread at article viewer level. .SH HISTORY Based on the \fItass\fP newsreader that was developed by Rich Skrenta and posted to alt.sources in March 1991. Tass was itself heavily influenced by NOTES which was developed at the University of Illinois by Ray Essick and Rob Kolstad in 1982. .PP v1.0 PL0 (full) was posted in 8 parts to alt.sources on 23 Aug 1991. .br v1.0 PL1 (full) was posted in 8 parts to alt.sources on 03 Sep 1991. .br v1.0 PL2 (full) was posted in 9 parts to alt.sources on 24 Sep 1991. .br v1.0 PL3 (patch) was posted in 4 parts to alt.sources on 30 Sep 1991. .br v1.0 PL4 (patch) was posted in 2 parts to alt.sources on 02 Oct 1991. .br v1.0 PL5 (patch) was posted in 4 parts to alt.sources on 17 Oct 1991. .br v1.0 PL6 (patch) was posted in 5 parts to alt.sources on 27 Nov 1991. .br v1.0 PL7 (patch) was posted in 2 parts to alt.sources on 27 Nov 1991. .br v1.1 PL0 (full) was posted in 11 parts to alt.sources on 13 Feb 1992. .br v1.1 PL1 (full) was posted in 12 parts to alt.sources on 24 Mar 1992. .br v1.1 PL2 (patch) was posted in 4 parts to alt.sources on 30 Mar 1992. .br v1.1 PL3 (full) was posted in 15 parts to alt.sources on 13 May 1992. .br v1.1 PL4 (full) was posted in 15 parts to alt.sources on 22 Jun 1992. .br v1.1 PL5 (patch) was posted in 7 parts to alt.sources on 11 Aug 1992. .br v1.1 PL6 (full) was posted in 15 parts to alt.sources on 14 Sep 1992. .br v1.1 PL7 (patch) was posted in 10 parts to alt.sources on 15 Nov 1992. .br v1.1 PL8 (patch) was posted in 6 parts to alt.sources on 06 Dec 1992. .br v1.1 PL9 (patch) was posted in 3 parts to alt.sources on 20 Mar 1993. .br v1.2 PL0 (full) was posted in 14 parts to alt.sources on 25 May 1993. .br v1.2 PL1 (patch) was posted in 8 parts to alt.sources on 14 Jul 1993. .br v1.2 PL2 (patch) was posted in ?? parts to alt.sources on ?? Sep 1993. .SH CREDITS .TP 1.5i Rich Skrenta author of tass v3.2 which this newsreader used as its base. .TP 1.5i Bill Davidsen author of envarg.c environment variable reading routine. .TP 1.5i Mike Gleason author of sigfile.c random signature generation routines. Arnold Robbins author of strftime.c date formatting routine. .TP 1.5i Jim Robinson co-author of kill.c article kill and auto-selection routines. .TP 1.5i Rich Salz author of wildmat.c pattern matching and parsedate.y date parsing routines. .TP 1.5i Dave Taylor author of curses.c from the elm mailreader. .TP 1.5i Chris Thewalt author of getline.c emacs style editing routine. .TP 1.5i Mark Tomlinson for porting \fItin\fP to the AmigaDOS operating system. .TP 1.5i Andreas Wrede for porting \fItin\fP to the OS/2 operating system. .TP 1.5i Dieter Becker for generously posting certain releases for me when my net connection was removed by a group of very short sighted people. .PP I wish to thank the following people for supplying patches: .PP David Abbott, Earle Ake, Joachim Astel, Anton Aylward, George Baltz, Paul Bauwens, Dieter Becker, Dan Berry, David Binderman, Fokke de Boer, Mark Boucher, Herman ten Brugge, Leila Burrell-Davis, Peter Castro, Robert Claeson, Steven Cogswell, Don Costello, Bryan Curnutt, Ned Danieley, Chris Davies, John Davis, Tom Dickey, Bryan Dongray, Craig Durland, Kirk Edson, Stefan Elf, Rob Engle, Brent Ermlick, Olle Eriksson, Michael Faurot, Werner Fleck, Callum Gibson, Mike Glendinning, Philippe Goujard, Carl Hage, Paul Halsema, Ed Hanway, Scott Hauck, Per Headland, Daniel Hermans, Jose Herrero, Tom Hite, Torsten Homeyer, Tommy Hsieh, Steve Hunt, Pieter Immelman, Robbin Johnson, Nelson Kading, Fritz Kleeman, Dwarven Knight, Karl-Koenig Koenigsson, Martin Kraemer, Kris Kugel, Geoff Lane, Alex Lange, Alain Lasserre, Marty Leisner, Hakan Lennestal, Otto Lind, Richard Lloyd, Clifford Luke, David MacKenzie, Hugh Mahon, Kazushi Marukawa, Owen Medd, Soren Moller, Sergio Morales, Michael Morrell, Klaus Mueller, Udo Munk, James Nugen, Jeb Palmer, Neil Parker, Tom Parry, Jim Patterson, Walter Pelissero, Colin Perkins, Eric Peterson, Tim Pierce, Bill Poitras, Wolfgang Prediger, Ted Richards, Ollivier Robert, Jim Robinson, Stephen Roseman, Clifton Royston, Nickolay Saukh, Rich Salz, Gary Sanders, John Sauter, Christopher Sawtell, John Schmitz, Bart Sears, Karl-Olav Serrander, Doug Sewell, Philip Shearer, Mark Smith, Steve Spearman, Cliff Stanford, Steve Starck, Jason Steiner, Ed Sznyter, Derek Terveer, Julian Thompson, Andry Timonin, Mark Tomlin, Michael Traub, Adri Verhoef, Paul Vickers, Cary Whitney, Greg Woods, Lloyd Wright .PP I wish to thank the following people for bug reports/comments: .PP Jack Applin, Klaus Arzig, Scott Babb, Reiner Balling, Preston Bannister, Bill de Beabien, Volker Beyer, Etienne Bido, Roger Binns, Georg Biehler, Jean-Marc Bonnaudet, Eric Bowles, Sean Brady, Ian Brown, Andreas Brosig, Craig Bruce, Brett Carver,Tom Czarnik, Dave Datta, Mat Davis, Karl Denninger, Klaus Dimmler, David Donovan, Peter Dressler, Gerhard Ermer, Hugh Fader, Miguel Farah, Joachim Feld, Paul Fox, Jay Geertsen, Herschel Gelman, Bernhard Gmelch, Jason Haar, Viet Hoang, Andy Jackson, Joe Johnson, Ralph Jud, Cyrill Jung, Kuo-Chein Kai, Tonis Kelder, Hans-Juergen Knopp, Sridhar Komandur, Tom Kovar, Bernhard Kroenung, Murray Laing, Per Lindqvist, Eric Litman, Bob Lukas, Michael Marshall, Kazushi Marukawa, Olaf Mittelstaedt, Phillip Molloy, Phil Molyneux, Toni Metz, Greg Miller, Deeptendu Majumder, Klaus Neuberger, Otto Niesser, Reiner Oelhaf, Alex Pakter, John Palkovic, Dave Pascoe, Wolf Paul, Andrew Phillips, Stefan Rathmann, Jon Robinson, David Ross, Jonas Rwgmyr, Malkani Sanjay, Daemon Schaefer, Dean Schrimpf, Klamer Schutte, Fredy Schwatz, Dave Schweisguth, Bernd Schwerin, Don Sheythe, Chris Smith, Daniel Smith, Richard Stanton, Ralf Stephan, Hironobu Takahashi, Ken Taylor, Tony Travis, Paul Verket, Sven Werner, Dick Wexelblat, Paul Wood, Gregory Woodbury, Norm Yamane, Blair Zajac, Orest Zboroski, Thomas Ziegler .SH AUTHOR .TP 1i Iain Lea (iain.lea@erlm.siemens.de) *[SRC.TIN-1_22]TIN.H;29+, .<// 4<:-d0@123KPWO=56az򘒗789IʒG/HJ$/* * Project : tin - a Usenet reader * Module : tin.h * Author : I.Lea & R.Skrenta * Created : 01-04-91 * Updated : 22-09-93 * Notes : #include files, #defines & struct's * Copyright : (c) Copyright 1991-93 by Iain Lea & Rich Skrenta * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ /* * OS specific doda's */ #include "config.h" #include #include #include #ifdef VMS #define NNTP_ONLY #define NNTP_INEWS #define DONT_LOG_USER #define NO_PIPING #define NO_SHELL_ESCAPE #define USE_CLEARSCREEN extern char *get_uaf_fullname(); #define qsort my_qsort #endif #ifdef M_AMIGA # include # include #else # ifdef apollo # include # include # include # else # ifdef VMS # include "select.h" # include "vmstimval.h" # endif # include # include # ifdef HAVE_SYS_TIME_H # include # endif # ifdef HAVE_SYS_TIMES_H # include # endif # ifdef HAVE_TIME_H # include # endif # endif # ifdef HAVE_UNISTD_H # include # endif # ifdef HAVE_PWD_H # include # endif # ifdef HAVE_SYS_PARAM_H # include # endif #endif #include #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRINGS_H # include #else # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_PROTOTYPES_H # include #endif #ifdef HAVE_LOCALE_H # include #endif #ifdef HAVE_SYS_UTSNAME_H # include #endif /* * Needed for catching child processes */ #ifdef HAVE_SYS_WAIT_H # include #endif /* * Needed for timeout in user abort of indexing a group (BSD & SYSV variaties) */ #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_STROPTS_H # include #endif #ifdef HAVE_POLL_H # include #endif /* * Needed for resizing under an xterm */ #ifdef HAVE_TERMIOS_H # include #endif #if defined(SIGWINCH) && !defined(DONT_HAVE_SIGWINCH) # if !defined(TIOCGWINSZ) && !defined(TIOCGSIZE) # ifdef HAVE_SYS_STREAM_H # include # endif # ifdef HAVE_TERMIO_H # include # else # ifdef HAVE_SYS_PTEM_H # include # include # endif # ifdef HAVE_SYS_PTY_H # if !defined(_h_BSDTYPES) && !defined(DONT_HAVE_SYS_BSDTYPES_H) # include # endif # include # endif # endif # endif #endif #ifdef HAVE_CURSES_H # include #endif /* * Directory handling code */ #if defined(BSD) && !defined(__386BSD__) && !defined(M_OS2) # ifdef sinix # include # else # if defined(__arm) || defined(__osf__) # include # define DIR_BUF struct dirent # else # include # endif # endif # ifndef DIR_BUF # define DIR_BUF struct direct # endif # define D_LENGTH d_namlen #endif #ifdef M_AMIGA # include "amiga.h" # define DIR_BUF struct dirent # define D_LENGTH d_reclen #endif #ifdef M_OS2 # include "os_2.h" # define DIR_BUF struct dirent # define D_LENGTH d_reclen #endif #ifdef M_XENIX # include # define DIR_BUF struct direct # define D_LENGTH d_namlen #endif #ifdef VMS # include "ndir.h" # define DIR_BUF struct direct # define D_LENGTH d_namlen #endif #ifndef DIR_BUF # include # define DIR_BUF struct dirent # define D_LENGTH d_reclen #endif /* * If native OS has'nt defined STDIN_FILENO be a smartass and do it */ #if !defined(STDIN_FILENO) # define STDIN_FILENO 0 #endif /* * Setup support for reading from CD-ROM */ #ifdef CDROM_ONLY # define CDROM_ABLE #endif #ifdef CDROM_ABLE # define XSPOOLDIR # define NNTP_ABLE # undef NNTP_ONLY # undef NNTP_INEWS #endif /* * Setup support for reading from NNTP */ #if defined(NNTP_ABLE) || defined(NNTP_ONLY) # ifndef NNTP_ABLE # define NNTP_ABLE # endif # ifndef NNTP_INEWS # define NNTP_INEWS # endif #endif /* * Time idle after which to check (via STAT) if nntp connection is still ok */ #define NNTP_IDLE_RETRY_SECS 300 /* * Index file daemon version of tin. Will create/update index files from cron * on NNTP server machine so clients can retreive index file with NNTP XINDEX * command from server. Also can be used on machines that just want one copy * of all the index files in one place. In this case the normal tin must have * access to the index directory (-I dir option) or be setuid news. */ #ifdef INDEX_DAEMON # define LOCK_FILE "tind.LCK" # undef HAVE_POLL # undef HAVE_SELECT # undef NNTP_ABLE # undef NNTP_ONLY # undef NNTP_INEWS #endif /* * Specify News spool & control directories */ #ifndef SPOOLDIR # ifdef VMS # define SPOOLDIR "NEWSSPOOL:[000000]" # else # define SPOOLDIR "/usr/spool/news" # endif #endif #ifndef LIBDIR # ifdef VMS # define LIBDIR "NEWSLIB:[000000]" # else # define LIBDIR "/usr/lib/news" # endif #endif #ifndef NOVROOTDIR # define NOVROOTDIR SPOOLDIR #endif #ifndef INEWSDIR # define INEWSDIR LIBDIR #endif /* * Determine machine configuration for external programs & directories */ #ifndef BSD # ifndef bcopy # define bcopy(a,b,c) memcpy(b,a,c) # define bzero(a,b) memset(a,'\0',b) # endif #endif #ifdef BSD # ifdef DONT_HAVE_MEMCMP # define memcmp(s1, s2, n) bcmp(s2, s1, n) # endif # define strchr(str, ch) index(str, ch) # define strrchr(str, ch) rindex(str, ch) # define DEFAULT_SHELL "/bin/csh" # if defined(__386BSD__) || defined(__bsdi__) # define DEFAULT_EDITOR "/usr/bin/vi" # define DEFAULT_PRINTER "/usr/bin/lpr" # define DEFAULT_MAILER "/usr/sbin/sendmail" # define DEFAULT_MAILBOX "/var/mail" # else # define DEFAULT_EDITOR "/usr/ucb/vi" # define DEFAULT_PRINTER "/usr/ucb/lpr" # endif # define DEFAULT_SUM "sum" # ifdef DGUX # define DEFAULT_MAILBOX "/usr/mail" # define USE_INVERSE_HACK # endif # ifdef pyr # define DEFAULT_MAILER "/usr/.ucbucb/mail" # endif # ifndef DEFAULT_MAILER # define DEFAULT_MAILER "/usr/lib/sendmail" # endif # ifndef DEFAULT_MAILBOX # define DEFAULT_MAILBOX "/usr/spool/mail" # endif #else # if defined(NCR) || defined(atthcx) || defined(PTX) || defined(sinix) # define DEFAULT_MAILER "/usr/bin/mailx" # endif # if defined(__hpux) || defined(u3b2) # define DEFAULT_MAILER "/usr/lib/sendmail" # endif # ifdef linux # define DEFAULT_MAILBOX "/usr/spool/mail" # define DEFAULT_MAILER "/usr/bin/smail" # define DEFAULT_PRINTER "/usr/bin/lpr" # endif # ifdef M_AMIGA # define DEFAULT_EDITOR "c:ed" # define DEFAULT_MAILBOX "uumail:" # define DEFAULT_MAILER "uucp:c/sendmail <%s -f %s" # define DEFAULT_POSTER "uucp:c/postnews %s" # define DEFAULT_PRINTER "c:copy PIPE: NIL:" # define DEFAULT_SHELL "c:newshell" /* Not Yet Implemented */ # define DEFAULT_UUDECODE "uudecode %s" # define DEFAULT_UNSHAR "unshar %s" # endif # ifdef VMS # define DEFAULT_EDITOR "EDIT/EDT" # define DEFAULT_MAILBOX "SYS$LOGIN:" # define DEFAULT_MAILER "MAIL %s /PERSONAL=%s SMTP%%\"\"\"%s\"\"\"" # define DEFAULT_POSTER "inews %s" # define DEFAULT_PRINTER "PRINT" # define DEFAULT_UUDECODE "uudecode %s" # define DEFAULT_UNSHAR "unshar %s" # endif # ifdef M_OS2 # define DEFAULT_EDITOR "epm /m" # define DEFAULT_MAILBOX "/mail" # define DEFAULT_MAILER "sendmail -af %s -f %s %s" # define DEFAULT_POSTER "postnews %s" # define DEFAULT_PRINTER "lpt1" # define DEFAULT_SHELL "cmd.exe" # define DEFAULT_UUDECODE "uudecode %s" # define DEFAULT_UNSHAR "unshar %s" # endif # ifdef M_XENIX # define DEFAULT_EDITOR "/bin/vi" # define DEFAULT_MAILER "/usr/bin/mail" # define DEFAULT_MAILBOX "/usr/spool/mail" # endif # ifdef QNX4 # define DEFAULT_EDITOR "/bin/vedit" # define DEFAULT_MAILER "/usr/bin/sendmail" # define DEFAULT_MAILBOX "/usr/spool/mail" # endif # ifdef RS6000 # define DEFAULT_MAILER "/usr/lib/sendmail" # define DEFAULT_PRINTER "/bin/lp" # endif # ifdef SCO_UNIX # define HAVE_MMDF_MAILER # endif # ifdef sinix # define DEFAULT_PRINTER "/bin/lpr" # endif # ifdef sysV68 # define DEFAULT_MAILER "/bin/rmail" # endif # ifdef UNIXPC # define DEFAULT_MAILER "/bin/rmail" # endif # ifndef DEFAULT_SHELL # define DEFAULT_SHELL "/bin/sh" # endif # ifndef DEFAULT_EDITOR # define DEFAULT_EDITOR "/usr/bin/vi" # endif # ifndef DEFAULT_MAILBOX # define DEFAULT_MAILBOX "/usr/mail" # endif # ifndef DEFAULT_MAILER # define DEFAULT_MAILER "/bin/mail" # endif # ifndef DEFAULT_PRINTER # define DEFAULT_PRINTER "/usr/bin/lp" # endif # define DEFAULT_SUM "sum -r" #endif #ifdef HAVE_LONG_FILENAMES # define LONG_PATH_PART "part" # define LONG_PATH_PATCH "patch" #else # define LONG_PATH_PART "" # define LONG_PATH_PATCH "p" #endif /* * Useful for logging user usage */ #define LOG_USER_FILE ".tin_log" /* * Should active file be reread for new news & if so how often */ #ifdef DONT_REREAD_ACTIVE_FILE # define REREAD_ACTIVE_FILE_SECS 0 #else # ifndef REREAD_ACTIVE_FILE_SECS # define REREAD_ACTIVE_FILE_SECS 600 /* seconds */ # endif #endif /* * Initial sizes of internal arrays for small (<4MB) & large memory machines */ #ifdef SMALL_MEMORY_MACHINE # define DEFAULT_ACTIVE_NUM 1800 # define DEFAULT_ARTICLE_NUM 600 # define DEFAULT_KILL_NUM 10 # define DEFAULT_SAVE_NUM 10 # define DEFAULT_SPOOLDIR_NUM 5 # define DEFAULT_ACTIVE_SIZE_NUM 5 #else # define DEFAULT_ACTIVE_NUM 1800 # define DEFAULT_ARTICLE_NUM 1200 # define DEFAULT_KILL_NUM 30 # define DEFAULT_SAVE_NUM 30 # define DEFAULT_SPOOLDIR_NUM 10 # define DEFAULT_ACTIVE_SIZE_NUM 10 #endif #ifdef VMS #define RCDIR "TIN" #define INDEX_MAILDIR "MAILIDX" #define INDEX_NEWSDIR "INDEX" #else #define RCDIR ".tin" #define INDEX_MAILDIR ".mailidx" #define INDEX_NEWSDIR ".index" #endif #define RCFILE "tinrc" #define ACTIVE_FILE "active" #define ACTIVE_MAIL "active.mail" #define ACTIVE_SAVE "active.save" #define ACTIVE_TIMES "active.times" #define KILLFILE "kill" #define POSTFILE "posted" #define DEFAULT_MAILDIR "Mail" #define DEFAULT_SAVEDIR "News" #define MAILGROUPS_FILE "mailgroups" #define MOTD_FILE "motd" #define OVERVIEW_FILE ".overview" #define OVERVIEW_FMT "overview.fmt" #define SUBSCRIPTIONS_FILE "subscriptions" #define _CONF_FROMHOST "fromhost" #define _CONF_ORGANIZATION "organization" #define _CONF_SERVER "server" #ifdef TRUE # undef TRUE #endif #define TRUE 1 #ifdef FALSE # undef FALSE #endif #define FALSE 0 #ifdef VMS # define LEN 512 # define PATH_LEN 512 #endif #ifdef M_AMIGA # define LEN 512 # define PATH_LEN 128 #endif #if defined(M_OS2) || defined(M_UNIX) # ifndef MAXPATHLEN # define MAXPATHLEN 256 # endif # define PATH_LEN MAXPATHLEN # define LEN 1024 #endif #define NEWSRC_LINE 8192 #ifdef HAVE_MAIL_HANDLER # define HEADER_LEN 8192 #else # define HEADER_LEN 2048 #endif #define MODULO_COUNT_NUM 10 #define TABLE_SIZE 1409 #define MAX_PAGES 1000 #define ctrl(c) ((c) & 0x1F) #ifndef DEFAULT_COMMENT # define DEFAULT_COMMENT ": " /* used when by follow-ups & replys */ #endif #ifndef UNREAD_ART_MARK # define UNREAD_ART_MARK '+' /* used to show that an art is unread */ #endif #ifndef RETURN_ART_MARK # define RETURN_ART_MARK '-' /* used to show that an art will return */ #endif #ifndef HOT_ART_MARK # define HOT_ART_MARK '*' /* used to show that an art was auto selected */ #endif #ifndef READ_ART_MARK # define READ_ART_MARK ' ' /* used to show that an art was not read or seen */ #endif #ifdef USE_INVERSE_HACK # define SELECT_MISC_COLS 21 # define BLANK_GROUP_COLS 2 # define BLANK_PAGE_COLS 2 #else # define SELECT_MISC_COLS 21 # define BLANK_GROUP_COLS 0 # define BLANK_PAGE_COLS 0 #endif #define SCREEN_READ_UNREAD 6 /* position for " +" / " " */ #define INDEX_TOP 2 #ifdef NO_REGEX # define STR_MATCH(s1,s2) (str_str (s1, s2, strlen (s2)) != 0) #else # define STR_MATCH(s1,pat) (wildmat (s1, pat)) #endif #define IGNORE_ART(i) ((arts[i].thread == ART_EXPIRED) || \ (arts[i].killed && kill_level > 0)) /* * News/Mail group types */ #define GROUP_TYPE_MAIL 0 #define GROUP_TYPE_NEWS 1 #define GROUP_TYPE_SAVE 2 /* * used by get_arrow_key() */ #define KEYMAP_UNKNOWN 0 #define KEYMAP_UP 1 #define KEYMAP_DOWN 2 #define KEYMAP_LEFT 3 #define KEYMAP_RIGHT 4 #define KEYMAP_PAGE_UP 5 #define KEYMAP_PAGE_DOWN 6 #define KEYMAP_HOME 7 #define KEYMAP_END 8 #define KEYMAP_MOUSE 9 /* * used by feed_articles() & show_mini_help() */ #define SELECT_LEVEL 1 #define SPOOLDIR_LEVEL 2 #define GROUP_LEVEL 3 #define THREAD_LEVEL 4 #define PAGE_LEVEL 5 #define MINI_HELP_LINES 5 #define FEED_MAIL 1 #define FEED_PIPE 2 #define FEED_PRINT 3 #define FEED_SAVE 4 #define FEED_XPOST 5 /* * used in art.c & rcfile.c */ #define SORT_BY_NOTHING 0 /* sort types on arts[] array */ #define SORT_BY_SUBJ_DESCEND 1 #define SORT_BY_SUBJ_AS)nl~ TIN-1_22.BCK d[SRC.TIN-1_22]TIN.H;29<ja@CEND 2 #define SORT_BY_FROM_DESCEND 3 #define SORT_BY_FROM_ASCEND 4 #define SORT_BY_DATE_DESCEND 5 #define SORT_BY_DATE_ASCEND 6 #define SHOW_FROM_NONE 0 #define SHOW_FROM_ADDR 1 #define SHOW_FROM_NAME 2 #define SHOW_FROM_BOTH 3 /* * used in help.c */ #define HELP_INFO 0 #define POST_INFO 1 /* * used in save.c */ #define CHECK_ANY_NEWS 0 #define START_ANY_NEWS 1 #define MAIL_ANY_NEWS 2 #define SAVE_ANY_NEWS 3 /* * used in help.c */ #define HEADER_TO 0 #define HEADER_SUBJECT 1 #define HEADER_NEWSGROUPS 2 /* * used in page.c & post.c */ #define POSTED_NONE 0 #define POSTED_REDRAW 1 #define POSTED_OK 2 /* * index_point variable values used throughout many modules */ #define GRP_UNINDEXED -1 /* Stop reading group */ #define GRP_QUIT -2 /* Set by 'Q' */ #define GRP_GOTONEXT -3 /* Goto another group */ #define GRP_CONTINUE -4 /* set in show_page() */ #define GRP_NOREDRAW -5 /* Unclear meaning ? */ /* * Assertion verifier */ #if !defined(M_OS2) # ifdef HAVE_ANSI_ASSERT # define assert(p) if(! (p)) asfail(__FILE__, __LINE__, #p); else # else # define assert(p) if(! (p)) asfail(__FILE__, __LINE__, "p"); else # endif #endif #define ESC 27 #ifdef HAVE_CR_AS_CHAR # define CR '\r' #else # define CR 10 #endif /* * return codes for change_rcfile () */ #define NO_KILLING 0 #define KILLING 1 /* * art.thread */ #define ART_NORMAL -1 #define ART_EXPIRED -2 /* * art.unread */ #define ART_READ 0 #define ART_UNREAD 1 #define ART_WILL_RETURN 2 #define ART_UNAVAILABLE -1 /* * used by group_t & my_group[] */ #define UNSUBSCRIBED 0x01 /* haven't put in my_group[] yet */ #define SUBSCRIBED 0x02 /* subscribed to */ /* * kill_type used in struct kill_t */ #define KILL_SUBJ 1 #define KILL_FROM 2 #define KILL_BOTH 3 /* * used in feed.c & save.c */ #define POST_PROC_NONE 0 #define POST_PROC_SHAR 1 #define POST_PROC_UUDECODE 2 #define POST_PROC_UUD_LST_ZOO 3 #define POST_PROC_UUD_EXT_ZOO 4 #define POST_PROC_UUD_LST_ZIP 5 #define POST_PROC_UUD_EXT_ZIP 6 /* * used in checking article header before posting */ #define NGLIMIT 20 /* Max. num. of crossposted groups before warning */ #define MAX_COL 78 /* Max. line length before issuing a warning */ /* * The following macros are used to simplify and speed up the * manipulation of the bitmaps in memory which record which articles * are read or unread in each news group. * * Data representation: * * Each bitmap is handled as an array of bytes; the least-significant * bit of the 0th byte is the 0th bit; the most significant bit of * the 0th byte is the 7th bit. Thus, the most-significant bit of the * 128th byte is the 1023rd bit, and in general the mth bit of the nth * byte is considered to be bit (n*8)+m of the map as a whole. Conversely, * the position of bit q in the map is the bit (q & 7) of byte (q >> 3). * A bitmap of b bits will be allocated as ((b+7) >> 3) bytes. * * The routines could be changed to operate on a word-oriented bitmap by * changing the constants used from 8 to 16, 3 to 4, 7 to 15, etc. and * changing the allocate/deallocate routines. * * In the newsrc context, a 0 bit represents an article which is read * or expired; a 1 represents an unread article. The 0th bit corresponds * to the minimum article number for this group, and (max-min+7)/8 bytes * are allocated to the bitmap. * * Constants: * * NMAXBIT = number of bits per byte or word; * NBITPOS = number of bits in NMAXBIT; * NBITSON = byte/word used to set all bits in byte/word to 1; * NBITNEG1 = binary negation of 1, used in constructing masks. * * Macro naming and use: * * The NOFFSET and NBITIDX macro construct the byte and bit indexes in * the map, given a bit number. * * The NSETxxx macros set a bit or bits to binary 1; * the NRESETxxx macros set the same bit or bits to binary 0; * the NTEST macro tests a single bit. * These are used frequently to access the group bitmap. * * NSETBLK and NRESETBLK operate on whole numbers of bytes, and are * mainly useful for initializing complete bitmaps to one state or * another. Both use the memset function, which is assumed to be * optimized for the target architecture. NSETBLK is currently used to * initialize the group bitmap to 1s (unread). * * NSETRNG and NRESETRNG operate on ranges of bits, from a low bit number * to a high bit number (inclusive), and are especially useful for * efficiently setting a contiguous range of bits to one state or another. * NRESETRNG is currently used on the group bitmap to mark the ranges the * newsrc file says are read or expired. * * Their algorithm is this. If the high number is less than the low, then * do nothing (error); if both fall within the same byte, construct a * single mask expressing the range and AND or OR it into the byte; else: * construct a mask for the byte containing the low bit, AND or OR it in; * use memset to fill in the intervening bytes efficiently; then construct * a mask for the byte containing the high bit, and AND or OR this mask * in. Masks are constructed by left-shift of 0xff (to set high-order bits * to 1), negating a left-shift of 0xfe (to set low-order bits to 1), and * the various negations and combinations of the same. This procedure is * complex, but 1 to 2 orders of magnitude faster than a shift inside a * loop for each bit inside a loop for each individual byte. * */ #define NMAXBIT 7 #define NBITPOS 3 #define NBITSON 0xff #define NBITNEG1 0xfe #define NOFFSET(b) ((b) >> NBITPOS) #define NBITIDX(b) ((b) & NMAXBIT) #define NSET(n,b) (n[(b) >> NBITPOS] |= 1 << ((b) & NMAXBIT)) #define NRESET(n,b) (n[(b) >> NBITPOS] &= ~(1 << ((b) & NMAXBIT))) #define NTEST(n,b) (n[(b) >> NBITPOS] & (1 << ((b) & NMAXBIT))) #define NSETBLK(n,i) (memset(n,NBITSON,(size_t)(NOFFSET(i)+1))) #define NRESETBLK(n,i) (memset(n,0,(size_t)(NOFFSET(i)+1))) /* * Following macros (could be made functions) fill in a range of a bit map */ #define NSETRNG(n,l,h) (\ ( h < l ) ? \ 0 : \ ( NOFFSET(h) == NOFFSET(l) ) ? \ n[ NOFFSET(l) ] |= ( NBITSON << NBITIDX(l) ) & \ ~ ( NBITNEG1 << NBITIDX(h) ) : \ ( /* else */ \ n[ NOFFSET(l) ] |= ( NBITSON << NBITIDX(l) ), \ ( (NOFFSET(h)-NOFFSET(l)-1)>0 ) ? \ (int)memset( &n[NOFFSET(l)+1],NBITSON,(size_t)(NOFFSET(h)-NOFFSET(l)-1) ) : \ 0, \ n[ NOFFSET(h) ] |= ~ ( NBITNEG1 << NBITIDX(h) ) \ ) ) #define NRESETRNG(n,l,h) (\ ( h < l ) ? \ 0 : \ ( NOFFSET(h) == NOFFSET(l) ) ? \ n[ NOFFSET(l) ] &= ( NBITNEG1 << NBITIDX(h) ) | \ ~ ( NBITSON << NBITIDX(l) ) : \ ( /* else */ \ n[ NOFFSET(l) ] &= ~ ( NBITSON << NBITIDX(l) ), \ ( (NOFFSET(h)-NOFFSET(l)-1)>0 ) ? \ (int)memset( &n[NOFFSET(l)+1],0,(size_t)(NOFFSET(h)-NOFFSET(l)-1) ) : \ 0, \ n[ NOFFSET(h) ] &= ( NBITNEG1 << NBITIDX(h) ) \ ) ) /* * struct t_article - article header * * article.artnum: * article number in spool directory for group * * article.thread: * -1 initial default * -2 means article has expired (wasn't found in file search * of spool directory for the group) * >=0 points to another arts[] (struct article_t) * * article.inthread: * FALSE for the first article in a thread, TRUE for all * following articles in thread * * article.unread: * boolean, has this article been read or not * * article.killed: * boolean, has this article been killed * * article.hot: * boolean, has this article been auto-selected * * article.tagged: * count, has this article been tagged for saving (>0) or not (=0) * * article.date * date: line used for sorting articles by date order * * article.archive: * archive name used in *source* groups * * article.part: * part no. of archive * * article.patch: * patch no. of archive * */ struct t_article { long artnum; char *subject; /* Subject: line from mail header */ char *from; /* From: line from mail header (address) */ char *name; /* From: line from mail header (full name) */ long date; /* Date: line from header in seconds */ char *xref; /* Xref: cross posted article reference line */ int lines; /* Lines: number of lines in article */ char *archive; /* Archive-name: line from mail header */ char *part; /* part no. of archive */ char *patch; /* patch no. of archive */ int tagged; /* 0 = not tagged, >0 = tagged */ int thread; unsigned int inthread:1;/* 0 = thread head, 1 = thread follower */ unsigned int unread:2; /* 0 = read, 1 = unread, 2 = will return */ unsigned int killed:1; /* 0 = not killed, 1 = killed */ unsigned int hot:1; /* 0 = not hot, 1 = hot */ unsigned int zombie:1; /* 1 = was alive (unread) before 'X' command */ unsigned int o_unread:2;/* original value of unread - used in xref */ }; /* * struct attribute_t - configurable attributes on a per group basis */ struct t_attribute { char *maildir; /* mail dir if other than ~/Mail */ char *savedir; /* save dir if other than ~/News */ char *sigfile; /* sig file if other than ~/.Sig */ char *organization; /* organization name */ char *followup_to; /* where posts should be redirected */ char *printer; /* printer command & parameters */ unsigned int read_during_session:1; /* marked TRUE if group entered during session */ unsigned int auto_save:1; /* 0=none, 1=save */ unsigned int batch_save:1; /* 0=none, 1=save -S/mail -M */ unsigned int delete_tmp_files:1; /* 0=leave, 1=delete */ unsigned int show_only_unread:1; /* 0=all, 1=only unread */ unsigned int thread_arts:1; /* 0=unthread, 1=thread */ unsigned int show_author:4; /* 0=none, 1=name, 2=addr, 3=both */ unsigned int sort_art_type:4; /* 0=none, 1=subj descend, 2=subj ascend, 3=from descend, 4=from ascend, 5=date descend, 6=date ascend */ unsigned int post_proc_type:4; /* 0=none, 1=shar, 2=uudecode, 3=uud & list zoo, 4=uud & ext zoo*/ }; /* * struct t_group - newsgroup info from active file */ struct t_group { char *name; /* newsgroup / mailbox name */ char *description; /* text from LIBDIR/newsgroups file */ char *spooldir; /* groups spool directory */ char moderated; /* state of group moderation */ long max; /* max. article number */ long min; /* min. article number */ int type; /* grouptype - newsgroup / mailbox */ int next; /* next active entry in hash chain */ int my_group; /* subscribed/unsubscribed to group */ int unread; /* unread articles in group */ long newsrcmax; /* newsrc max (lastmax) */ long newsrcmin; /* newsrc min (lastmin) */ long newsrcsize; /* newsrc size (max-min+1) */ int newsrcupdate; /* newsrc update */ char *newsrc; /* newsrc read/unread (max-min+1+7)/8*/ struct t_attribute attribute; /* group specific attributes */ #ifdef INDEX_DAEMON long last_updated_time; /* last time group dir was changed */ #endif }; /* * used in hashstr.c */ struct t_hashnode { char *s; /* the string we're saving */ struct t_hashnode *next; /* chain for spillover */ }; /* * used in filter.c (new name for kill.c) * * Create 2 filter arrays - global & local. Local will be part of group_t * structure and will have priority over global filter. Should help to * speed kill/selecting within a group. The long value number that is in * ~/.tin/kill will be replaced by group name so that it is more human * readable and that if hash routine is changed it will still work. * * Add time period to filter_t struct to allow timed kills & auto-selection * Default kill & select time 30 days. Store as a long and compare when * loading against present time. If time secs is passed set flag to save * filter file and don't load expired entry. Renamed to filter because of * future directions in adding other retrieval methods to present kill & * hot selection. * * Also seperate kill/select screen to allow ^K=kill ^A=auto-select */ struct t_kill { unsigned int kill_type:8; unsigned int kill_how:8; /* kill/auto select */ long kill_group; char *kill_subj; char *kill_from; }; struct t_save { char *subject; char *dir; char *file; char *archive; char *part; char *patch; int index; int saved; int is_mailbox; }; struct t_screen { char *col; }; struct t_posted { char date[10]; char group[80]; char action; char subj[120]; }; struct t_art_stat { int total; /* total article count */ int unread; /* number of unread articles (does not include seen) arts */ int seen; /* number of seen articles (ART_WILL_RETURN) */ int hot_total; /* total hot count */ int hot_unread; /* hot and unread */ int hot_seen; /* hot and seen */ char art_mark; /* mark to use for this thread - not used for groups */ }; /* * Used by spooldir command */ struct t_spooldir { int state; char *name; char *comment; }; /* * Used for detecting changes in active file size on different news servers */ struct t_active_size { char *server; char *attribute; }; /* * Time functions. */ typedef struct _TIMEINFO { time_t time; long usec; long tzone; } TIMEINFO; /* * Used for detecting new groups when reading news locally. It's easy to be * confused by arrays of pointers to pointers, so typedef's are used for the * first level pointers to keep it clearer. */ struct t_notify { char name[PATH_LEN]; int visited; }; typedef struct t_group *group_p; typedef struct t_notify *notify_p; /* * Determine signal return type */ #ifdef HAVE_SIGTYPE_VOID typedef void t_sigtype; #endif #ifdef HAVE_SIGTYPE_INT typedef int t_sigtype; #endif /* * Determine qsort compare type */ #ifdef HAVE_COMPTYPE_VOID typedef const void t_comptype; #endif #ifdef HAVE_COMPTYPE_CHAR typedef char t_comptype; #endif #ifdef M_OS2 # define SIGTYPE int # define _CDECL _cdecl # define _FAR_ _far16 # define SEPDIR '\\' #else # define SIGTYPE # define _CDECL # define _FAR_ # define SEPDIR '/' #endif #define EDITOR_FORMAT_OFF "%E %F" #define METAMAIL_CMD "metamail -e -p -m \"tin\"" #ifdef M_AMIGA # define NEWSGROUPS_FILE "newsdescrip" # define BUG_REPORT_ADDRESS "mark@garden.equinox.gen.nz" # define REDIRECT_OUTPUT "> NIL:" # define ENV_VAR_GROUPS "TIN_GROUPS" # define ENV_VAR_MAILER "TIN_MAIL" # define ENV_VAR_POSTER "TIN_POST" # define ENV_VAR_SHELL "SHELL" # define EDITOR_FORMAT_ON "%E %F" # define TMPDIR "T:" # ifdef HAVE_KEY_PREFIX # define KEY_PREFIX 0x9b # endif extern void joinpath (char *result, char *dir, char *file); #endif #ifdef M_OS2 # define NEWSGROUPS_FILE "newsgroups" # define BUG_REPORT_ADDRESS "andreas@scilink.org" # define REDIRECT_OUTPUT "> NUL" # define ENV_VAR_GROUPS "TIN_GROUPS" # define ENV_VAR_MAILER "TIN_MAIL" # define ENV_VAR_POSTER "TIN_POST" # define ENV_VAR_SHELL "COMSPEC" # define EDITOR_FORMAT_ON "%E %F" extern void joinpath (char *result, char *dir, char *file); #endif #ifdef M_UNIX # define NEWSGROUPS_FILE "newsgroups" # define BUG_REPORT_ADDRESS "iain.lea@erlm.siemens.de" # define REDIRECT_OUTPUT "> /dev/null 2>&1" # define ENV_VAR_MAILER "MAILER" # define ENV_VAR_SHELL "SHELL" # define EDITOR_FORMAT_ON "%E +%N %F" # define TMPDIR "/tmp/" # ifdef HAVE_KEY_PREFIX # define KEY_PREFIX 0xff # endif # define joinpath(result,dir,file) sprintf (result,"%s/%s", dir, (file) ? file : "") #endif #ifdef VMS # define NEWSGROUPS_FILE "newsgroups" # define BUG_REPORT_ADDRESS "mcquill@next.duq.edu" # define REDIRECT_OUTPUT "" # define ENV_VAR_MAILER "TIN_MAILER" /*# define ENV_VAR_SHELL "SHELL"*/ # define ENV_VAR_POSTER "TIN_POST" # define EDITOR_FORMAT_ON "%E %F" # define TMPDIR "SYS$SCRATCH:" extern void joinpath (char *result, char *dir, char *file); extern void joindir (char *result, char *dir, char *file); #endif #if !defined(S_ISDIR) # if defined(M_AMIGA) # define st_mode st_attr # define S_ISDIR(m) (((m) & ST_DIRECT) == ST_DIRECT) # endif # if defined(M_OS2) # define S_ISDIR(m) ((m) & S_IF_DIR) # endif # if defined(M_UNIX) # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) # endif #endif #if !defined(S_ISREG) # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif /* * function prototypes & extern definitions */ #include "patchlev.h" #include "extern.h" #include "nntplib.h" #include "proto.h" d*[SRC.TIN-1_22]TIN.LSM;1+,v.// 4A-d0@123KPWO56t7c189]VG/HJBegin2 Title = Tin - full screen easy to use Usenet newsreader Version = 1.2 Patchlevel 2 Desc1 = Full screen easy to use Usenet newsreader aimed at Desc2 = the beginner/intermediate user. Desc3 = Can read news from local spooldir or via NNTP. Desc4 = Supports news overview/xover index files. Author = Iain Lea AuthorEmail = iain.lea@erlm.siemens.de Maintainer = Iain Lea MaintEmail = iain.lea@erlm.siemens.de Site1 = ftp.germany.eu.net Path1 = /pub/news/tin File1 = tin.tar.Z FileSize1 = 400K Site2 = src.doc.ic.ac.uk: Path2 = computing/usenet/software/readers/tin File2 = ? FileSize2 = 400K Site3 = ftp.uwp.edu Path3 = pub/tin File3 = ? FileSize3 = 400K Site4 = yoyo.cc.monash.edu.au Path4 = pub/tin File4 = ? FileSize4 = 400K Required1 = Local news spooldir / NNTP connection CopyPolicy1 = Copyright (C) Copyright 1991-93 by Iain Lea CopyPolicy2 = Read the source for the full copyright Keywords = tin usenet newsreader nntp Comment1 = Actively supported by author via email and Comment2 = the usenet newsgroup news.software.readers Comment3 = Supported on Unix, AmigaDOS & OS/2 Entered = 07SEP93 EnteredBy = Iain Lea CheckedEmail = iain.lea@erlm.siemens.de End *[SRC.TIN-1_22]TIN.NRF;1+,u.// 4-d0@123KPWO56wt7b189]VG/HJ TIN(1) LOCAL TIN(1) NNAAMMEE tin, rtin, cdtin, tind - A Netnews reader SSYYNNOOPPSSIISS ttiinn//rrttiinn//ccddttiinn//ttiinndd [ _o_p_t_i_o_n_s ] [ _n_e_w_s_g_r_o_u_p_s ] DDEESSCCRRIIPPTTIIOONN _T_i_n is a full-screen easy to use Netnews reader. It can read news locally (i.e. _/_u_s_r_/_s_p_o_o_l_/_n_e_w_s) or remotely (_r_t_i_n or _t_i_n _-_r option) via a NNTP (Network News Transport Protocol) server. _C_d_t_i_n can read news locally and news archived on CD-ROM. It will automatically utilize nov (news overview) style index files if available locally or via the nntp xover command. _T_i_n has five separate levels of operation: Group selection level, Spooldir selection level, Group level, Thread level and Article level. Use the 'h' (help) command to view a list of the commands available at a particular level. On startup _t_i_n will show a list of the newsgroups found in _$_H_O_M_E_/_._n_e_w_s_r_c. An arrow '->' or highlighted bar will point to the first newsgroup. Move to a group by using the terminal arrow keys (terminal dependent) or 'j' and 'k'. Use PgUp/PgDn (terminal dependent) or Ctrl-U and Ctrl-D to page up/down. Enter a newsgroup by pressing RETURN. The TAB key advances to the next newsgroup with unread articles and enters it. OOPPTTIIOONNSS --cc create/update index files for every group in _$_H_O_M_E_/_._n_e_w_s_r_c or file specified by -f option and mark all articles as read. --ff _f_i_l_e use the specified file of subscribed to newsgroups in place of _$_H_O_M_E_/_._n_e_w_s_r_c. --hh help listing all command line options. --HH brief introduction to _t_i_n that is also shown the first time it is started. --II _d_i_r directory in which to store newsgroup index files. Default is _$_H_O_M_E_/_._t_i_n_/_._i_n_d_e_x. --mm _d_i_r mailbox directory to use. Default is _$_H_O_M_E_/_M_a_i_l. --MM _u_s_e_r mail unread articles to specified user for later reading. For more information read section Automatic Mailing and Saving New News. --nn Only load groups from the active file that are also subscribed to in the users _._n_e_w_s_r_c. This allows a noticeable speedup when connecting via a slow line. Version 1.2 PL2 1 TIN(1) LOCAL TIN(1) --pp _p_r_o_g_r_a_m print program with options. --qq quick start without checking for new newsgroups. --PP purge group index files of articles that no longer exist. Care should be taken when using this command as it stats each and every article in each group that is accessed. On a low speed connection this can have an undisirable effect and it also knocks the hell out of your filesystem. --rr read news remotely from the default NNTP server specified in the environment variable NNTPSERVER or contained in the file _/_e_t_c_/_n_n_t_p_s_e_r_v_e_r. --RR read news saved by -S option (not yet implemented). --ss _d_i_r save articles to directory. Default is _$_H_O_M_E_/_N_e_w_s. --SS save unread articles for later reading by -R option. For more information read section Automatic Mailing and Saving New News. --uu create/update index files for every group in _$_H_O_M_E_/_._n_e_w_s_r_c or file specified by -f option. This option is disabled if _t_i_n retreives its index files via a NNTP server. --UU start _t_i_n in the background to update index files while reading news in the foreground. This option is disabled if _t_i_n retreives its index files via a NNTP server. --vv verbose mode for -c -M -S -u and -Z options. --ww quick mode to post an article and then exit. --zz only start _t_i_n if there is any new/unread news. If there is news _t_i_n will position cursor at first group with unread news. Useful for putting in login file. --ZZ check if there is any new/unread news and exit with appropriate status. If -v option is specified the number of unread articles in each group is printed. An exit code 0 indicates no news, 1 that an error occurred and 2 that new/unread news exists. Useful for writing scripts. _T_i_n can also dynamically change its options by the 'M' menu command. Any changes are written to _$_H_O_M_E_/_._t_i_n_/_t_i_n_r_c. The index daemon version, _t_i_n_d, only supports the -f, -h, -I and -v options. Version 1.2 PL2 2 TIN(1) LOCAL TIN(1) IINNDDEEXX FFIILLEESS In order to keep track of threads, _t_i_n maintains an index for each newsgroup. There are a numbe r of methods in which index files can be created and updated. The simplest method is that each user creates/updates there own index files that are stored in _$_H_O_M_E_/_._t_i_n_/_._i_n_d_e_x. This has the advantage that any user can compile and install _t_i_n, but the disadvantage is that each user is going to be creating duplicate files and using precious disk space. A good way to keep index files updated is by doing a _t_i_n _-_U that will update index files in the background while you are reading news in the foreground. You can also update index files via the system batcher cron with the -u option: 3300 66 ** ** ** //uussrr//llooccaall//bbiinn//ttiinn --uu A slightly better method is to set _t_i_n setuid news and have all index files created and updated in the news spool directory (i.e. _/_u_s_r_/_s_p_o_o_l_/_n_e_w_s_/_._i_n_d_e_x). This has the advantage that there will only be one copy of the index files on each machine on your network, but the disadvantage is that you will have _t_i_n running setuid news. A better method is to install the _t_i_n_d index file updating daemon and have it create and update index files for all groups in your active file at regular intervals in the news spool directory (i.e. _/_u_s_r_/_s_p_o_o_l_/_n_e_w_s_/_._i_n_d_e_x). This has the advantage that there will only be one copy of the index files on each machine on your network and _t_i_n must not be setuid news, but the disadvantage is that you will have to have news permissions to install _t_i_n_d and root permissions to install an entry in the cron batcher system to have _t_i_n_d regularly update index files. The best method is to install the _t_i_n_d index file updating daemon on your NNTP server and have it create and update index files for all groups in your active file at regular intervals in the news spool directory (i.e. _/_u_s_r_/_s_p_o_o_l_/_n_e_w_s_/_._i_n_d_e_x). This has the advantage that there will only be one copy of the index files on the NNTP server for the whole of your network, but the disadvantage is that you will have to install my NNTP server patches to allow _t_i_n to retreive index file from your NNTP server and and you must install an entry in the cron batcher system to have _t_i_n_d regularly update index files (note that this is the method we use on our network of 40-50 machines and have not had any problems). Entering a group the first time tends to be slow because the index file must be built from scratch unless the _t_i_n_d update daemon is being used. To alleviate the slowness start _t_i_n to create all index files for the groups you subscribe to with _t_i_n _-_u _-_v and go for a coffee. Subsequent readings of a group will cause incremental updating of the index file. If reading news remotely and locally updating index files Version 1.2 PL2 3 TIN(1) LOCAL TIN(1) operation will be somewhat slower because the articles must be retreived from the NNTP server. NNEEWWSS AADDMMIINNIISSTTRRAATTIIOONN Maintaining Netnews on large networks of machines can be a pretty time consuming job as I discovered when I was given the job of maintaining our news system and news users. _T_i_n is a News User Agent and so most of the users were always asking questions or doing things that could be frowned upon by there departments. To releive news admins (and especially me) of this features have been added to make life easier for news adminstrators. When a user starts _t_i_n it is possible to inform them of any important changes/information concerning the news system by displaying a message of the day (motd) file. The motd file should be created in your news lib directory (i.e. _/_u_s_r_/_l_i_b_/_n_e_w_s_/_m_o_t_d) and should have file permissions set to 0644. The motd file will only be displayed if its contents is newer than the last time the user started _t_i_n. If reading news via NNTP my XMOTD patch will have to have been applied to your NNTP server. A user starting _t_i_n for the first time can be automatically subscribed to a list of newsgroups that are deemed appropriate by the news administrator. At our site the subscriptions file has 125 groups (our active file contains over 400 groups with many only being marginally interesting to most people). The subscriptions file should be created in your news lib directory (i.e. _/_u_s_r_/_l_i_b_/_n_e_w_s_/_s_u_b_s_c_r_i_p_t_i_o_n_s) and should have file permissions set to 0644. If reading news via NNTP my LIST SUBSCRIPTIONS patch will have to have been applied to your NNTP server. If my NNTP XUSER patch has been applied to your NNTP server you will be able to log the username and machine to your NNTP logfile for usage statistics. SSCCRREEEENN FFOORRMMAATT _T_i_n has five separate levels of operation: Group selection level, Spooldir selection level, Group level, Thread level and Article level. At the Group Selection level the title displays the number of subscribed groups. The newsgroups are displayed on the left of the screen with the number of unread articles displayed on the same line in the middle of the screen. <> <> <> i.e., 11 aalltt..ssoouurrcceess 1100 22 ccoommpp..ssoouurrcceess..mmiisscc 33 33 nneewwss..ssooffttwwaarree..rreeaaddeerrss 1122 At the Group level the title contains the name of the group, the Version 1.2 PL2 4 TIN(1) LOCAL TIN(1) number of conversation threads and total number of articles i.e., alt.sources (7 23). If the group has been setup not to thread articles (i.e., alt.sources is in _$_(_H_O_M_E_)_/_._t_i_n_/_u_n_t_h_r_e_a_d) the tit*X7~ TIN-1_22.BCKud[SRC.TIN-1_22]TIN.NRF;16|le will be alt.sources (U 23). There are two possible display formats as shown below: <> <> <> <> <> i.e., 11 ++ 33 BBnneewwss ssoouurrcceess?? iiaaiinn@@aannll443333..uuuuccpp 22 11 TThhiiss qquueessttiioonn hhaass eetthheerr@@nneett or <> <> <> <> i.e., 11 ++ 33 BBnneewwss ssoouurrcceess?? 22 11 TThhiiss qquueessttiioonn hhaass aa lloonnggeerr ssuubbjjeecctt lliinnee At the Article level the page header has the following format: <> <> <> <> <> <> <> <> <> i.e., 2244 JJuull 1155::2200::0033 GGMMTT aalltt..ssoouurrcceess TThhrreeaadd 11 ooff 22 AArrttiiccllee 445522 BBnneewwss ssoouurrcceess?? 33 rreessppoonnsseess iiaaiinn@@aannll443333..uuuuccpp OOrrggaanniizzaattiioonn nnaammee <> CCOOMMMMOONN MMOOVVIINNGG KKEEYYSS This table shows the common keys/commands for moving at all five levels within _t_i_n. ansi/at386/vt100 Other Terminals Beginning of list/article HHoommee 11 (^^RR or gg at article level) End of list/article EEnndd $$ (also GG at article level) Page Up PPggUUpp ^^UU or ^^BB or bb Page Down PPggDDnn ^^DD or ^^FF or <> Line Up UUpp aarrrrooww kk (not at article level) Line Down DDoowwnn aarrrrooww jj (not at article level) CCOOMMMMOONN EEDDIITTIINNGG CCOOMMMMAANNDDSS An emacs style editing package allows the easy editing of input strings. An history list allows the easy reuse of previously entered strings. The following commands are available when editing a string: ^^AA,,^^EE move to beginning or end of line, respectively. ^^FF,,^^BB nondestructive move forward or back one location, respectively. ^^DD delete the character currently under the cursor, or send EOF if no characters in the buffer. Version 1.2 PL2 5 TIN(1) LOCAL TIN(1) ^^HH,,<> delete character left of the cursor. ^^KK delete from cursor to end of line. ^^PP,,^^NN move through history, previous and next, respectively. ^^LL,,^^RR redraw the current line. <> places line on history list if nonblank, appends newline and returns to the caller. <> aborts the present editing operation. NNEEWWSSGGRROOUUPP SSEELLEECCTTIIOONN CCOOMMMMAANNDDSS 44 Select group 4. ^^KK Delete current group from _$_H_O_M_E_/_._n_e_w_s_r_c file. ^^LL Redraw page. ^^RR Reset _$_H_O_M_E_/_._n_e_w_s_r_c file. <> Read current group. <> View next group with unread news. Will wrap around to the beginning of the group selection list looking for unread groups. BB Mail a bug report or comment to the author. This is the best way of getting bugs fixed and features added/changed. cc Mark current group as all read with confirmation and goto next group in group selection list. CC Mark current group as all read and goto next unread group in group selection list. dd Toggle display to show just the groupname or the groupname and the groups description. gg Choose a new group by name. The position of the group within the group list will also be asked for. By entering '1' the new group will be the first group in the displayed list, by entering '8' the group will be the eighth group in the list etc. By entering '$' the group will be the last group displayed. hh Help screen of newsgroup selection commands. HH Toggle the display of help mini menu at the bottom of the screen. II Toggle inverse video. Version 1.2 PL2 6 TIN(1) LOCAL TIN(1) ll List and allow selection of the available spool directories. This feature requires a special library to be linked with _t_i_n to create _c_d_t_i_n which can then read news from an active news feed and also from multiple CD-ROMs. mm Move the current group within the group selection list. By entering '1' the group will become the first displayed group in the list, by entering '8' the eighth group in the list etc. By entering '$' the group will be the last group displayed. MM User configurable options menu (for more information see section Options Menu). qq Quit _t_i_n. QQ Quit _t_i_n. rr Toggle display of all subscribed to groups and just the subscribed to groups containing unread articles. Command has no effect if groups were read from the command line when _t_i_n was started. ss Subscribe to current group. SS Subscribe to groups matching user specified pattern. uu Unsubscribe to current group. UU Unsubscribe to groups matching user specified pattern. vv Print _t_i_n version information. ww Post an article to current group. WW List articles posted by user. The date posted, the newsgroup and the subject are listed. yy The first time this command is called it will yank in all groups from _$_L_I_B_D_I_R_/_a_c_t_i_v_e that are not in _$_H_O_M_E_/_._n_e_w_s_r_c. After any groups have been subscribed/unsubscribed to, this command if pressed again will reread _$_H_O_M_E_/_._n_e_w_s_r_c and display only the subscribed groups. YY Reread the active file to see if any new news has arrived since starting _t_i_n_. zz Mark all articles in the current group as unread. ZZ Undelete previously deleted group by ^K command from _$_H_O_M_E_/_._n_e_w_s_r_c. Version 1.2 PL2 7 TIN(1) LOCAL TIN(1) // Group forward search. ?? Group backward search. SSPPOOOOLL DDIIRREECCTTOORRYY SSEELLEECCTTIIOONN CCOOMMMMAANNDDSS 44 Select spool directory 4. ^^LL Redraw page. <> Read news from selected spool directory. BB Mail a bug report or comment to the author. This is the best way of getting bugs fixed and features added/changed. hh Help screen of spool directory selection commands. HH Toggle the display of help mini menu at the bottom of the screen. II Toggle inverse video. qq Return to previous level. QQ Quit _t_i_n_. vv Print _t_i_n version information. GGRROOUUPP IINNDDEEXX CCOOMMMMAANNDDSS 44 Select article 4. ^^KK Kill current article (for more information read section Kill Article Menu). ^^LL Redraw page. <> Read current article. <> View next unread article or group. aa Author forward search. AA Author backward search. cc Mark all articles as read with confirmation. CC Mark all articles as read and goto next group with unread news. dd Toggle display to show just the subject or the subject and author. gg Choose a new group by name. Version 1.2 PL2 8 TIN(1) LOCAL TIN(1) hh Help screen of group index commands. HH Toggle the display of help mini menu at the bottom of the screen. II Toggle inverse video. KK Mark article/thread as read and advance to next unread article/thread. ll List the author of each response in current thread and enter thread selection level. mm Mail current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to someone. MM User configurable options menu (for more information see section Options Menu). nn Go to next group. NN Go to next unread article. oo Output current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to printer. pp Go to previous group. PP Go to previous unread article. qq Return to previous level. QQ Quit _t_i_n_. ss Save current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to file / files / mailbox. To save to a mailbox enter '=' or '=mailbox' when asked for filename to save to. To save in / format enter '+filename'. Environment variables are allowed within a filename (i.e. _$_S_O_U_R_C_E_S_/_d_i_r_/_f_i_l_e_n_a_m_e). tt Tag current article / thread for mailing ('m') / piping ('|') / printing ('o') / saving ('s') / crossposting ('x'). uu Toggle display to show all articles as unthreaded or threaded. UU Untag all articles that were tagged. vv Print _t_i_n version information. Version 1.2 PL2 9 TIN(1) LOCAL TIN(1) ww Post an article to current group. WW List articles posted by user. The date posted, the newsgroup and the subject are listed. xx Crosspost already posted current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to another newsgroup(s). Useful for reposting from global to local newsgroups. XX Mark all unread articles that have not been selected as read, redo screen to reflect changes and put index at the first thread to begin reading. Pressing 'X' again will toggle back to the way it was before. See '~' command for clearing the toggle effect. zz Mark current article as unread. ZZ Mark current thread as unread. // Search forward for specified subject. ?? Search backward for specified subject. -- Show last message. || Pipe current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles into command. ** Select current thread for later processing. .. Toggle selection of current thread. If at least one unread art, but not all unread arts, in thread is selected, then all unread arts become selected. @@ Reverse all selections on all articles. ~~ Undo all selections on all articles. It clears the toggle effect of 'X' command. Thus after first doing a 'X', one can then do '~' to reset articles. Thus, one can iteratively whittle down uninteresting threads. ++ Perform auto-selection on current group. ;; For each thread in current group, if it at least one unread art is selected, all unread arts become selected. This is useful for auto-selection on author where reader wants to see entire thread. == Prompts for a pattern with which to match on. All threads whose subjects match the pattern will be selected. A pattern of "*" will match all subjects. Entering just will cause the previous pattern to Version 1.2 PL2 10 TIN(1) LOCAL TIN(1) be used. TTHHRREEAADD LLIISSTTIINNGG CCOOMMMMAANNDDSS 44 Select article 4 within thread. ^^LL Redraw page. <> Read current article within thread. <> View next unread article within thread. BB Mail a bug report or comment to the author. This is the best way of getting bugs fixed and features added/changed. cc Mark thread as read after confirmation and return to previous level. dd Toggle display to show just the subject or the subject and author. hh Help screen of thread listing commands. HH Toggle the display of help mini menu at the bottom of the screen. II Toggle inverse video. KK Mark thread as read and return to previous level. qq Return to previous level. QQ Quit _t_i_n_. rr Toggle display to show all articles or only unread articles. BB Mail a bug report or comment to the author. This is the best way of getting bugs fixed and features added/changed. tt Tag current article for mailing ('m') / piping ('|') / printing ('o') / saving ('s') / crossposting ('x'). TT Return to group index level. vv Print _t_i_n version information. zz Mark current article in thread as unread. ZZ Mark all articles in thread as unread. AARRTTIICCLLEE VVIIEEWWEERR CCOOMMMMAANNDDSS 00 Read the base article in this thread. Version 1.2 PL2 11 TIN(1) LOCAL TIN(1) 44 Read response 4 in this thread. ^^HH Show all of the articles mail header. ^^KK Kill current article (for more information read section Kill Article Menu). ^^LL Redraw page. <> Goto next base article. <> Goto next unread article. aa Author forward search. AA Author backward search. cc Mark all articles as read with confirmation and return to group selection level. CC Mark current group as all read and goto next unread group in group selection list. dd Toggle rot-13 decoding for this article. DD Delete current article. It must have been posted by the same user. The cancel message can be seen in the newsgroup 'control'. ff Post a followup to the current article with a copy of the article included. FF Post a followup to the current article. hh Help screen of article page commands. HH Toggle the display of help mini menu at the bottom of the screen. II Toggle inverse video. kk Mark article as read and advance to next unread article. KK Mark thread as read and advance to next unread thread. mm Mail current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to someone. MM User configurable options menu (for more information see section Options Menu). nn Go to the next article. Version 1.2 PL2 12 TIN(1) LOCAL TIN(1) NN Go to the next unread article. oo Output current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to printer. oo Output article/thread/tagged articles to printer. pp Go to the previous article. PP Go to the previous unread article. qq Return to previous level. QQ Quit _t_i_n_. rr Reply through mail to the author of the current article with a copy of the article included. RR Reply through mail to the author of the current article. ss Save current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to file / files / mailbox. To save to a mailbox enter '=' or '=mailbox' when asked for filename to save to. To save in / format enter '+filename'. Environment variables are allowed within a filename (i.e. _$_S_O_U_R_C_E_S_/_d_i_r_/_f_i_l_e_n_a_m_e). tt Return to group selection level. TT Tag current article for mailing ('m') / piping ('|') / printing ('o') / saving ('s') / crossposting ('x'). vv Print _t_i_n version information. ww Post an article to current group. WW List articles posted by user. The date posted, the newsgroup and the subject are listed. xx Crosspost already posted current article / thread / auto selected (hot) articles / articles matching pattern / tagged articles to another newsgroup(s). Useful for reposting from global to local newsgroups. zz Mark article as unread. // Article forward search. ?? Article backward search || Pipe current article / thread / auto selected (hot) Version 1.2 PL2 13 TIN(1) LOCAL TIN(1) articles / articles matching pattern / tagged articles into command. << Goto the first article in the current thread. >> Goto the last article in the current thread. ** Select current thread for later processing. .. Toggle selection of current article. @@ Reverse article selections. ~~ Undo all selections on current thread. GGLLOOBBAALL OOPPTTIIOONNSS MMEENNUU This menu is accessed by pressing 'M' at all levels. It allows the user to customize the behaviour of _t_i_n. The options are saved to the file _$_H_O_M_E_/_._t_i_n_/_t_i_n_r_c. Use to toggle the required option and to set. AAuuttoo ssaavvee Automatically save articles/threads by ``Archive-name:'' line in article header and post process them if process type is not set to None. EEddiittoorr ooffffsseett Set ON if the editor used for posting, follow-ups and bug reports has the capability of starting and positioning the cursor at a specified line within a file. MMaarrkk ssaavveedd rreeaadd Allows saved articles/threads to be automatically marked as read. CCoonnffiirrmm CCoommmmaanndd Allows certain commands (i.e. 'c' catchup) that require user confirmation to be executed immediately if set OFF. DDrraaww aarrrrooww Allows groups/articles to be selected by an arrow '->' if set ON or by an highlighted bar if set OFF. PPrriinntt hheeaaddeerr This allows the complete mail header or only the ``Subject:'' and ``From:'' fields to be output when printing articles. GGoottoo 11sstt uunnrreeaadd This allows the cursor to be placed at the first / last unread article upon entering a newsgroup with unread news. SSccrroollll ffuullll ppaaggee If set ON scrolling of groups/articles will be a full page at a time, otherwise half a page at a time. Version 1.2 PL2 14 TIN(1) LOCAL TIN(1) CCaattcchhuupp oonn qquuiitt If set ON the user is asked when quitting if all groups read during the current session should be marked read. TThhrreeaadd aarrttiicclleess If set ON articles will be threaded in all groups (default), otherwise articles will be shown unthreaded. Threading/unthreading is possible on a per group basis by setting the group attribute variable 'thread_arts' to ON/OFF in the file _$_H_O_M_E_/_._t_i_n_/_a_t_t_r_i_b_u_t_e_s. SShhooww oonnllyy uunnrreeaadd If set ON show only new/unread articles, otherwise show all articles. SShhooww ddeessccrriippttiioonn If set ON show a short descriptive text for each displayed newsgroup. The text used is taken from the _$_L_I_B_D_I_R_/_n_e_w_s_g_r_o_u_p_s file. SShhooww AAuutthhoorr If set 'None' only the ``Subject:'' line will be displayed. If set 'Addr' ``Subject:'' line & the address part of the ``From:'' line are displayed. If set 'Name' ``Subject:'' line & the authors full name part of the ``From:'' line are displayed. If set 'Both' ``Subject:'' line & all of the ``From:'' line are displayed. PPrroocceessss ttyyppee This specifies the default type of post processing to perform on saved articles. The following types of processing are allowed: --none. --unpacking of multi-part shell archives. --unpacking of multi-part uuencoded files. --unpacking of multi-part uuencoded files, which produce a *.zoo archive whose contents is listed. --unpacking of multi-part uuencoded files, which produce a *.zoo archive whose contents is extracted. --unpacking of multi-part uuencoded files, which produce a *.zip archive whose contents is listed. --unpacking of multi-part uuencoded files, which produce a *.zip archive whose contents is extracted. --unpacking of multi-part uuencoded files, which produce a *.lha archive whose contents is listed (AmigaDOS version only). --unpacking of multi-part uuencoded files, which produce a *.lha archive whose contents is extracted (AmigaDOS version only). SSoorrtt aarrttiicclleess bbyy This specifies how articles should be sorted. The following sort types are allowed: --don't sort articles (default). Version 1.2 PL2 15 TIN(1) LOCAL TIN(1) --sort articles by ``Subject:'' field (ascending & descending). --sort articles by ``From:'' field (ascending & descending). --sort articles by ``Date:'' field (ascending & descending). SSaavvee ddiirreeccttoorryy The directory where articles/threads are to be saved. Default is _$_H_O_M_E_/_N_e_w_s. MMaaiill ddiirreeccttoorryy The directory where articles/threads are to be saved in mailbox format. This feature is mainly for use with the Elm mail program. It allows the user to save articles/threads/groups simply by giving '=' as the filename to save to. PPrriinntteerr The printer program with options that is to be used to print articles. Default is lpr for BSD machines and lp for SysV machines. TTIINNRRCC CCOONNFFIIGGUURRAABBLLEE VVAARRIIAABBLLEESS The following variables are user configurable by editing _$_H_O_M_E_/_._t_i_n_/_t_i_n_r_c directly. It is hoped to eventually provide a menu to allow the setting of the most common variables. bbaattcchh__ssaavvee If set ON articles/threads will be saved in batch mode when save -S or mail -M is specified on the command line. Default is OFF. bbeeggiinnnneerr__lleevveell If set ON a mini menu of the most useful commands will be displayed at the bottom of the screen for each level. Default is ON. ddiissppllaayy__rreeaaddiinngg__pprroommpptt The prompt ``Reading...'' will be displayed when reading an article from a NNTP server to provide feedback to the user. Default is ON. ffoorrccee__ssccrreeeenn__rreeddrraaww Specifies whether a screen redraw should always be done after certain external commands. Default is OFF. ggrroouuppnnaammee__mmaaxx__lleennggtthh Maximum length of the names of newsgroups to be displayed so that more of the newgroup description can be displayed. Default is 132. ddeeffaauulltt__ssiiggffiillee The path that specifies the signature file to use when Version 1.2 PL2 16 TIN(1) LOCAL TIN(1) posting, following upto or replying to an article. If the path is a directory then the signature will be randomly generated from files that are in the specified directory. Default is _$_H_O_M_E_/_._S_i_g. eeddiittoorr__ffoorrmmaatt The format string used to create the editor start command with parameters. Default is '%E +%N %F' (i.e., /bin/vi +7 .article). hhoott__aarrtt__mmaarrkk The character used to show that an article/thread is auto- selected (hot). Default is '*'. qquuoottee__cchhaarrss The character used in quoting included text to article followups and mail replys. The '_' character represents a blank character and is replaced with ' ' when read. Default is ':_'. rreerreeaadd__aaccttiivvee__ffiillee__sseeccss The news active file is reread at regular intervals to show if any new news has arrived. Default is 300 seconds. rreettuurrnn__aarrtt__mmaarrkk The character used to show that an article will return. Default is '-'. ssaavvee__ttoo__mmmmddff__mmaaiillbbooxx Allows articles to be saved to a MMDF style mailbox instead of mbox format. Default is OFF unless reading news on SCO Unix which uses MMDF by default. sshhooww__llaasstt__lliinnee__pprreevv__ppaaggee The last line of the previous page will be displayed as the first line +\,~ TIN-1_22.BCKud[SRC.TIN-1_22]TIN.NRF;1M|Yof next page. Default is OFF. ssllooww__ssppeeeedd__tteerrmmiinnaall Strips the blanks from the end of each line therefore speeding up the display when reading on a slow terminal or via modem. Default is OFF. ttaabb__aafftteerr__XX__sseelleeccttiioonn If enabled will automatically goto the first unread article after having selected all hot articles and threads with the 'X' command at group index level. Default is OFF. ttaabb__ggoottoo__nneexxtt__uunnrreeaadd If enabled pressing TAB at the article viewer level will goto the next unread article immediately instead of first paging through the current one. Default is ON. uunnrreeaadd__aarrtt__mmaarrkk The character used to show that an article has not been read. Version 1.2 PL2 17 TIN(1) LOCAL TIN(1) Default is '+'. uussee__bbuuiillttiinn__iinneewwss Allows the builtin NNTP inews to be enabled/disabled. Default is ON (enabled). uussee__kkeeyyppaadd Allows the scroll keys on the keypad to be enabled/disabled on supported terminals. Default is OFF. GGRROOUUPP AATTTTRRIIBBUUTTEESS _T_i_n allows certain attributes to be set on a per group basis. These group attributes are read from the file _$_H_O_M_E_/_._t_i_n_/_a_t_t_r_i_b_u_t_e_s. A later version will provide a menu interface to set all the attributes. At present you will have to edit the file with your editor :-(. The following group attributes are available: nneewwssggrroouupp==aalltt..ssoouurrcceess mmaaiillddiirr==//uussrr//iiaaiinn//MMaaiill//ssoouurrcceess ssaavveeddiirr==//uussrr//iiaaiinn//NNeewwss//aalltt..ssoouurrcceess ssiiggffiillee==//uussrr//iiaaiinn//..ffuunnnnyy__ssiigg oorrggaanniizzaattiioonn==WWaacckkyy BBiittss IInncc.. ffoolllloowwuupp__ttoo==aalltt..ssoouurrcceess..dd pprriinntteerr==//uussrr//llooccaall//bbiinn//aa22ppss --nnnn || //bbiinn//llpprr aauuttoo__ssaavvee==OONN bbaattcchh__ssaavvee==OOFFFF ddeelleettee__ttmmpp__ffiilleess==OONN sshhooww__oonnllyy__uunnrreeaadd==OOFFFF tthhrreeaadd__aarrttss==OONN sshhooww__aauutthhoorr==11 ssoorrtt__aarrtt__ttyyppee==55 ppoosstt__pprroocc__ttyyppee==11 Note that the ''newsgroup='' line has to be specified before the attributes are specified for that group. All attributes are set to a reasonable default so you only have to specify the attribute that you want to change (i.e., savedir). All toggle attributes are set by specifying ON/OFF. The show_author attribute is specified by a number from the following range: 0=none, 1=username, 2=network address, 3=both. The sort_art_type attribute is specified by a number from the following range: 0=none, 1=subject descending, 2=subject ascending, 3=from descending, 4=from ascending, 5=date descending, 6=date ascending. The post_proc_type attribute is specified by a number from the following range: 0=none, 1=unshar, 2=uudecode, 3=uudecode & list zoo archive, 4=uudecode & extract zoo archive, 5=uudecode & list zip archive, 6=uudecode & extract zip archive. (note: if running Version 1.2 PL2 18 TIN(1) LOCAL TIN(1) on AmigaDOS the zoo options are replaced by there corresponding lha archiver options). AAUUTTOOMMAATTIICC KKIILLLL AANNDD SSEELLEECCTTIIOONN When there is a subject or an author which you are either very interested in, or find completely uninteresting, you can easily instruct _t_i_n to _a_u_t_o_-_s_e_l_e_c_t or _a_u_t_o_-_k_i_l_l articles with specific subjects or from specific authors. These instructions are stored in a _k_i_l_l _f_i_l_e. This menu is accessed by pressing '^K' at the group and page levels. It allows the user to kill or select an article that matches the current ``Subject:'' line, ``From:'' line or a string entered by the user. The user entered string can be applied to the ``Subject:'' or ``From:'' lines of an article. The kill description can be limited to the current newsgroup or it can apply to all newsgroups. Once entered the user can abort the command and not save the kill description, edit the kill file or save the kill description. On starting _t_i_n the users killfile _$_H_O_M_E_/_._t_i_n_/_k_i_l_l is read and on entering a newsgroup any kill or select descriptions are applied. Articles that match a kill description are marked killed and are not displayed. Articles that match an auto-select description are marked with a ''*'' when displayed. PPOOSSTTIINNGG AARRTTIICCLLEESS _T_i_n allows posting of articles, follow-up to already posted articles and replying direct through mail to the author of an article. Use the 'w' command to post an article to a newsgroup. After entering the post subject the default editor (i.e. vi) or the editor specified by the $VISUAL environment variable will be started and the article can be entered. To crosspost articles simply add a comma and the name of the newsgroup(s) to the end of the ``Newsgroups:'' line at the beginning of the article. After saving and exiting the editor you are asked if you wish to a)bort posting the article, e)dit the article again or p)ost the article to the specified newsgroup(s). Use the 'W' command to display a history of the articles you have posted. The date the article was posted, which newsgroups the article was posted to and the articles subject line are displayed. Use the 'f' / 'F' command to post a follow-up article to an already posted article. The 'f' command will copy the text of the original article into the editor. The editing procedure is the same as when posting an article with the 'w' command. Use the 'r' / 'R' command to reply direct through mail to the author of an already posted article. The 'r' command will copy Version 1.2 PL2 19 TIN(1) LOCAL TIN(1) the text of the original article into the editor. The editing procedure is the same as when posting an article with the 'w' command. After saving and exiting the editor you are asked if you wish to a)bort sending the article, e)dit the article again or s)end the article to the author. CCUUSSTTOOMMIIZZIINNGG TTHHEE AARRTTIICCLLEE QQUUOOTTEE SSTTRRIINNGG When posting a followup to an article or replying direct to the author of an article via email the text of the article can be quoted. The beginning of the quoted text can contain information about the quoted article (i.e., Name and the Message Id of the article). To allow for different situations certain information from the article can be used in the quoted string. The following variables are expanded if found in the tinrc variables 'mail_quote_format=' or 'news_quote_format=': %%AA Address (Email) %%DD Date %%FF Full address (%N (%A)) %%GG Groupname %%MM Message Id %%NN Name of user i.e., mmaaiill__qquuoottee__ffoorrmmaatt==OOnn %%DD iinn %%GG yyoouu wwrroottee:: nneewwss__qquuoottee__ffoorrmmaatt==IInn %%MM,, %%FF wwrroottee:: would expand when used to: OOnn 2211 JJuull 11999922 0099::4455::5511 --00440000 iinn aalltt..ssoouurrcceess yyoouu wwrroottee:: IInn <>,, IIaaiinn LLeeaa ((iiaaiinn@@eerrllmm..ssiieemmeennss..ddee)) wwrroottee:: MMAAIILLIINNGG PPIIPPIINNGG PPRRIINNTTIINNGG RREEPPOOSSTTIINNGG AANNDD SSAAVVIINNGG AARRTTIICCLLEESS The command interface to mail ('m'), pipe ('|'), print ('o'), crosspost ('x') and save ('s') articles is the same for ease of use. The initial command will ask you to select which a)rticle, t)hread, h)ot (auto selected) r)egex pattern, t)agged articles you wish to mail, pipe etc. Tagged articles must have already been tagged with the 'T' command. All tagged articles can be untagged by the 'U' untag command. If regex pattern matching is selected you are asked to enter a regular expression (i.e. to match all articles subject lines containing 'net News' you must enter '*net News*'). Any articles that match the entered expression will be mailed, piped etc. To save articles to a mailbox with the name of the current newsgroup (i.e. Alt.sources) enter '=' or '=' when asked for the save filename. To save articles in / format enter '+'. When saving articles you can specify whether the saved files Version 1.2 PL2 20 TIN(1) LOCAL TIN(1) should be post processed (i.e. unshar shell archive, uudecode multiple parts etc). A default process type can be set by the 'Process type:' in the 'M' options menu. AAUUTTOOMMAATTIICC MMAAIILLIINNGG AANNDD SSAAVVIINNGG NNEEWW NNEEWWSS _T_i_n allows new/unread news articles to be mailed (-M option)/saved (-S option) in batch mode for later reading. Useful when going on holiday and you don't want to return and find that expire has removed a whole load of unread articles. Best to run from crontab everyday while away, after which you will be mailed a report of which articles were mailed/saved from which newsgroups and the total number of articles mailed/saved. Articles are saved in a private news structure under your directory (default is _$_H_O_M_E_/_N_e_w_s). Be careful of using this option if you read a lot of groups because you could overflow your filesystem. If you only want to save a few groups it would be best to backup your full _$_H_O_M_E_/_._n_e_w_s_r_c and create a new one that only contains the newsgroups you want to mail/save. Saved news can be read later by _t_i_n _-_R. _t_i_n _-_M _i_a_i_n _-_c _-_f _n_e_w_s_r_c_._m_a_i_l (mail any unread articles in newgroups specified in file newsrc.mail) _t_i_n _-_S _-_c _-_f _n_e_w_s_r_c_._s_a_v_e (save any unread articles in newgroups specified in file newsrc.save) _t_i_n _-_R (read any articles saved by _t_i_n _-_S) SSIIGGNNAATTUURREESS _T_i_n will recognize a signature in either _$_H_O_M_E_/_._s_i_g_n_a_t_u_r_e or _$_H_O_M_E_/_._S_i_g. If _$_H_O_M_E_/_._s_i_g_n_a_t_u_r_e exists, then the signature will be pulled into the editor for mail commands. A signature in _$_H_O_M_E_/_._s_i_g_n_a_t_u_r_e will not be pulled into the editor for posting commands since _i_n_e_w_s will append the signature itself. A signature in _$_H_O_M_E_/_._S_i_g will be pulled into the editor for both posting and mailing commands. The following is an example of a _$_H_O_M_E_/_._S_i_g file: NNAAMMEESS IIaaiinn LLeeaa iiaaiinn..lleeaa@@eerrllmm..ssiieemmeennss..ddee SSNNAAIILL BBrruueecckkeenn SSttrraassssee 1122,, 88550000 NNuueerrnnbbeerrgg 9900,, GGeerrmmaannyy PPHHOONNEE ++4499--991111--333311996633 ((hhoommee)) ++4499--991111--33008899--440077 ((wwoorrkk)) _T_i_n also has the capability to generate random signatures on a per newsgroup basis if so desired. The way to accomplish this is to specify the default signature or the group attribute sigfile as a directory. If for example the sigfile path is _/_u_s_r_/_i_a_i_n_/_._s_i_g_s and _._s_i_g_s is a directory then _t_i_n will select a random signature from any file that is in the directory _._s_i_g_s (note: one signature per numbered file). A random signature can Version 1.2 PL2 21 TIN(1) LOCAL TIN(1) also consist of a fixed part signature that can contain your name, address etc. followed by the random sig. The fixed part of the random sig is read from the file _$_H_O_M_E_/_._s_i_g_f_i_x_e_d. EENNVVIIRROOMMEENNTT VVAARRIIAABBLLEESS TTIINNRRCC Define this variable if you want to specify command line options that _t_i_n should be started with to save typing them each time it is started. The contents of the environment variable are added to the front of the command line options before it is parsed therefore allowing an option specified on the command line to override the same option specified in the environment. TTIINN__HHOOMMEEDDIIRR Define this variable if you do not want the .tin directory in _$_H_O_M_E_/_._t_i_n. (i.e. if you want all tin's private files in _/_t_m_p_/_._t_i_n you would set TINDIR to _/_t_m_p. TTIINN__IINNDDEEXXDDIIRR Define this variable if you do not want the .index directory in _$_H_O_M_E_/_._t_i_n_/_._i_n_d_e_x. (i.e. if you want all tin's index files in _/_t_m_p_/_._i_n_d_e_x you would set TIN_INDEXDIR to _/_t_m_p. TTIINN__LLIIBBDDIIRR Define this variable if you want to override the LIBDIR path that was compiled into the tin binary via the Makefile. TTIINN__SSPPOOOOLLDDIIRR Define this variable if you want to override the SPOOLDIR path that was compiled into the tin binary via the Makefile. TTIINN__NNOOVVRROOOOTTDDIIRR Define this variable if you want to override the NOVROOTDIR path that was compiled into the tin binary via the Makefile. TTIINN__AACCTTIIVVEEFFIILLEE Define this variable if you want to override the LIBDIR/active path that was compiled into the tin binary via the Makefile. NNNNTTPPSSEERRVVEERR The default NNTP server to remotely read news from. This variable only needs to be set if the -r command line option is specified and the file _/_e_t_c_/_n_n_t_p_s_e_r_v_e_r does not exist. DDIISSTTRRIIBBUUTTIIOONN Set the article header field ``Distribution:'' to the contents of the variable instead of the system default. Version 1.2 PL2 22 TIN(1) LOCAL TIN(1) OORRGGAANNIIZZAATTIIOONN Set the article header field ``Organization:'' to the contents of the variable instead of the system default. This variable has precedence over the file _$_H_O_M_E_/_._t_i_n_/_o_r_g_a_n_i_z_a_t_i_o_n that may also contain an organization string. If reading news on an Apollo DomainOS machine the environment variable NEWSORG has to be used instead of ORGANIZATION. RREEPPLLYYTTOO Set the article header field ``Reply-To:'' to the return address specified by the variable. This is useful if the machine is not registered in the UUCP mail maps or if you wish to receive replies at a different machine. This variable has precedence over the file _$_H_O_M_E_/_._t_i_n_/_r_e_p_l_y_t_o that may also contain a return address. AADDDD__AADDDDRREESSSS This can contain an address to append to the return address when replying directly through mail to somebody whose mail address is not directly recognized by the local host. For example say the return address is _u_s_e_r_@_b_i_g_v_a_x, but _b_i_g_v_a_x is not recognized by your host, so therefore the mail will not reach _u_s_e_r. But the host _l_i_t_t_e_v_a_x is known to recognize your host and _b_i_g_v_a_x, so if ADD_ADDRESS is set (i.e. 'setenv ADD_ADDRESS @littevax' for csh or 'set ADD_ADDRESS @littevax' and 'export ADD_ADDRESS' for sh) the address _u_s_e_r_@_b_i_g_v_a_x_@_l_i_t_t_l_e_v_a_x will be used and the mail will reach _u_s_e_r_@_b_i_g_v_a_x. This variable has precedence over the file _$_H_O_M_E_/_._t_i_n_/_a_d_d___a_d_d_r_e_s_s that may also contain an address. BBUUGG__AADDDDRREESSSS If the 'B' command bug report mail address is not correct this variable should be set to the correct mail address. This variable has precedence over the file _$_H_O_M_E_/_._t_i_n_/_b_u_g___a_d_d_r_e_s_s that may also contain a mail address. MMAAIILLEERR This variable has precedence over the default mailer that is used in all mailing operations within _t_i_n (i.e. replying 'rR', and bug reports 'B'). VVIISSUUAALL This variable has precedence over the default editor (i.e. vi) that is used in all editing operations within _t_i_n (i.e. posting 'w', replying 'rR', follow-ups 'fF' and bug reports 'B'). AAUUTTOOSSUUBBSSCCRRIIBBEE _t_i_n interprets this variable similarly to rn. It contains a list of patterns, separated by commas and possibly prefixed with exclamation points. A new group is checked against the list of patterns; if it matches, _t_i_n subscribes the user to the group without further query. An Version 1.2 PL2 23 TIN(1) LOCAL TIN(1) exclamation point negates the meaning of a match on this pattern, and can be used to cancel certain matches. For example, setting _A_U_T_O_S_U_B_S_C_R_I_B_E_=_c_o_m_p_._o_s_._u_n_i_x_._*_,_t_a_l_k_._*_,_!_t_a_l_k_._p_o_l_i_t_i_c_s_._* will automatically subscribe the user to all new groups in the comp.os.unix hierarchy, and all talk groups other than talk.politics groups (which will be queried for as usual.) AAUUTTOOUUNNSSUUBBSSCCRRIIBBEE _t_i_n interprets this variable similarly to rn. It is handled like the _A_U_T_O_S_U_B_S_C_R_I_B_E variable, but groups matching the list are unsubscribed from without further query. For example, setting _A_U_T_O_U_N_S_U_B_S_C_R_I_B_E_=_a_l_t_._f_l_a_m_e_._*_,_u_*_,_!_u_k_._* will automatically unsubscribe the user from all new alt.flame groups and all groups starting with u (university groups) other than UK groups (which will be queried for as usual.) TTIIPPSS AANNDD TTRRIICCKKSS Tin can be pretty much be navigated by using the four cursor keys. The left arrow key goes up a level, the right arrow key goes down a level, the up arrow key goes up a line (page at article viewer level) and the down arrow key goes down a line (page at article viewer level). The following newsgroups provide useful information concerning news software: --news.software.readers (info. about news user agents tin,rn,nn,vn etc.) --news.software.nntp (info. about NNTP) --news.software.b (info. about news transport agents Bnews,Cnews and INN) --news.answers (Frequently Asked Questions (FAQ) about many different themes) Many prompts (i.e. 'Mark everything as read? (y/n): y') within _t_i_n offer a default choice that the cursor is positioned on. By pressing the default value is taken. Many prompts (i.e. 'Post subject []>') within _t_i_n can be aborted by pressing ESC. When _t_i_n is run in an xterm window it will resize itself each time the xterm is resized. _T_i_n will reread the active file at set intervals to show any newly arrived news. XXTTEERRMM BBUUTTTTOONNSS If the environment variable _T_E_R_M is set to _x_t_e_r_m, then button pressing can be used to select groups and articles. In the group selection menu, if the mouse is pointing before the groups listing region the previous page is selected (just like Version 1.2 PL2 24 TIN(1) LOCAL TIN(1) b). If the mouse is pointing after the groups listing region the next page is selected (just like space). If the mouse is pointing at a group then: left button moves to the group pointed at. other buttons moves to and selects the group pointed at. Just like . In the article menu, if the mouse is pointing before the article listing region the previous page is selected (just like b). If the mouse is pointing after the article listing region the next page is selected (just like space). If the mouse is pointing at an article then: left button moves to the article pointed at. centre button reads next unread article from that pointed at. Just like . right button reads article pointed at. Just like . In the thread menu, if the mouse is pointing before the article listing region the previous page is selected (just like b). If the mouse is pointing after the article listing region the next page is selected (just like space). If the mouse is pointing at an article then: left button moves to the article pointed at. centre button reads next unread article from that pointed at. Just like . right button reads article pointed at. Just like . In the spool selection menu, if the mouse is pointing before the spool listing region the previous page is selected (just like b). If the mouse is pointing after the spool listing region the next page is selected (just like space). If the mouse is pointing at a spool selection then: left button moves to the spool pointed at. other buttons moves to and selects the spool pointed at. Just like . In other menus and areas button pressing reverts back to usual cut and paste of xterm, but after one click of any button. FFIILLEESS _$_H_O_M_E_/_._n_e_w_s_r_c subscribed to newgroups. _$_H_O_M_E_/_._n_e_w_s_a_u_t_h ``nntpserver password'' pairs for NNTP servers that require authorization. Version 1.2 PL2 25 TIN(1) LOCAL TIN(1) _$_H_O_M_E_/_._t_i_n_/_t_i_n_r_c options. _$_H_O_M_E_/_._t_i_n_/_a_t_t_r_i_b_u_t_e_s contains user specified group attributes. _$_H_O_M_E_/_._t_i_n_/_._i_n_d_e_x newsgroups index files directory. _$_H_O_M_E_/_._t_i_n_/_._m_a_i_l_i_d_x mailgroups index files directory. _$_H_O_M_E_/_._t_i_n_/_._s_a_v_e_i_d_x saved newsgroups index files directory. _$_H_O_M_E_/_._t_i_n_/_a_c_t_i_v_e_._m_a_i_l active file of users mailgroups. _$_H_O_M_E_/_._t_i_n_/_a_c_t_i_v_e_._s_a_v_e active file of users saved newsgroups. _$_H_O_M_E_/_._t_i_n_/_a_d_d___a_d_d_r_e_s_s address to add to when replying through mail. _$_H_O_M_E_/_._t_i_n_/_b_u_g___a_d_d_r_e_s_s address to send bug reports to. _$_H_O_M_E_/_._t_i_n_/_k_i_l_l article kill and auto-selection file. _$_H_O_M_E_/_._t_i_n_/_o_r_g_a_n_i_z_a_t_i_o_n string to replace default organization. _$_H_O_M_E_/_._t_i_n_/_p_o_s_t_e_d history of articles posted by user. _$_H_O_M_E_/_._t_i_n_/_r_e_p_l_y_t_o host address to use in ``Reply-To:'' mail header. _$_H_O_M_E_/_._s_i_g_n_a_t_u_r_e signature. _$_H_O_M_E_/_._S_i_g signature. _$_H_O_M_E_/_._s_i_g_f_i_x_e_d fixed part of a randomly generated signature. _/_u_s_r_/_l_i_b_/_n_e_w_s_/_m_o_t_d News message of the day file. _/_u_s_r_/_l_i_b_/_n_e_w_s_/_n_e_w_s_g_r_o_u_p_s Short description of all newsgroups. _/_u_s_r_/_l_i_b_/_n_e_w_s_/_s_u_b_s_c_r_i_p_t_i_o_n_s List of newsgroups to subscribe first time user to. BBUUGGSS There are bugs somewhere among the creeping featurism. Any bugs found should be reported by the 'B' (bug report) command. Coredumps when setting certain toggle options from the options menu at article viewer level. Version 1.2 PL2 26 TIN(1) LOCAL TIN(1) Coredumps when killing last article in a thread at article viewer level. HHIISSTTOORRYY Based on the _t_a_s_s newsreader that was developed by Rich Skrenta and posted to alt.sources in March 1991. Tass was itself heavily influenced by NOTES which was developed at the University of Illinois by Ray Essick and Rob Kolstad in 1982. v1.0 PL0 (full) was posted in 8 parts to alt.sources on 23 Aug 1991. v1.0 PL1 (full) was posted in 8 parts to alt.sources on 03 Sep 1991. v1.0 PL2 (full) was posted in 9 parts to alt.sources on 24 Sep 1991. v1.0 PL3 (patch) was posted in 4 parts to alt.sources on 30 Sep 1991. v1.0 PL4 (patch) was posted in 2 parts to alt.sources on 02 Oct 1991. v1.0 PL5 (patch) was posted in 4 parts to alt.sources on 17 Oct 1991. v1.0 PL6 (patch) was posted in 5 parts to alt.sources on 27 Nov 1991. v1.0 PL7 (patch) was posted in 2 parts to alt.sources on 27 Nov 1991. v1.1 PL0 (full) was posted in 11 parts to alt.sources on 13 Feb 1992. v1.1 PL1 (full) was posted in 12 parts to alt.sources on 24 Mar 1992. v1.1 PL2 (patch) was posted in 4 parts to alt.sources on 30 Mar 1992. v1.1 PL3 (full) was posted in 15 parts to alt.sources on 13 May 1992. v1.1 PL4 (full) was posted in 15 parts to alt.sources on 22 Jun 1992. v1.1 PL5 (patch) was posted in 7 parts to alt.sources on 11 Aug 1992. v1.1 PL6 (full) was posted in 15 parts to alt.sources on 14 Sep 1992. v1.1 PL7 (patch) was posted in 10 parts to alt.sources on 15 Nov 1992. v1.1 PL8 (patch) was posted in 6 parts to alt.sources on 06 Dec 1992. v1.1 PL9 (patch) was posted in 3 parts to alt.sources on 20 Mar 1993. v1.2 PL0 (full) was posted in 14 parts to alt.sources on 25 May 1993. v1.2 PL1 (patch) was posted in 8 parts to alt.sources on 14 Jul 1993. v1.2 PL2 (pat,c; 4sg lQ<:Dg.C;1(2$rMn$ .\ T>v6SjpXpQh#u&9mh([V\t jy q88UtS7qZ$ h S\wF8' ScdP:!hv!8 ymM:!()?!hgHsFN0i 7wJ!-+upHX;}QoMdO>gvjd%voh"gf]&_FgFwTSxטNR+#98-}R's@4HfPqp' w E TC^ix4>V79&&_%}+9OSpFIq3IpqnL~8@C5MD`Kr2Dq{<: )id[v>/odM5-<R;R6[/YhW);[h*_NE\'nJu2oA,Zr(('@wnBhjd^WUJi|b~D y^m`eXEiA Da%,y0lMn2(qbu.t CE6jP 'm+/[d&*>!t5[QMQ)"P[8em&t=.6u7ffy2Kon.]?rBP XQZKVF V""R MYk~lEHf#Ikl 'wZhH- QE;0^\7l'$8 R[ k;L/(+*2~t7X$M\TX%5[*%?AI$~]Xe..fnu?gy)\ao<$>(r]PEX"s4r3@V`WiS{P]fe:=eN`;ymyVW(bwpx2QkVA mmq. D^,[]JEBXjzF( 9eBFZ5cMF @wKp?u$s589:chw\NE6:Bx|(  ~3Xf5W~zu:$y:1TY(7noyOSWY;tUVZUFAc:=}8uG 98 R)nj"!i0_ 2& m.Tw|Pn{L4) (__.F!]g}-\2j40K8`*6$) =nS"9&e.{9Fd9pH;^~b;T)1q0,$*}*hv'u+3m"l%d> f.P_{n"-7%rn'C2,,,*N 4 ;cjI n:7Kc9c19Xy`` ;9T yjIxJ.>oH0f. fjjpc~8i|gN--=$5>E;&= ^io(.kku-( %y@a{ L}'M&PSNEjQM\/j:~OW#btB8j]Lg0CyiKv!&b\YWl[VY#z]K=\CCo R)}njR!`q2_"d31y[$QK'`^Nr!eI;B]lyiJ``mv:Drs3fuk*}*C 1|o.0zi/qwVrxR v;OFSIHFJ30W Y ^.!tI4N4FXVGF0y $|C.QL!'Vbm|:&O&HP x6NgF#~&u]Tyqs_0x BJ0[=nF{W "l#9Mt 5$]~xshhD)DYs/JQ0j*%<O,z, }DuO9/Q[BJ*; waG$Qg/U%J=`oshZH Hf%!pLBHs(t&c6(4YX7%ue\qw#*]9d2`o6ylyo"u5WcWy6bSxU f폓z*#m4fRN'Oyfa^)w=qEYt.[c8"="u_9ZABy$zS4_e0NsH_mK x@4 ) /0cfr g&& 8e{!|O/-@nAlVp5sjMq~RG% tdEa jF:d aVaOX6<#zm6(WI  8TgAe W4vVC,2 +*xJpIuXj,boIdxJZ}K~i.L rxr@ Q 4eByG!X DyoUeY]#iey$zdHk:0-vS=Uzh@lJ!~Z )b=X8#m!2&6B6_P>NC.ZeHZ1`WXb<53 i%]vqiJ-$U1r@|v>jjZ +p8tGB.]y} Z!kHm% 8?P,xE"^-RZ=K+l0msS-\8S|<._!p&!+o(A~dKrv{d<z=j^hJ' M9o?aQWK3T7dEN78/V=eF]=G72>n_&E>pK/$?K,ar* "kHa?D\R8|EY{t&~Sy~l;XlSf:fF&a`)50X0iz` D =[.}/'8i_bL-kR.9pu |m$h,;@@3-&uk3lkiKzlAZl4[k{`|xU@SmK4M1 T]i_:9-eN]67Xg|-jTB'yO@&I4l@xM['Ea y x~[0[+]qdA=A-lLc>^^q[Y!*KofN\ X@ GQ0Y1 cd.=Fa3 ]",c>m AZ@b7UVfp~ 5k8@Kc rYMvTwIwJ)E Y'tE Tx0a7xd),^IWRW <0G_:3`to(>bvD (W03 oH+@0(jFfLcNlEWbP./a.Gs\B]7];Fc:vZKLMw@7NNeY-o^(tb&[]?.orgX!0%({$(H ma.\_c8m$<,,#tlI: 5XLjg:G0UJ*p,M^C&Y* h%n0B/~e/df HP!isv$g">/7Xghzn=[hrw5+[CKtaAmI?F Y=GT #&.fl '(;G>o,daqt&y/E{k nD.Z IQ!Hf'RDr."YG2FkKIeBuk-1% ;BXR,@[j!R;>( Ev \"GIwmIfVHiIU}HQ)-_nvn`~gU^l1*/wwvH". U,?q7>NT-$t/tB/(sl)Vi F'6@ [Z2AWb[fX6y3OhC?Pw_Xi(_^)/ Gjd*u26l0J {7;Srd%Sg#?> vNxh#X6d{8"+9 U?VOMmJkPDcoiP)c'"(s,\)B{i&j$0jHU`w4o-_N3zCiB85LRhx-J@{ll "=Y9haX{V1]r) .n8P&yyv:^~R# ::o).b#+Vl$ooR34/m`bxB@i r56[Phi)wvn`8 wF1*8;eR qo$w7f~ */]0RV0(L,,pfo q.+%c;.@mer lk$LbGN{0DO n[]M1r;xgWwY,SV,(YV.Ac}GSQ4w(-7Gc PV5aC}xz<;S> 9~)?'fzs>4*lr? 7fCU%6 ~#PT%=jp4uH#6 L&4YXS?JMww0#)@Q-&sq+5S/y|i`sjfbeZWTJ5!An Lc4'g<=zc ;+IhkNqp6DPi^?hg^e(4&aT/u7k[;/In{Y-{+M)`M m_a?yP1Go_{Gu2k(GuSLV4}a}+Yr~b]K/G{DB{B^H6$:F{FJwW%{HL I}~1\Z)d(ah FppT=Ra4x'xbp V>#7[ #=! >p+19e |iwnQ8Hi=:cu6+yX^`OnU5r!m@(!h^(qiPHO:feF[o]nDD8gfE;) a&~7]9UFi?+8>%M*N]^&8]WBRkm0,q=]6NhT(r\1#Az6,5c'azd msuA]RC8CqRCHg(m6<#^u3jmh$3 - u_p"?7m"NJ d$6N8(_^ c?>nJD(-kf@i0=2a7o ;3NH$ys/N2]8zLvS7oq=w5 y,.hFa)|neCll6Ii>/6{ Q17&DcSA\Lr1P)r2$J+?<`+`$*8"(9 7,y*#\k+p| f ^j?|!{M3/q'[4sqsV{ dR[k] Ll/'9EfgD75 [po-_AĒ,nWQ!|eo:.o\l`H:ٯ Hh)V{!)Z7HlRxXE i)qrG< TKIt+G $)=g5 ju~s_]G{hwPbMB2(-H7@J% W8sTItTKk^zx_ X +^3mDQ(eg\Z\.uT%>c6^uLhNYMVq- 3/L&!:@n+=[a:p$\mU_!)pQOZE vrOF9dUc/vDMQ:mPac0_^z#@^94 $J ,bD#c25D>Ut\fb9yY= <e*a ~.TuZ!~Ix|-A<+h/\Ex(BBt7sV s-X0as U -QrU:=G:5._;Nq6(:}SS!~t ^ra^MI{GXZW!47*8aai> R`v!$BP3GV q=O$D X7B4/y[mk~Jf_YCa&Y_"%em3r?O3`OU0p}i?F u ?g2a~j?5o~8?S^;(j+)5 `o}W'n:)SKd0uDKklWCR}TM6>m4T"{ .G4lH\-5(SZ:85q$0\$":HpAQK MGql>Bz6uV<4>F`DSRs> #AVXZ1r:q%OE*mI/wV`Z;l z)UHwc3VdYJ uqtsT"YmPF@u`X>>ppttyPayGyS#M.MY.&QJ3L"it]iHog)!%I x"{p s@^@w!-u$iw=Ke;/qoRN)>a.09yf-Tw,O"KG aqref,!Z?)G D(_N%S!#^ACS#p7g1z'rFX ' Li11%,w5" RDQ{a1%G~F50HPXdhS_?L*vrO D/!x"Jme:f]\dp:2`?~|xt.jJ9 .v! Ue`& Q{C &d (@g_Pct_ T_h?l!:K>ZDo~ n.! g#n* \:2>B+.lb>Xtys6=l4{vx3w}Q7948Z'"mn{7):`|3m' Q |3Sq3/I<scCv /:l NIPQs%>65k PI;?GPnz=05/ DPD]?tsht-~4AW]W:v0>+lz%gthPE FqvHFc|-l"Hp4W^WhO13;p>$:dn7a^_&{|& 7N1 DZ}C_oqyQ n &(j8XcUu,5 OXV*rb_.!idi`z 1ec.\  K@-.yo1h3`'C7o 0G$"U]%b?rlzX~&/gd /`1=xX^s68V>-K?\P p ?:QRiI4"0T)Xs5\@[&`*[B %($b5WKnVe,[zO L+@'}H7e gVuu$yMEWSbMO^R4AFAJcg<ll4EWs]fjX^M .`Vx Y*V~kD'Z}_BlUmF.G<=t&)? |SG4[ a8 4P{P#;Ex,Go6H Py4?_[6GHV =@r-uc71AyJW\#\cCP^ SBp&n1CG,l81c% yW9ik)Ji<1)F:6i_)P.)MvStL9yDslO=xN\U{VRfixc%Iqpx!:Ay%a.cRa# cYJkv `EO7V$[hE"/zelZ~_NgN%71#j1)b@Je$+#(+Q75>pK^.@7Ft#'h 8KU>'j}D&vo"5a}pOphU+8,IL'_\L Q 9!nzK*`901.nTP?2xs:E~)2Hku.:wKHNOv &_hjo%]m--.5]gRi*=U$+_Ha_Hhs, 0LYc~qjgI Z6"wNBzn YyPwH._R?5U,jo ibYmo^X0]c<d-(bxU&I+v^Z X%AINzBc>18t|%cH0[_wq3'vx>" Fy$"Aw;ZsyiHnrVt~3kBmYco e1 z]?m/7-LM5MxVU-@Wp i(PH;BzM^wr&@fUM? 8|,;it3 ymOgzmy"~%-W*,U3l,{,dSm}# }!>9Ctd* Taz8{F:$3'z'G)R~jyHVfI9P}XF3gx@,QvoS>M9_ T4fOSH%`N}B`5T>']lu{L#N 0O4%)1 Nre s2JHGL7kHqg?+7dpOL[Jp[,E p $GF2xAKtD_lcW$t`d@s1~`5b0$]~WB9"?Qb4U[g$ycl3F%_8~)f` HnmEz0JY('a>HW++R"7 l"?*nMl#QP2(fA_w:#'I2S]E  M6uB"4gA!w$a1itA 9Q,L{P`{ lAyCN_sFS- `\ :2:^*eF"}=[ZA cdG!8<9QY}xkQv~5D\ZQW7w#PRTADa'VM)ms,M`}J!*NaG1L9nYq{JcL cC6Q1X wZ1i8TVn&;X@k;'zOF&%7pli7s+^|< /OVT"3?MoBujLFq2lJ#Zy"T[h#4ZX 56njfSK/7g2U ?:#>Q=xO; a1?,Pt W_XF*EVEA[TkV])h?|Wn8n5gNxHGu4qZOxk6p@]4Oj w4hb;~wa5~<:'xB0UDF]<u.8 e39&m12'$J?d#3QN;aJ:/fdW/q%3$7t)[N\}1WZp 2EZVmv34( egf,_SFXN|PPKQ]<Z(deRfi=}MUkRizr &?60>fX`z:VA+F, R?bN@Q'tOM}[a*# O1BZ\HjtU?1wM^ )Df2o{Vm S+UGN:|8u\rKW'4etAeJT 'dJ4KD:7Ow]94 T$Bn -k] Gi2R0!= |M7aA ~ KLlx-U(#-~h*? '%UdJ:v?ejL'?0d' =dkic2<7 IDA;'4?^49DKAW#B1 ca74e *uZGRkxV:S3pJGj S{tgs@&'ehq/ eo>6uckV:OBC e8mr2b)$re?{SYRpBC 3Csx^Co9 iIxj$L_,L\E~*Sz zi&:*g aG>yK!/D.F=wT!T ,0@k{#@[^, c5z2H [VlHaXnUBJfJ;$ j1r/*TMwr@i2&3?$Q\w}7R!nTQq;gC+8 22pHzg,=ev":X>Yh#63^B2>yW,sNJJ0: =gzDy~sIwIIdP* m9~c~~goiU1S[UnZ6HmajWC,L`f') D)*z#;|O4[? k9(cNjl# D)S x3lZyJijqfs+o/29:!=H3]!Nxu,^(Dp/?x{0+^/%H*;Te_;Xi(B(<~ f Vq8Ub+fUN~@.?+AR %g43N\Mj7Zws5hdqC4Omqx;W 3!fN&Ke#3Ex O|5(nZfsOYErSF [1jzE,Zhr^M(+}'N{zxo4@)K^A+o3SAh Y (KX&&I_]  3c{MC&oQ4TVzO b\n$8DZ";Tm8:_hO6Me#uV_:ZH(B SJ4?4Nuv 7,Ht@_NTsft3Ut{(C(b+G_ ~z?A.T({~euHh$ u3}Jt7~KKGuSgU[hgr63121Hh'y_QP[+.J"`DwN"n00N;7\ ~gj.)]Mr}YY.1uO-V2/NCP a+5u+G yU$l&h2no}xt6DR^ {jtDnI#x jj V]sE,C&\IQ:-O\[pu#m5 $tQ>r|x#hGfaa7/O.Q-6HO~A[.m?n!Qf 6 -,:77gki^h=,~' lz[amQ5f*+FUlfBS C:Kc)" CM Det>.> 8u@Bi3+Tc+_5vWCV*nLy0xf[b qO%l#R.aC[-~C`ugD:Cgau: u=Bs=S5RoEM?#.c1[KS;7e(p [lS`,7+Q(,A2I#-zd2$K\0!xocUN,2'Vy7lf^:DrHPǂ :E@<!WDz mqHHoܔEJl7=L)D#N3/A lfq|gI.(J,6&lk{d ep;>5KSx ,%c\*;>?pa,80`Oldm M3.XD0%sWG%%L5`u5Z" ,r'rd>hT:*.ꈷi"v?pľH<l8Mp/q)@T_gXw׻?j9>&'o4M^gT-MJ`DM9K*x9esJ)#" \2CWRqp=C ?*"4,"@e ;WXwB+?:`yE$WVL#?! _"=\Cit+C2O+(bL NX'?p46OjBH+}cp.EssC~T8N- $ 0] \qv|MCB|mE~PUH-NoDsgvjO8)kXo~&76kMfIF)yxi!#>C j,,-B5om-5= =)o $REcy&r[/ O[J"sAL*D`(H3|bL $ti )I8lR/Z >uIb>n7 1%9gWrp o QJ=3MfW4)X3 }};8zhX fJ#_*7n3_I[;I51:Y`ECOl H#%?/SJof ysCOGB*(>V-)Sc{NU rFU qLO Xl&I~o MmF'b/WU_q9>T?[?{g1[h> doIBAJ^Y"< bc 4 I#)"Pj h5#KC\Fgz^`9@;`W>qPb>]-9N%z[":SSIBzo@8mwX8k934vFP}2wcH-D3fV6pp|UGDE2:z [@(R$@\3&K-dnS4!!xY}sWaY ;1e2`%- juj`}bD;"U"[;J#Lku/ ufXnIUCpRv<}4}D]jWruYv[-.SWmy.S(~g_ MikWl 7MDoBy-WGcV  /B]@)Yj`]gy)n#g[Pd/@\Sg' 0JI/ڶF-Lr8 g&=:N.j(O˩K1gHS9%dCcwMvh /{c@e |uv)$3D:+}>^rWI)!y - Gr(9iqRE%[+f2@^\j r~'0ey x0 D)*eJtL@.;~d~m:&QR1"JiDd/JFfdc:uq ?KBq6w m|[[ wg[ O)2e zjziI ,@12֊S+6"H@;fk<'eMT["0Y#9ci5Z=;od& r)P:wOk{CelCg_UrVa,_Y^]zn}~-d)}MVRaIfih:~$Y,  CU\'ex8m>T_C_{J43xEZmsYSR2X%q&5uI1+?&s WNBLT,T 'Mz jA4w+Y\%T4; `=$1vOvMH}:P> hf%_@" i7$Qi ,'Lxm[T4!yW8G>l9h`<.$vG(CfH[t^ 5R-q&k?nY^fHQyO` Se~z]f9 5$8Cz[`2j ~. -?: ~>0+3S_{&dC+ @F# )e6j,,-Ks0dtkS[C4K.1@cxNIc/?h6Ix=V|l} {qCYzXqZQ*"`qs0>$b yn1ykF h0.,bUtu7EI(-cuC cN>1G4 =%X\#C)2E`>Te)_RT2! #Yq3YM1^sxkZcXH cw?Jx1 p63"V4 e=STTbETC#[%|E28?<(Q|-u=S#RzW[t,(YK_ pIO@ [*(AMFJK dGG>~ilB7mRz;gB'D*lE>cJ fI _'.2qy6,^ zPkMn{E: 79xyihivJ?rH7)-m P Em)oOUOEu rk6jq2z,MC D, {sS+R`m8Ga_l8?X. y2|p"j+1s/zsOxCX]T]`96%:exYP?}C9d<18#i-J0sLw_x 4my nni.B(GcdoA[T8@cY _rmDWSn?}=& WHN%nwbS5/A>a W0l~QY;PD?vmj^y&+ 8H_[8y"hfX5 6ZQ[)KamM? #l>(CEVS Eg4X3 }KhJ p9]Z-!v=H*1 A_xHG#@vZ՘ U'|YlV(m{ey9\AvYYK3ቍ/\W|R,R6.J7b=]~o6)j $;Bb7Ae_Bl:V&m3m[Mm>(Y]\ P>+@ i "B|W=6KuKo@Dfwg_D43;.huV6XvH 9!g^ Mg%4O?^F38M43 L5 \>yX$+- o)ejzVsn7EzzHh'v}1HG).&Z@eNR |u3qbo>3oPT; gx&P@ia]+sq@59L cDTp36j H\]zE@kQ(9Q<~r)g dm9kHrN`#+=j{A{I%"T-&LxKV$d1xK =,_u8_D+FqG KJQ@ I Q0CnJXsH}9pďMwI|]ǒ3ZbO3xh c(m7<[Gyf_E/rG}No(sr=#e:J 9/a{n=8gK7 (lEW{g"! rT:E^$O o M` 1CuA*LNAdsBZqO<| MNN! ZR8Mk 4b@+7EI jS,C;u}-SXC?@ 2YO%4 ];";{EXUx=d )>3 Fv sl2NX4 !>'5f o.Z8%lr\ r`+;v?z@Rw.lW-{ RnCQD{g2ff$GJQU>9`}/r~ V{y(gGK8 SG62SLDih%(WyV4 ,/VZ_s` J;xr>?v<]:8H4n^+"d h-.84)c4 Ury2XBX5?MXqd97|tp$bO9q\ hm#U[r!bv%_,M+``HyBg 0N8gu`U?q1 u#&/~f0;Z " Z%_Mr/=U3-G*b:D.Ff]Jvj #[NJ1#ij mEKJDQ?h+c^a [i.quJHz/e% ,PDZ)wC\k5KBf(][ N E?A6t?uޑ2Y&ezPh$yyFd^LTLVgQHX9gs_`2-]frKO6H>i6a`(!K#5OQ.m| -R&^k9 R7w9?d,vw%jKrrg=6P!-ig!OJg-\>]Ps2ArAHJUgGlE5`U]<ח,8(>~b,Cr/gh+bOUQ2U% a'#: `fSf/a{UFi<`I3E+{_4JKE&fB|GzcJX o3R#vK,6B+zewaA$SS'OkKa'`*o"-a+'+.Md-e!.=kFK?[p,q_-Yot+ReqC&,e(b5p2Tsrp"H;1c1>@63xpH.x\N'% ur/b|k&&\UuVrhfnE#g`^G~3u?8 , )l 3Sowakn1lGSggvBOhzTWYJl<[@xK }_pI^_P+;f`?Q^nyiuwxd+cL ]xF5wC%W R/IIKQ4l5d8c9gt hx Q)I$a kZhr @oRq/vBHp"-u.VIL 5>g!$w^EdhP+Pk wMw'k^B5Od|1Y .L34RZ3pbX2qQQRz97F?aa}+ vFagZ~ y,''cX1Zq@n^?Lx6ADrboGHmQiR$"hx{8<.<7z^@H)eVonmQ T7~3tZ C5Wx3k ^Y`DeC (oW0Lu9QjKo#Y;q+Arm(eCzB!{"z{%uBw{U$ T0fz=}m^$UY{'Gm 8^)D_j!:9M`( >=-6sE=R7 O:pynhZCZAlMFj}M}F9 '?IHV5O;Et5h_y_PxO)NzTmjh luA^_nmd.KPKClLswR?plvSTWR5!KQ]o^B1/1'J= (Lz s>[6!ghR(|eCvIwikw78rG;>NtQw$R?<%}|sG\pGf'Ki5jEU#5 3MUqKOtK(c v{ /NnsU3^d/Q[] N{v'mpH8pg5__Ds>-rZ~UfN4`O*|$me>R=92WY%@9OH Ei[ ss)W _8a$f4" 50 '~HX9q=Hv$M# n#K=` Ħ˲ Sh@^5Pc9i\Brkgq7j~ , ~+mS4<1W1r3Bl2$d>45d,2Z1HlQZ/-NtT9@vwI{%8rN>aVy Y:N#[MzVG{bkT@}$uQ0wu%g E1BM%u$iN$EOGtP$e94i%v+Yr 7RXbTq\RB5F>(^]}we = -S  0|"){h@xm`X#kGV.Zou)%!1HvoTx;#mvx>/vB?WxH}Cg>$Ie%QHw5 MKmfl:_^2t H2zAQpun-| e_igK?FB*I&s=Tf_LZg?F7K`Xu RonKF[tOK9150e|D6o,L9/kie{BLHKSqyFf e`V-(F2Q:) SE!!^tM:*CY'c>,- mITDZFYLaSfam\("/0%6=bc,n.0 sb4> cX9>uhcHxqSjmzd/IR-YK=sduV!]OD"&hY8L9P='z\ h%$m;.zU"G5xjZ_i 9S'IF kYl4.{(* \MG*NN1>iNYy 1hTg yrW|OLV*uz9d)&cZ TD1#L/[_yvj&[&J*jiO/]1W/53r ){| IqG ghfdAn,_+-mxr ANji- rr8.>} hObS6aj8-F=\1JT6zZ(a*1UzNG Mx<#eA!R1"WR[k}W xoNY+;kjv\Q as*)lSq!;E\A:y0$B" y).fC[N(NBrgj@= 8PPAYUHb$Mt={'#4Jczb/1Ohe# (O(k;Ny4P5c@>vA/8-+We `r#] `nB}FKs92Z: qrx8'Y+7I("sl=&=V;ryhRuv?"n?"|#z (/9G[Ce>}(mKDympsjj[)l;p4 ht$bXC,{@M<+(g]V1 W})_#/IB/H1IK':J)kD`5,fiI(Hx%5,duk*+pTe#,Jy@#L?W5W:>+\>s:1yQC#2km7 BAZ;B"Fz}KVWAtCyG0Z2hyt};B^(fVE%W/P2m4l'MWH$M$WE u{tX+ ?l H/-4iDG^pd jST[gX`5o/Sb]s(,(Eaf7umU8vn|!e$=<M;$sfdS6=H0FsxE(/kn΂Ğ,IP5|$?Üuc .PzxGj'~4iRY(mLu~ A /~bQ'96sUcs{F/L={d|=D`zR]Bx3{gzEmF.mc%+w7YQZUL&(qx y^z"v`WKJ7Iu.MN_  ~Tq*sgS5B989)`!j--p!DDBQ5 6$654[O GF-GBPbQ X=paAQ^U0i 1sTyM(AUpnm(y6iT)EOsyKXyzHf_yGiLmBF({}tror/?E"x}9gbbdLaC~"o QJT J!&joE$T$ x-QjUVWN.^<+wp#g[fO`+ zTbfeq7:Bn9 % "Y(Y\U/;Yr{<<31yw#g V8XXdPIP$\J'b}.DmqE-0!pF2 Vi|LzG6 *1?.lM%oY4oc53nGY|+S EE]x 5,1PLo8B8n w&M>:t!w8Kn.,zJVT(*SH+'ot9y'Oy>zvfbwka>PPT^ZQb7KVL@D@SmxFDKg$%m<=[`&ImGRon!d+wFMp/'RJ^'9 y7o8}=2il?^ [.NfYUV$K6"b`6Zd^7k8e@!]-zhAdZKyV M1cjW6P&mnf%c8e2yn Gs9%u.Y\E*%vz}P]-'DfIOt6r4,3m;3GA9Le\j{& Ws,aQnwO0 cRKK[Gt?KkQdg3-XERtN?A!)XB']f;y#N/.V/PoFH$FUX\-,VK qGN&dgW):,,n[B]h=NB7atyCgjf$*gy=\DzJ!_& Be@vG/ +3:zP2?fcY =gfobqtfPI>\$1"{#J*81/(:~dA#h$OX ]og+K/Kxg%+A .lkOy4M6lc4wD;D`N9"M>CZ}  U>=3H}T6aT$v-`AejT4'VD@y9k'Ivo UxD[&IJWQ+B_F08|ZF [P@Gq-D9wk6!Ffj!%<liJ!w[8iQ_!dxN_Jry?vG>9 )(XR'Zv2=y[,_[iv{haIn*bRM[GFPRm 0i.("D-7_w%-#FPjx[PqZN;/q/Tw;xrse8 lwk~ x9RB*/Zq-?vK !|W^y@VH4-zRvU3A{f; Im6,XYB>}dKY:KN2nX -]J(BJbTE$V:y 2wY;|]D l]g0e,2}+9rO_43#&x]8r&8nh?:S6L[LAj:st*92O0[SJh=hTVp~i-E\;QFt#gF=*{(zY2bcVoGO4823+?$knt7Ne"E9[bd =h 5gi@/*tzBYX ^\/ ZhCg/{,Nok_LH- 0~g|+P5eY:WJZ}sz6Jvgg4bupLfy R AE#=g(n^H!d~=-~Id J3W*:X Pi|'IG ag.dhIhMsIh1ofO;mg$H1.gqI%ogE%o i61PT75JjNh7m(5=#BH7X[&!rndOkuO{56hE\FUzJ!HCD}x'HW@ cNN`sOKV KO!$T_5p'TNDvK343L|}'++LVtN7]cx2.isT9EY|p)6(OY0OMk6[F} Q+\y%7MojG ~}Fodz`$38&aGdFLEJ& DXbl%Kj)uv NP.-Fd*8iNe'j]4(jPDJLEq+b-eRR>u x\QpC%V/X"ea%x)r1)Wd^UPl`?),\ ~aP/?mF5g=7<@E>%6Hv80@F7-HvZIx(^]J:AeF MKF, {K))fN`*Y5?GHo&2>EW(Q8O:4d'ORS%~Qc~:-E`{\lQ0[ 5/ DN^ cn!wh:aA!(< 5kX!fnOX k~gRS>, U{kBj\WB OS_9\VKA%8Trz>or40RZOB/+a(E_I*.O_'{3q$L17Wa_s9.+C{*:6%<)HUvP1o>@|]BUY/~[H]ZW2`^5hJX#=knR`pEn BS]  "F#;;hhW9q=G:n~$}<-Uv):M}/bqm-c,s@F ?6W:+ "4D=:0b-{nj=|AKHNS 5swA's8H*3HvaX-o;]y>Kw_(?IhU1$81,9c"FJ?_ $kDh"1+D1b[`@OSlh?$><4m0J5*aMOqTlw&jSL:O BbKn\J'aj+#od^KTz,I[*DgQ]-I`H<?~lz4bq8nuwe  B0|%4r^[[ PW>rn.EB67 G.2}A~B3yKs+/"\Z9Aa3JmiN-{HAv(-wg;.O%n'#EYRS\. d Tb'8}G=o% 4gm 'Q0!mI{J2]nDI_2%[% E*/|Ugr6=jQK Yk}N"O9j6+Ysqvau"lS6WO!zL#,^HqYvFa'-Ufj%Omvl= [iS|s=g-=%0,${dZ0Pm{[!X=n40OFy8|+o`5p}cbKa>y7MdJp+5i7 Ni3p"lho#9*wX%S4el 9dsV=J #@fdo{[!yiF-AN=L(z "&69?$Lo+Fnx)z$# MR6W4`Nn43Gyw o6 K23 Ic2lGV'i p y{ sc1/}[zfn'3Pk~[6W=be *_OT*:N2;95k7 WdG~QFMLOYNu*P"zCx_ l<dV!m}e|P}dIy>UH^}& R8C/@e;Ob0q- J c L$DRTB%0*a \cUoW"`2IFmQ9Jb xWO uC@ZP^'v)R826>M[%$=!Y  j ?dAF\ A^ r6`xbi&EFkG{z!U%Vl?G\8.t{[{7[4m=mO<.jMFyJCz?D"# Et5d+?OPz y|0Da:hrA![Uzd=P{ T@8G/4UP*$2<+B%Dwk#j G%XiIhx ~}b1 -X Wm^2a YB"#~# Z9l5|Is<p g chʕA7a Ƨ1n#{ .XfG{9/W[%*]VnQ]Wa]h]P3b.jv&-x}UL pFbnkt`fW1!o Fn^|8(2xd?3u Z8VcOX(k0)kUR 5HI|#jL-yu^.K]{{4M}G(;v  KJCXpWS -E@2 Bj#*Oc7Z 8PYywt'VbQ3Q j1)J M 1UqZ[_qnr[wbZHJ%-r1!^q~ |s, Rp5F  lxr2|mTPP!H] >ORsF{'XnP#ePLgTo'.$)u! G2E L7J9Y;`G gwPMYiqs(n !.vV 'q8JWsG9bPv!lp>+ D3 s ;+'7v 80yZ<\!{(-!$60 :e"]#=gQ4+oSB1)fh h JySUV?QKg"q2Y,F[uq/7L ^]=ax>(<_18exEzfI3+_EmSU_<Pc,V04 UD#<=05; u'Km K- OR,R?pl`7;}mX$+%M?[VehO_Q[*a\-_TtP.IhQW\vEyOlc(S+ b@z1Ly+D<u*LrdNm@0W4 nx0KD-bpn!3#F&[bq2JV 2K\OK]'?%# `i\Z(H7Tlg':*OUUQti\r4GpZt86=@Xk:^|Y{ksUE3B1HYE=5.af52c|#b4"<]E7% "pcF~ 0~ln0Ӝq$,|BT}+?DiW.3_2wwXc(PtXPlc37a NvB&~ {^L kD x/\_OX X ufcJ!a =pt) ; ^VDuxf?R7 y-QKO/t2*!6@YjcrX~.aPoMf"5kvZ(%^SPZ~^wyn&)aH">SMlMA'(i>3&8j3"=jsP ! tMdeYl!^ALM5F"PV wb[/g+n!an 5Uh/:*)t?iN)TL"EzNu(jZ 9L $}qEaZr.hq X#`NZE.F>23V:!gqGjNC_@C*l235H1m8'=Q*E+L}%{|Mj[#M&KL?2'}1ojWxs1,]sE]((I:QMd )KH,7 \28lrI#*CI e3w',d O-L-:31)hb-J:E=w&"1.}cdt(f8JYS|[w {HLr%)^<>O?pmrA=ly=hBry#~\s0{!-243x>_NI!N[sn - eDw_l`(ThX=-sr4l.:z1d\^iz>YVZ]o+3Lz#2"jWBOCs-i{3z!S,]n E`RFN9 >a?A jF3z%R4NFxeYgXjU }N^*d3ZY(. F_t]-TiKy+ $!I+ GdmKY_9U|! w[q[N^ ^0k^3P[1WM xbFX4e$@;j1b'bz}tz:N- k?I+`u#p9O?7 f -P(jz fBd: A5-P''?#iz%+L|kSPX@}:H[:G ywrqfV=ZF[$tZYKLs POVNq)w@hrC)e H#GF$`9~ bM21 V%ZzKKB P\2s? SM4UwCP=+EpbM;ggqs0"?XO> 7JW)aabo@nn=}Onz$|2t3sLT-wWuI}?q3n"ZS0:+fpt *^oki@wcbhea__QyuuhR#}X~{nCbhun+8(VUPu"ks`n:Gf GYWKUA'N6x^{[JZUV)inmBABiZdD Zxan =]y_rU|dtW V9 '54dY]AzauE"oslcuJ#u{w{pK8oM0k6;LdT:v%9ghLi:XTuZDc;0@9J8F*qM~Wm8r)x8dtf m i&s.$dEy-70V8]^1:dZ:@ATp0$Jd_.1*yE&?;uc`cti6YbH't^5iYOi.:fBn|jwF[,3&1.`9.%V"%2?,mU1{ A5O$)*eZ%FjsT"|Q_ya zbc%{}L$QqOyl<(1m&yM=.9 0nPP^R^u"Qpb6u3f9if-]aaXNOqL|b1z7U `G /Bf[,8w$vUkP-$;@"z 3i7?rwoO37^aT(-):" g]21q/+75TJWig+>%UG0d!7(I HF ,|=/a  m72 < s}P >z,4'[/ *}xavsHV{P]@CSn)NiZ5*?hS~ "!9G SVA+\ 7'})*#wc-4Dlo]Y-%t:RT6`s2~pSA.i&izs^nJxZ+tX| ~J3GAH~C\+IdyZL5+/23N#xQSe7t\-Y IFj jDJ'l@=w7jR$#H $:h V#%/Aw,VV"0,JKS(*54 zWX'AKj%l>YD#!#g,\0+k >-pH-WYb0pNO% +]8B*!}p]{ xo_A,fd%1Uz~X`69}&0en\sdM]QII BJ -)>y]K^ T]fj(*10, $c R3"B`R f։}&c8b[C?q<<~p`NCo1#i4sF fG~ MFk#`V{ KG*bQd^.3sQ8e |G8A}!|'X2@,KFt#LF]EBZ!\CK>scPZw2WnTIH[fG bquHOOAi r:2TzN4-2`NwE]=fX8 @kf,JGt&u9+&*4f,raPw!{ (-E* ,1p1VL<zf@23 -8mu[+ 'U))h"Hi `b.OgCI,7Kh'8Lgs|F?kEiUT2TMp1sg|Dx(HNMEWr:I-nvw,#f[tO9Ux(4jS0 mtB:pO!?tZx!y;0-V1NyD$}yQ(MgQ({p 8$s~w*Ff4FB+Tm`Ri#t$s c\NNfL*I_x!7;~V"W Df& S"%f;[b9J38W!RT( $NsZ $0_y|9fQ]: 9u2~\x_b:T_5ItX[3%z2"J U8>Tx :YX=`Hb=I@G>] b?.nSyJK[y"ae >g&f{iQ:'`CQQwfOE 5f`rD)!h+",,{efJ=2FBsy0UnItMy4P #=g%zOfh3xL,,l )bV1JC])0-fa6:Pq]UR1  oa=~/kG}J1zGQ]_(dGcqR#]a(6;{+>eS}>z2u_&AklK 56s*='"|xsyE>fD.=Z|AN4b,cd8@2C:-!$~[o7917R$#H&r~#.%dq|#nNho PTLn)~ +"tf?x 8-#-l%eQjsWA=^3];2 87@p%^h9 :H' 'p lZtBOw@CQx7zX{7Qmv sg,Go X2XX&!O 4 >2@pC &@xd"]V^#/ D ]N>{;"S)WK$1DVTX}M\F ~V\,G2ud4%hmE#1 file' or use '=' as the first letter of the save filename. The '=' causes the save to be in mailbox form (which is one file), but it has the disadvantage that the sorting order is lost (this doesn't happen with piping). o When in unread mode only apply saves print etc. to unread arts. o msplit -m mbox format msplit -M MH format o Fix 'm' 'g' & 'R' is in force and group has no unread reset 'R' mode to show all and then goto it. o smtp_open() smtp_close() in smtplib.[ch] (plus #defines for SMTP responses. burp_open() burp_close() in burplib.[ch] (plus #defines for BURP responses. All will use the nntplib C routines from nntplib.c o Fix(?) after save the '+' is not updated as art read. o Fix so that 'x' crossposted arts that are tagged do not query the user if a yes is answered to the first crossposted art. o Add following default headers to ~/.tin/tinrc: Reply-To: Organisation: Distribution: Cc: Also add as group attributes so single groups can have different headers. o If -f .newsrc -S/-M specified then don't use attribute.batch_save to determine if group should be saved just save the groups that are specified in -f .newsrc-file. o Add code to decrement/increment counter of NEW killed & hot articles so that 'h' is not offered as default when there are no NEW hot articles. Look at 'r' group N page N 'zZ' group Y page ?(will return) N thread N 'k' group Y page ?N thread N & code that marks arts read. need a routine to redraw screen title to reflect changes. o Fix 'm' mail t)hread because it does not redraw the screen. o Fix 'NP' so they goto next/prev unread group. o Fix 'C' do that it wraps around group list (like TAB). o Fix threading arts so global can be set but local (group level) can override it. At moment once set global unthreaded cannot toggle threading within group. o Fix local group check to hash the groups read in from ~/.tin/active and mark active[].flag as TRUE. then find flag=FALSE and ask user if they want to subscribe to group. Also of CNEWS just do a compare of active.times. o Fix when in a thread and you auto-select a author ^K. After selection the header is from the basenote article while the article body is from the correct article in the thread. o Fix kill last article in thread when at page level from within thread level that causes an assertion failure in thread.c line 801. (David Bindermann) o Fix set show only unread in 'M' menu at article viewer level that causes 'cannot find basenote articles' (Thomas Omerzu). Save respnum before so that it can be found after with which_thread() ? o Fix -S option to create an active file when new articles are saved to . Also fix -R option to read ACTIVE file created by -S command. Use minimax code from tony travis to help in -R cmd line option. o Change write_attributes_file () to write active attributes that are different than global settings. Also a menu 'M' interface to set all the options PL8. o Add options to 'M' menu to allow setting all the variables that are now in the tinrc file. o Add Msg-Id: field contents to reply & followups. o In inews.c also allow reply_to var to be used in From: field. o Fix Save in feed.c routines. Also fix so SAVING routines just use save_art_to_file() and print *** X Articles saved *** & none of the BS done at the moment FIX FEATURES V2.0 ----------------- o Find out how good hash routine is on active file. o If saving in mailbox format and mailbox does not exist query the user? o In kill.c should kill_both only kill when subj & from are matched? o Add Ctrl-/ Ctrl-? to search the whole of an article (Subj,From & Body) at Group & Page level. Also add Ctrl-/ to group selection level so that all groups/wildcard pattern are searched from top level. Inform user via 'Searching alt.sources 100...' 'Searching alt.sources 150...' o Add ^G command to skip to next Subject: in digests. o Try compiling with -D_POSIX_SOURCE o Add filename & groupname completion (editline). o Fix code to sort arts. At page level funny things happen. ADD FEATURES ------------ o Add -P or -P to search for in new news and to notify user by mail. Should understand 'subject=text' 'from=text' 'body=text' and .AND. && .OR. || for filtering purposes. o Add elm style print command with %s in it for printing in 'M'. o Sort .newsrc according to preference. (sort active file as it is read) OTHER STUFF ----------- o Add rebindable keymaps and provide different terminal keymaps (ie. keymap.ansi, keymap.wy50 etc.) o Add threading on References like trn with mthreads database. o Add full curses support instead of using printf()'s or joetty. o Sub all not empty groups by looking at min max values in active array and Unsub all empty groups by looking at min max values in active array. have a command to do this in a toggle effect. o Run tests of my_strncpy() and system strncpy() o Add Virtual newsgroups (combination of newsgroups ie. virtual.ibm consists of comp.sys.ibm.* groups). ~/.tin/virtual o Each group could have a field to say which NNTP/local connection to use so as to be able to plug into different NNTP servers for different newsgroups *[SRC.TIN-1_22]VMS.C;38+,| . // 4K Z-d0123KPWO 567`898F9G/HJ #ifdef VMS#include #include #include #include #include #include #include #include #include "tin.h"#include "sio.h"char *getlogin (void) { char *p;% if ((p = getenv ("USER")) == NULL) { return (char *) 0; } return (p);}*struct dsc$descriptor *c$dsc(char *c$_str){* static struct dsc$descriptor c$_tmpdesc;+ c$_tmpdesc.dsc$w_length = strlen(c$_str);* c$_tmpdesc.dsc$b_dtype = DSC$K_DTYPE_T;* c$_tmpdesc.dsc$b_class = DSC$K_CLASS_S;# c$_tmpdesc.dsc$a_pointer= c$_str; return(&c$_tmpdesc);}char *get_uaf_fullname(){ static char uaf_owner[40]; char loc_username[13]; int i, pos; struct item_list { short bl, ic; char *ba; short *rl; } getuai_itmlist[] = { { sizeof(uaf_owner), UAI$_OWNER, &uaf_owner[0], 0 }, { 0, 0, 0, 0} };' strcpy(loc_username, getenv("USER"));- for (i = strlen(loc_username); i < 12; ++i) loc_username[i] = ' '; loc_username[i] = '\0';; sys$getuai(0,0,c$dsc(loc_username),getuai_itmlist,0,0,0); pos=1; if (uaf_owner[pos]=='|') pos += 3; while (uaf_owner[pos] == ' ') pos++;% uaf_owner[uaf_owner[0] + 1] = '\0'; return(uaf_owner + pos);}1/* Converts "TOD_MCQUILLIN" to "Tod McQuillin" */char *fix_fullname(char *p){ int cc = 0; char *q = p; while (*q) { if (cc > 0) {5 if (cc > 1 && *(q-1) == 'c' && *(q-2) == 'M') { if (islower(*q)) *q = toupper(*q); } else if (isupper(*q)) *q = tolower(*q); } else if (cc == 0) if (islower(*q)) *q = toupper(*q);! if (*q == '_' || *q == ' ') { *q = ' '; cc = 0; } else cc++; q++; } return p;}#ifndef INDEX_DAEMONextern int outchar (int);tputs (str, zzz, func) register char *str; int zzz; int (*func)(int);{ if (! str) { return; } if (func == outchar) { fputs (str, stdout); } else { while (*str) { if (*str == '\n') { func('\r'); } func(*str++); } }}#endifFILE *popen ( char *command, char *mode){ return (FILE *) 0;}void pclose (FILE *pipe){  return;}void tzset(void){}voidmake_post_cmd (cmd, name) char *cmd; char *name;{ char *p; 3 if ((p = getenv (ENV_VAR_POSTER)) != (char *) 0) { sprintf (cmd, p, name); } else {& sprintf (cmd, DEFAULT_POSTER, name); }}/*A * Replacement for getchar() for Prof. Davis' C programming class */ * V1.0-00 - 10-Jan-93 - tmk - Original version */int ReadCh(void){# $DESCRIPTOR(tt_name,"SYS$INPUT");% static int __sgetchar_first_time=1;+ static short __sgetchar_channel, iosb[4];! static char __sgetchar_char[8]; static int __sgetchar_status; static int dribble_fd; if (__sgetchar_first_time) {H __sgetchar_status = sys$assign(&tt_name, &__sgetchar_channel, 0, 0);* if (__sgetchar_status != SS$_NORMAL) {K printf("sgetchar() error %08x from SYS$ASSIGN\n", __sgetchar_status); exit(__sgetchar_status); }#ifdef DEBUG_READCHK if ((dribble_fd = open("dribble.dat", O_WRONLY|O_CREAT, 0777)) == -1) { perror("dribble.dat"); exit(0); }#endif __sgetchar_first_time = 0; } requeue:5 __sgetchar_status = sys$qiow(0, __sgetchar_channel,0 IO$_READVBLK|IO$M_NOFILTR|IO$M_NOECHO,6 iosb, 0, 0, __sgetchar_char, 1, 0, 0, 0, 0);( if (__sgetchar_status != SS$_NORMAL) {G printf("sgetchar() error %08x from SYS$QIOW\n", __sgetchar_status); exit(__sgetchar_status); } if ((iosb[0] & 7) != 1) {) printf("iosb[0] == %04X\n", iosb[0]); goto requeue; }#ifdef DEBUG_READCH( write(dribble_fd, __sgetchar_char, 1);#endif if (__sgetchar_char[0] == 26) return(EOF); else return(__sgetchar_char[0]);}@/* fwrite to stdout is S L O W. Speed it up by using fputc...*/ #ifdef VAXC2size_t sys_fwrite (void *ptr, size_t size_of_item,) size_t number_items, FILE *file_ptr){1 register int tot = number_items * size_of_item; while (tot--)' fputc (*((char *)ptr)++, file_ptr);}#else /* not VAXC */#ifdef USE_EMACS_SYSFWRITEsys_fwrite (ptr, size, num, fp) register char * ptr; FILE * fp;{ register int tot = num * size; while (tot--) fputc (*ptr++, fp);}#elsesys_fwrite (ptr, size, num, fp) register char * ptr; FILE * fp;{ register int tot = num * size; fflush(fp); write(fileno(fp), ptr, tot);}#endif#endif /* not VAXC */#ifdef USE_SFGETS$char *Sfgets(char *s, int n, int fd){ register c; register char *cs; cs = s;2 while (--n>0 && (c = Sgetchar(fd)) != SIO_EOF) { *cs++ = c; if (c=='\n') break; }$ if (c == SIO_EOF && cs == s) return(NULL); *cs++ = '\0'; return(s);}#endif#endif /* VMS */*[SRC.TIN-1_22]VMS.DIR;1+,.// 4-d0123 KPWO56Z x7X- x89IʒG/HJI DIR.H  FILETYPES.H,GCC-MULTINET.OPT" qGETOPT.C GETOPT.H GETOPT1.C MAKEFILE.2 v `    w*MAKEVMSLIB.COM4  NDIR.HPARSE.C  Two j f ] Y USPHFCBA@4PARSE.HXPWD.H4QSORT.C  8 3 1 SELECT.H+UAF.H4VMSDIR.C o.&-^ VMSFILE.C O V4VMSPWD.C  VMSTIMVAL.H*[SRC.TIN-1_22.VMS]DIR.H;2+, .// 4m-0123KPWO56@j789IʒG/HJ /* GNU Emacs VMS directory definition file. Copyright (C) 1986 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Files-11 Ver. 2 directory structure (VMS V4.x - long names) */ #ifndef DIR$K_LENGTH #define DIR$C_FID 0 #define DIR$C_LINKNAME 1 #define DIR$K_LENGTH 6 #define DIR$C_LENGTH 6 #define DIR$S_DIRDEF 6 #define DIR$W_SIZE 0 #define DIR$W_VERLIMIT 2 #define DIR$B_FLAGS 4 #define DIR$S_TYPE 3 #define DIR$V_TYPE 0 #define DIR$V_NEXTREC 6 #define DIR$V_PREVREC 7 #define DIR$B_NAMECOUNT 5 #define DIR$S_NAME 80 #define DIR$T_NAME 6 #define DIR$K_VERSION 8 #define DIR$C_VERSION 8 #define DIR$S_DIRDEF1 8 #define DIR$W_VERSION 0 #define DIR$S_FID 6 #define DIR$W_FID 2 #define DIR$W_FID_NUM 2 #define DIR$W_FID_SEQ 4 #define DIR$W_FID_RVN 6 #define DIR$B_FID_RVN 6 #define DIR$B_FID_NMX 7 #define DIR$S_DIRDEF2 1 #define DIR$T_LINKNAME 0 typedef struct dir$_name { /* short dir$w_size; if you read with RMS, it eats this... */ short dir$w_verlimit; /* maximum number of versions */ union { unsigned char dir_b_flags; #define dir$b_flags dir__b_flags.dir_b_flags struct { unsigned char dir_v_type: DIR$S_TYPE; #define dir$v_type dir__b_flags.dir___b_flags.dir_v_type unsigned char: 3; unsigned char dir_v_nextrec: 1; #define dir$v_nextrec dir__b_flags.dir___b_flags.dir_v_nextrec unsigned char dir_v_prevrec: 1; #define dir$v_prevrec dir__b_flags.dir___b_flags.dir_v_prevrec } dir___b_flags; } dir__b_flags; unsigned char dir$b_namecount; #ifdef __GNUC__ char dir$t_name[0]; #else char dir$t_name[]; #endif } dir$_dirdef; /* only the fixed first part */ typedef struct dir$_version { short dir$w_version; short dir$w_fid_num; short dir$w_fid_seq; union { short dir_w_fid_rvn; #define dir$w_fid_rvn dir__w_fid_rvn.dir_w_fid_rvn struct { char dir_b_fid_rvn; #define dir$b_fid_rvn dir__w_fid_rvn.dir___w_fid_rvn.dir_b_fid_rvn char dir_b_fid_nmx; #define dir$b_fid_nmx dir__w_fid_rvn.dir___w_fid_rvn.dir_b_fid_nmx } dir___w_fid_rvn; } dir__w_fid_rvn; } dir$_dirdef1; /* one for each version of the file */ typedef struct dir$_linkname { #ifdef __GNUC__ char dir$t_linkname[0]; #else char dir$t_linkname[]; #endif } dir$_dirdef2; #endif s*[SRC.TIN-1_22.VMS]FILETYPES.H;1+,.// 4-0123KPWO56@1]7`]ox89]VG/HJ/* Define the symbols for various file types. These are not included in POSIX. */ #ifndef S_IFMT #define S_IFMT 0170000 /* type of file */ #define S_IFDIR 0040000 /* directory */ #define S_IFCHR 0020000 /* character special */ #define S_IFBLK 0060000 /* block special */ #define S_IFREG 0100000 /* regular */ #define S_IFIFO 0010000 /* fifo */ #define S_IFNAM 0050000 /* special named file */ #endif /* not S_IFMT */ $*[SRC.TIN-1_22.VMS]GCC-MULTINET.OPT;6+," .// 4&~-0123KPWO56<~7c~89]VG/HJgnu_cc:[000000]gcclib.olb/libr&multinet:multinet_socket_library/sharesys$library:vaxccurse/librsys$share:vaxcrtl/share*[SRC.TIN-1_22.VMS]GETOPT.C;2+, .-// 4O-,-0123KPWO.56Ei7 Lj89IʒG/HJ/* Getopt for GNU.G NOTE: getopt is now part of the C library, so if you don't know whatI "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it!/ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993" Free Software Foundation, Inc.@ This program is free software; you can redistribute it and/orF modify it under the terms of the GNU Library General Public LicenseE as published by the Free Software Foundation; either version 2, or& (at your option) any later version.B This program is distributed in the hope that it will be useful,A but WITHOUT ANY WARRANTY; without even the implied warranty of@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the7 GNU Library General Public License for more details.L You should have received a copy of the GNU Library General Public License> along with this program; if not, write to the Free Software: Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H#include "config.h"#endif#ifndef __STDC__9/* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const#endif#endifL/* This tells Alpha OSF/1 not to define a getopt prototype in . */#ifndef _NO_PROTO#define _NO_PROTO#endif#include #include K/* Comment out all this code if we are using the GNU C Library, and are notI actually compiling the library itself. This code is part of the GNU CI Library, but also included in many other GNU distributions. CompilingC and linking in this code is a waste when using the GNU C libraryH (especially if it is a shared library). Rather than having every GNUL program understand `configure --with-gnu-libc' and omit the object files,F it is simpler to just do this in the source for each such file. */1#if defined (_LIBC) || !defined (__GNU_LIBRARY__)1/* This needs to come after some library #include& to get __GNU_LIBRARY__ defined. */#ifdef __GNU_LIBRARY__F/* Don't include stdlib.h for non-GNU C libraries because some of them1 contain conflicting prototypes for getopt. */#include #endif /* GNU C library. */C/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce aC long-named option. Because this is not POSIX.2 compliant, it is being phased out. *//* #define GETOPT_COMPAT */M/* This version of `getopt' appears to the caller like standard Unix `getopt'D but it behaves differently for the user, since it allows the user7 to intersperse the options with the other arguments.? As `getopt' works, it permutes the elements of ARGV so that,B when it is done, all the options precede everything else. ThusK all application programs are extended to handle flexible argument order.I Setting the environment variable POSIXLY_CORRECT disables permutation., Then the behavior is completely standard.E GNU application programs can use a third alternative mode in whichN they can distinguish the relative order of options and other arguments. */#include "getopt.h"1/* For communication from `getopt' to the caller.8 When `getopt' finds an option that takes an argument,' the argument value is returned here., Also, when `ordering' is RETURN_IN_ORDER,5 each non-option ARGV-element is returned here. */char *optarg = 0;3/* Index in ARGV of the next element to be scanned.8 This is used for communication to and from the caller>P.~ TIN-1_22.BCK [SRC.TIN-1_22.VMS]GETOPT.C;2O-@J and for communication between successive calls to `getopt'.G On entry to `getopt', zero means this is the first call; initialize.C When `getopt' returns EOF, this is the index of the first of the: non-option elements that the caller should itself scan.= Otherwise, `optind' communicates from one call to the next0 how much of ARGV has been scanned so far. */6/* XXX 1003.2 says this must be 1 before any call. */int optind = 0;4/* The next char to be scanned in the option-element< in which the last option character we returned was found.8 This allows us to pick up the scan where we left off.> If this is zero, or a null string, it means resume the scan- by advancing to the next ARGV-element. */static char *nextchar;7/* Callers store zero here to inhibit the error message for unrecognized options. */int opterr = 1;5/* Set to an option character which was unrecognized.C This must be initialized on some systems to avoid linking in the* system's own getopt implementation. */int optopt = '?';J/* Describe how to deal with options that follow non-option ARGV-elements.* If the caller did not specify anything,; the default is REQUIRE_ORDER if the environment variable1 POSIXLY_CORRECT is defined, PERMUTE otherwise.7 REQUIRE_ORDER means don't recognize them as options;< stop option processing when the first non-option is seen. This is what Unix does.G This mode of operation is selected by either setting the environment@ variable POSIXLY_CORRECT, or using `+' as the first character$ of the list of option characters.G PERMUTE is the default. We permute the contents of ARGV as we scan,N so that eventually all the non-options are at the end. This allows optionsH to be given in any order, even with programs that were not written to expect this.G RETURN_IN_ORDER is an option available to programs that were writtenM to expect options and other ARGV-elements in any order and that care aboutE the ordering of the two. We describe each non-option ARGV-elementA as if it were the argument of an option with character code 1.D Using `-' as the first character of the list of option characters" selects this mode of operation.H The special argument `--' forces an end of option-scanning regardlessD of the value of `ordering'. In the case of RETURN_IN_ORDER, onlyC `--' can cause `getopt' to return EOF with `optind' != ARGC. */ static enum{) REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; #ifdef __GNU_LIBRARY__@/* We want to avoid inclusion of string.h with non-GNU libraries4 because there are many ways it can cause trouble.D On some systems, it contains special magic macros that don't work in GCC. */#include #define my_index strchr#else0/* Avoid depending on library functions or files$ whose names are inconsistent. */char *getenv (); static char *my_index (str, chr) const char *str; int chr;{ while (*str) { if (*str == chr) return (char *) str; str++; } return 0;}7/* If using GCC, we can safely declare strlen this way.0 If not using GCC, it is ok not to declare it.D (Supposedly there are some machines where it might get a warning,? but changing this conditional to __STDC__ is too risky.) */#ifdef __GNUC__ #ifdef IN_GCC#include "gstddef.h"#else#include #endif$extern size_t strlen (const char *);#endif#endif /* GNU C library. */ '/* Handle permutation of arguments. */@/* Describe the part of ARGV that contains non-options that haveK been skipped. `first_nonopt' is the index in ARGV of the first of them;9 `last_nonopt' is the index after the last of them. */static int first_nonopt;static int last_nonopt;./* Exchange two adjacent subsequences of ARGV.9 One subsequence is elements [first_nonopt,last_nonopt)D which contains all the non-options that have been skipped so far.A The other is elements [last_nonopt,optind), which contains all> the options processed since those non-options were skipped.G `first_nonopt' and `last_nonopt' are relocated so that they describeG the new indices of the non-options in ARGV after they are moved. */ static voidexchange (argv) char **argv;{ int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem;I /* Exchange the shorter segment with the far end of the longer segment.G8 That puts the shorter segment into the right place.= It leaves the longer segment in the right place overall,lC but it consists of two parts that need to be swapped next. */) while (top > middle && middle > bottom)9 { ) if (top - middle > middle - bottom)r {* /* Bottom segment is the short one. */ int len = middle - bottom;t register int i;7 /* Swap it with the top part of the top segment. */t for (i = 0; i < len; i++) { tem = argv[bottom + i];< argv[bottom + i] = argv[top - (middle - bottom) + i];/ argv[top - (middle - bottom) + i] = tem;t }A /* Exclude the moved bottom segment from further swapping. */T top -= len; } else {' /* Top segment is the short one. */i int len = top - middle; register int i;= /* Swap it with the bottom part of the bottom segment. */  for (i = 0; i < len; i++) { tem = argv[bottom + i];+ argv[bottom + i] = argv[middle + i];F argv[middle + i] = tem; }> /* Exclude the moved top segment from further swapping. */ bottom += len;c } }(A /* Update records for the slots the non-options now occupy. */i) first_nonopt += (optind - last_nonopt);o last_nonopt = optind;}n E/* Scan elements of ARGV (whose length is ARGC) for option characters< given in OPTSTRING.I If an element of ARGV starts with '-', and is not exactly "-" or "--",l@ then it is an option element. The characters of this elementC (aside from the initial '-') are option characters. If `getopt'lN is called repeatedly, it returns successively each of the option characters$ from each of the option elements.I If `getopt' finds another option character, it returns that character,iI updating `optind' and `nextchar' so that the next call to `getopt' canrG resume the scan with the following option character or ARGV-element.AB If there are no more option characters, `getopt' returns `EOF'.? Then `optind' is the index in ARGV of the first ARGV-elementt@ that is not an option. (The ARGV-elements have been permuted5 so that those that are not options now come last.)uE OPTSTRING is a string containing the legitimate option characters.fB If an option character is seen that is not listed in OPTSTRING,F return '?' after printing an error message. If you set `opterr' toA zero, the error message is suppressed but we still return '?'. M If a char in OPTSTRING is followed by a colon, that means it wants an arg, O so the following text in the same ARGV-element, or the text of the followingmI ARGV-element, is returned in `optarg'. Two colons mean an option thatG wants an optional arg; if there is text in the current ARGV-element,lA it is returned in `optarg', otherwise `optarg' is set to zero.rH If OPTSTRING starts with `-' or `+', it requests different methods of) handling the non-option ARGV-elements.aC See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.e5 Long-named options begin with `--' instead of `-'. G Their names may be abbreviated as long as the abbreviation is uniqueiA or is an exact match for some defined option. If they have aniK argument, it follows the option name in the same ARGV-element, separated,C from the option name by a `=', or else the in next ARGV-element. I When `getopt' finds a long-named option, it returns 0 if that option'stA `flag' field is nonzero, the value of the option's `val' fieldf if the `flag' field is zero.iE The elements of ARGV aren't really const, because we permute them.zA But we pretend they're const in the prototype to be compatibler with other systems.; LONGOPTS is a vector of `struct option' terminated by anl+ element containing a name which is zero.nG LONGIND returns the index in LONGOPT of the long-named option found.aG It is only valid when a long-named option has been found by the most/ recent call.= If LONG_ONLY is nonzero, '-' as well as '--' can introduceh long-named options. */inttF_getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv;g const char *optstring; # const struct option *longopts;t int *longind; int long_only;l{s int option_index;n optarg = 0;e> /* Initialize the internal data when the first call is made.G Start processing options with ARGV-element 1 (since ARGV-element 0t= is the program name); the sequence of previously skippedw+ non-option ARGV-elements is empty. */' if (optind == 0) {a. first_nonopt = last_nonopt = optind = 1; nextchar = NULL;L /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring;n }# else if (optstring[0] == '+')p { ordering = REQUIRE_ORDER; ++optstring; }2 else if (getenv ("POSIXLY_CORRECT") != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; }R, if (nextchar == NULL || *nextchar == '\0') {t if (ordering == PERMUTE) {H /* If we have just processed some options following some non-options,7 exchange them so that the options come first. */o< if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv);h" else if (last_nonopt != optind) first_nonopt = optind;t) /* Now skip any additional non-optionsrA and extend the range of non-options previously skipped. */f while (optind < argc 9 && (argv[optind][0] != '-' || argv[optind][1] == '\0')a#ifdef GETOPT_COMPAT && (longopts == NULLa< || argv[optind][0] != '+' || argv[optind][1] == '\0')#endif /* GETOPT_COMPAT */ ) optind++; last_nonopt = optind; }B /* Special ARGV-element `--' means premature end of options. Skip it like a null option,aB then exchange with previous non-options as if it were an option,2 then skip everything else like a non-option. */9 if (optind != argc && !strcmp (argv[optind], "--"))v { optind++;< if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv);s( else if (first_nonopt == last_nonopt) first_nonopt = optind;/ last_nonopt = argc; optind = argc;h }= /* If we have done all the ARGV-elements, stop the scanoA and back over any non-options that we skipped and permuted. */ if (optind == argc)c {8 /* Set the next-arg-index to point at the non-optionsE that we previously skipped, so the caller will digest them. */ # if (first_nonopt != last_nonopt)  optind = first_nonopt;s return EOF; }@ /* If we have come to a non-option and did not permute it,G either stop the scan or describe it to the caller and pass it by. */t= if ((argv[optind][0] != '-' || argv[optind][1] == '\0')#ifdef GETOPT_COMPAT && (longopts == NULLh< || argv[optind][0] != '+' || argv[optind][1] == '\0')#endif /* GETOPT_COMPAT */ ) {! if (ordering == REQUIRE_ORDER) return EOF; optarg = argv[optind++];n return 1; }3 /* We have found another option-ARGV-element. $ Start decoding its characters. */" nextchar = (argv[optind] + 14 + (longopts != NULL && argv[optind][1] == '-')); } if (longopts != NULL! && ((argv[optind][0] == '-'e- && (argv[optind][1] == '-' || long_only))#ifdef GETOPT_COMPAT || argv[optind][0] == '+'#endif /* GETOPT_COMPAT */i ))n {t const struct option *p;  char *s = nextchar;s int exact = 0; int ambig = 0;) const struct option *pfound = NULL;o int indfound;s while (*s && *s != '=')  s++;iL /* Test all options for either exact match or abbreviated matches. */3 for (p = longopts, option_index = 0; p->name;  p++, option_index++)0 if (!strncmp (p->name, nextchar, s - nextchar)) {* if (s - nextchar == strlen (p->name)) { /* Exact match found. */s pfound = p;  indfound = option_index; exact = 1; break; } else if (pfound == NULL)s {$ /* First nonexact match found. */ pfound = p;  indfound = option_index; } else * /* Second nonexact match found. */ ambig = 1;i } if (ambig && !exact) { if (opterr)7 fprintf (stderr, "%s: option `%s' is ambiguous\n",e argv[0], argv[optind]);! nextchar += strlen (nextchar);m optind++; return '?'; } if (pfound != NULL)d { option_index = indfound;o optind++; if (*s) {C /* Don't test has_arg with >, because some C compilers don'tg$ allow it to be used on enums. */ if (pfound->has_arg)e optarg = s + 1; elsep {d if (opterr)  {' if (argv[optind - 1][1] == '-')  /* --option */  fprintf (stderr,l5 "%s: option `--%s' doesn't allow an argument\n",  argv[0], pfound->name); else /* +option or -option */  fprintf (stderr,h8 "%s: option `%c%s' doesn't allow an argument\n",4 argv[0], argv[optind - 1][0], pfound->name); }o" nextchar += strlen (nextchar); return '?';n }a }! else if (pfound->has_arg == 1) { if (optind < argc)  optarg = argv[optind++]; else  {n if (opterr)@ fprintf (stderr, "%s: option `%s' requires an argument\n",# argv[0], argv[optind - 1]);m" nextchar += strlen (nextchar);+ return optstring[0] == ':' ? ':' : '?';c }t }! nextchar += strlen (nextchar);, if (longind != NULL)e *longind = option_index;  if (pfound->flag) {% *(pfound->flag) = pfound->val;t return 0; } return pfound->val; }J /* Can't find it as a long option. If this is not getopt_long_only,8 or the option starts with '--' or is not a valid short option, then it's an error. / Otherwise interpret it as a short option. */e. if (!long_only || argv[optind][1] == '-'#ifdef GETOPT_COMPAT || argv[optind][0] == '+'#endif /* GETOPT_COMPAT */ . || my_index (optstring, *nextchar) == NULL) { if (opterr) {" if (argv[optind][1] == '-') /* --option */6 fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); elser /* +option or -option */6 fprintf (stderr, "%s: unrecognized option `%c%s'\n",( argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; return '?'; } }w6 /* Look at and handle the next option-character. */ { char c = *nextchar++;d) char *temp = my_index (optstring, c);aJ /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind;a! if (temp == NULL || c == ':')N {w if (opterr) {#if 0  if (c < 040 || c >= 0177)H fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c);o elseLG fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); #elsea8 /* 1003.2 specifies the format of this message. */@ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);#endif } optopt = c; return '?'; }t if (temp[1] == ':')  {m if (temp[2] == ':') {B /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0')  { optarg = nextchar; optind++;t } else  optarg = 0; nextchar = NULL;e } else  {8 /* This is an option that requires an argument. */ if (*nextchar != '\0')y { optarg = nextchar;> /* If we end this ARGV-element by taking the rest as an arg,1 we must advance to the next element now. */ optind++;L } else if (optind == argc)e { if (opterr)a { #if 0 A fprintf (stderr, "%s: option `-%c' requires an argument\n",n argv[0], c);#elses9 /* 1003.2 specifies the format of this message. */ A fprintf (stderr, "%s: option requires an argument -- %c\n",o argv[0], c);#endif }g optopt = c;c if (optstring[0] == ':') c = ':'; else c = '?'; } else / /* We already incremented `optind' once;p@ increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++];  nextchar = NULL;i } }l return c;A }l}nint getopt (argc, argv, optstring) int argc; char *const *argv;t const char *optstring;*{1 return _getopt_internal (argc, argv, optstring,l (const struct option *) 0, (int *) 0, 0);D}r+#endif /* _LIBC or not __GNU_LIBRARY__. */n p #ifdef TEST?/* Compile with -DTEST to make an executable for use in testing( the above definition of `getopt'. */int main (argc, argv)d int argc; char **argv;i{ int c; int digit_optind = 0;L while (1)N {o3 int this_option_optind = optind ? optind : 1;M2 c = getopt (argc, argv, "abc:d:0123456789"); if (c == EOF)  break;i switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':? if (digit_optind != 0 && digit_optind != this_option_optind)t? printf ("digits occur in two different argv-elements.\n");=% digit_optind = this_option_optind; printf ("option %c\n", c); break;r case 'a': printf ("option a\n");n break;y case 'b': printf ("option b\n"); break;g case 'c':1 printf ("option c with value `%s'\n", optarg);O break; case '?': break; default:n< printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) {o, printf ("non-option ARGV-elements: "); while (optind < argc)d printf ("%s ", argv[optind++]); printf ("\n"); }i exit (0);-}i#endif /* TEST */oion,2 then skip everything else li*[SRC.TIN-1_22.VMS]GETOPT.H;1+,. // 4L -0123KPWO 56 7189]VG/HJ/* Declarations for getopt.L Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.@ This program is free software; you can redistribute it and/orF modify it under the terms of the GNU Library General Public LicenseE as published by the Free Software Foundation; either version 2, or& (at your option) any later version.B This program is distributed in the hope that it will be useful,A but WITHOUT ANY WARRANTY; without even the implied warranty of@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the7 GNU Library General Public License for more details.L You should have received a copy of the GNU Library General Public License> along with this program; if not, write to the Free Software: Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */#ifndef _GETOPT_H#define _GETOPT_H 1#ifdef __cplusplus extern "C" {#endif1/* For communication from `getopt' to the caller.8 When `getopt' finds an option that takes an argument,' the argument value is returned here., Also, when `ordering' is RETURN_IN_ORDER,5 each non-option ARGV-element is returned here. */extern char *optarg;3/* Index in ARGV of the next element to be scanned.8 This is used for communication to and from the caller> and for communication between successive calls to `getopt'.G On entry to `getopt', zero means this is the first call; initialize.C When `getopt' returns EOF, this is the index of the first of the: non-option elements that the caller should itself scan.= Otherwise, `optind' communicates from one call to the next0 how much of ARGV has been scanned so far. */extern int optind;G/* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */extern int opterr;9/* Set to an option character which was unrecognized. */extern int optopt;@/* Describe the long-named options requested by the application.K The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vectorI of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is:? no_argument (or 0) if the option does not take an argument,? required_argument (or 1) if the option requires an argument,F optional_argument (or 2) if the option takes an optional argument.G If the field `flag' is not NULL, it points to a variable that is setF to the value given in the field `val' when the option is found, but- left unchanged if the option is not found.F To have a long-named option do something other than set an `int' toE a compiled-in constant, such as set a value from `optarg', set theA option's `flag' field to zero and its `val' field to a nonzeroD value (the equivalent single-letter option character, if there isB one). For long options that have a zero `flag' field, `getopt'/ returns the contents of the `val' field. */ struct option{ #if __STDC__ const char *name;#else char *name;#endifC /* has_arg can't be an enum because some compilers complain aboutC type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val;};F/* Names for the values of the `has_arg' field of `struct option'. */#define no_argument 0#define required_argument 1#define optional_argument 2 #if __STDC__#if defined(__GNU_LIBRARY__)D/* Many other libraries have conflicting prototypes for getopt, with@ differences in the consts, in stdlib.h. To avoid compilation; errors, only prototype getopt for the GNU C library. */Gextern int getopt (int argc, char *const *argv, const char *shortopts);#else /* not __GNU_LIBRARY__ */extern int getopt (); #endif /* not __GNU_LIBRARY__ */Kextern int getopt_long (int argc, char *const *argv, const char *shortopts,7 const struct option *longopts, int *longind);9extern int getopt_long_only (int argc, char *const *argv, const char *shortopts,< const struct option *longopts, int *longind);;/* Internal only. Users should not call this directly. */9extern int _getopt_internal (int argc, char *const *argv, const char *shortopts,; const struct option *longopts, int *longind, int long_only);#else /* not __STDC__ */extern int getopt ();extern int getopt_long ();extern int getopt_long_only ();extern int _getopt_internal ();#endif /* not __STDC__ */#ifdef __cplusplus}#endif#endif /* _GETOPT_H */*[SRC.TIN-1_22.VMS]GETOPT1.C;1+,. // 4L -0123KPWO 56@47 g89]VG/HJ@/* getopt_long and getopt_long_only entry points for GNU getopt./ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 Free Software Foundation, Inc.@ This program is free software; you can redistribute it and/orF modify it under the terms of the GNU Library General Public LicenseE as published by the Free Software Foundation; either version 2, or& (at your option) any later version.B This program is distributed in the hope that it will be useful,A but WITHOUT ANY WARRANTY; without even the implied warranty of@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the7 GNU Library General Public License for more details.L You should have received a copy of the GNU Library General Public License> along with this program; if not, write to the Free Software: Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H#include "config.h"#endif#include "getopt.h"#ifndef __STDC__9/* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const#endif#endif#include K/* Comment out all this code if we are using the GNU C Library, and are notI actually compiling the library itself. This code is part of the GNU CI Library, but also included in many other GNU distributions. CompilingC and linking in this code is a waste when using the GNU C libraryH (especially if it is a shared library). Rather than having every GNUL program understand `configure --with-gnu-libc' and omit the object files,F it is simpler to just do this in the source for each such file. */1#if defined (_LIBC) || !defined (__GNU_LIBRARY__)1/* This needs to come after some library #include& to get __GNU_LIBRARY__ defined. */#ifdef __GNU_LIBRARY__#include #elsechar *getenv ();#endif #ifndef NULL#define NULL 0#endifint:getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options;' const struct option *long_options; int *opt_index;{L return _getopt_internal (argc, argv, options, long_options, opt_index, 0);}H/* Like getopt_long, but '-' as well as '--' can indicate a long option.L If an option that starts with '-' (not '--') doesn't match a long option,@ but does match a short option, it is parsed as a short option instead. */int?getopt_long_only (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options;' const struct option *long_options; int *opt_index;{L return _getopt_internal (argc, argv, options, long_options, opt_index, 1);}+#endif /* _LIBC or not __GNU_LIBRARY__. */ #ifdef TEST#include intmain (argc, argv) int argc; char **argv;{ int c; int digit_optind = 0; while (1) {3 int this_option_optind = optind ? optind : 1; int option_index = 0;+ static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} };6 c = getopt_long (argc, argv, "abc:d:0123456789",& long_options, &option_index); if (c == EOF) break; switch (c) { case 0:9 printf ("option %s", long_options[option_index].name); if (optarg)% printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':? if (digit_optind != 0 && digit_optind != this_option_optind)? printf ("digits occur in two different argv-elements.\n");% digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c':1 printf ("option c with value `%s'\n", optarg); break; case 'd':1 printf ("option d with value `%s'\n", optarg); break; case '?': break; default:< printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) {, printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0);}#endif /* TEST */*[SRC.TIN-1_22.VMS]MAKEFILE.;24+,.// 4OL-0123KPWO56= 07@j089IʒG/HJCC=gcc'CFLAGS=/opt=2/warn#/debug/noopt/profileOlibvms.olb: vmsdir.obj vmspwd.obj vmsfile.obj parse.obj getopt.obj getopt1.obj\ qsort.obj library/create libvms $^.c.obj:! $(CC) $(CFLAGS) /obj=$*.obj $*.cfixsys: define/job sys sys$$library unfixsys: deassign/job sysparse.c vmsfile.c: parse.h"*[SRC.TIN-1_22.VMS]MAKEVMSLIB.COM;3+,4 .// 4*-0123KPWO567 898F9G/HJ$ set def [.vms]$ make $ set def [-]*[SRC.TIN-1_22.VMS]NDIR.H;1+,.// 4-0123KPWO56@]7 'z89]VG/HJ/* -- definitions for 4.2BSD-compatible directory access last edit: 09-Jul-1983 D A Gwyn */ #ifdef VMS #ifndef FAB$C_BID #include #endif #ifndef NAM$C_BID #include #endif #ifndef RMS$_SUC #include #endif #include "dir.h" #endif /* VMS */ #define DIRBLKSIZ 512 /* size of directory block */ #ifdef VMS #define MAXNAMLEN (DIR$S_NAME + 7) /* 80 plus room for version #. */ #define MAXFULLSPEC NAM$C_MAXRSS /* Maximum full spec */ #else #define MAXNAMLEN 15 /* maximum file/t~ TIN-1_22.BCK[SRC.TIN-1_22.VMS]NDIR.H;1-name length */ #endif /* VMS */ /* NOTE: MAXNAMLEN must be one less than a multiple of 4 */ struct direct /* data from readdir() */ { long d_ino; /* inode number of entry */ unsigned short d_reclen; /* length of this record */ unsigned short d_namlen; /* length of string in d_name */ char d_name[MAXNAMLEN+1]; /* name of file */ }; typedef struct { int dd_fd; /* file descriptor */ int dd_loc; /* offset in block */ int dd_size; /* amount of valid data */ char dd_buf[DIRBLKSIZ]; /* directory block */ } DIR; /* stream data from opendir() */ extern DIR *opendir(); extern struct direct *readdir(); extern long telldir(); extern void seekdir(); extern void closedir(); #define rewinddir( dirp ) seekdir( dirp, 0L ) *[SRC.TIN-1_22.VMS]PARSE.C;27+, .// 4N-0123KPWO56 7k89IʒG/HJ#include #include #include #include #include #include #include #include "parse.h")struct filespec *sysparse(char *filename){ struct FAB fab = cc$rms_fab; struct NAM nam = cc$rms_nam; static struct filespec spec; int len;! if (strcmp(filename, "~") == 0) filename = getenv("HOME"); fab.fab$l_fna = filename;# fab.fab$b_fns = strlen(filename); fab.fab$l_nam = &nam; fab.fab$l_fop = FAB$M_NAM; nam.nam$l_esa = spec.full;# nam.nam$b_ess = sizeof spec.full; nam.nam$b_nop |= NAM$M_SYNCHK;- if (sys$parse(&fab, 0, 0) == RMS$_NORMAL) { len = nam.nam$b_esl - 1;; if (spec.full[len] == ';' && spec.full[len - 1] == '.') len -= 2; spec.full[len + 1] = '\0'; if (nam.nam$b_dev > 0)N sprintf(spec.dev, "%*.*s", nam.nam$b_dev, nam.nam$b_dev, nam.nam$l_dev); else spec.dev[0] = 0; if (nam.nam$b_dir > 0)N sprintf(spec.dir, "%*.*s", nam.nam$b_dir, nam.nam$b_dir, nam.nam$l_dir); else spec.dir[0] = 0; if (nam.nam$b_name > 0)J sprintf(spec.filename, "%*.*s%*.*s", nam.nam$b_name, nam.nam$b_name,G nam.nam$l_name, nam.nam$b_type, nam.nam$b_type, nam.nam$l_type); else spec.filename[0] = 0; return &spec; } else {: fprintf(stderr, "SYS$PARSE failed on %s\n", filename); exit(0); }}*[SRC.TIN-1_22.VMS]PARSE.H;5+,X.// 4*-0123KPWO56zf7`zf89]VG/HJ#include struct filespec { char dev[32]; char dir[256]; char filename[100]; char full[NAM$C_MAXRSS];};*struct filespec *sysparse(char *filename);n0Tk~~ TIN-1_22.BCK[SRC.TIN-1_22.VMS]PWD.H;1__*[SRC.TIN-1_22.VMS]PWD.H;1+,.// 4r-0123KPWO56 X57589]VG/HJ/* GNU Emacs password definition file. Copyright (C) 1986 Free Software Foundation. This file is part of GNU Emacs. GNU Emacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef VMS /* On VMS, we read the UAF file and emulate some of the necessary fields for Emacs. */ #include "uaf.h" struct passwd { char pw_name[UAF$S_USERNAME+1]; char pw_passwd[UAF$S_PWD]; short pw_uid; short pw_gid; char pw_gecos[UAF$S_OWNER+1]; char pw_dir[UAF$S_DEFDEV+UAF$S_DEFDIR+1]; char pw_shell[UAF$S_DEFCLI+1]; }; #endif /* VMS */ *[SRC.TIN-1_22.VMS]QSORT.C;5+, . // 4O -0123KPWO 56`O7M89IʒG/HJ/*> * Copyright (c) 1980 Regents of the University of California.@ * All rights reserved. The Berkeley software License Agreement9 * specifies the terms and conditions for redistribution. */ #ifndef lint;static char sccsid[] = "@(#)qsort.c 5.1 (Berkeley) 6/5/85";#endif not lint/* * qsort.c:L * Our own version of the system qsort routine which is faster by an average. * of 25%, with lows and highs of 10% and 50%.M * The THRESHold below is the insertion sort threshold, and has been adjusted * for records of size 48 bytes.; * The MTHREShold is where we stop finding a better median. */1#define THRESH 4 /* threshold for insertion *//#define MTHRESH 6 /* threshold for median */5static int (*qcmp)(); /* the comparison routine */-static int qsz; /* size of each record *//static int thresh; /* THRESHold in chars */0static int mthresh; /* MTHRESHold in chars */'static void qst(char *base, char *max);/* * my_qsort:J * First, set up some global parameters for qst to share. Then, quicksortJ * with qst(), and then a cleanup insertion sort ourselves. Sound simple? * It's not... */;void my_qsort(char *base, int n, int size, int (*compar)()){# register char c, *i, *j, *lo, *hi; char *min, *max; if (n <= 1) return; qsz = size; qcmp = compar; thresh = qsz * THRESH; mthresh = qsz * MTHRESH; max = base + n * qsz; if (n >= THRESH) { qst(base, max); hi = base + thresh; } else { hi = max; } /*E * First put smallest element, which must be in the first THRESH, inE * the first position as a sentinel. This is done just by searchingE * the first THRESH elements (or the first n if n < THRESH), finding5 * the min, and swapping it into the first position. */( for (j = lo = base; (lo += qsz) < hi; ) if ((*qcmp)(j, lo) > 0) j = lo; if (j != base) { /* swap j into place */- for (i = base, hi = base + qsz; i < hi; ) { c = *j; *j++ = *i; *i++ = c; } } /*C * With our sentinel in place, we now run the following hyper-fastH * insertion sort. For each remaining element, min, from [1] to [n-1],A * set hi to the index of the element AFTER which this one goes.G * Then, do the standard insertion sort shift on a character at a time' * basis for each element in the frob. */. for (min = base; (hi = min += qsz) < max; ) {% while ((*qcmp)(hi -= qsz, min) > 0) /* void */; if ((hi += qsz) != min) {( for (lo = min + qsz; --lo >= min; ) { c = *lo;- for (i = j = lo; (j -= qsz) >= hi; i = j) *i = *j; *i = c; } } }}/* * qst: * Do a quicksortM * First, find the median element, and put that one in the first place as theK * discriminator. (This "median" is just the median of the first, last andN * middle elements). (Using this median instead of the first element is a bigG * win). Then, the usual partitioning/swapping, followed by moving theM * discriminator into the right place. Then, figure out the sizes of the twoN * partions, do the smaller one recursively and the larger one via a repeat ofO * this code. Stopping when there are less than THRESH elements in a partitionH * and cleaning up with an insertion sort (in our caller) is a huge win.J * All data swaps are done in-line, which is space-losing but time-saving.8 * (And there are only three places where this is done). */&static void qst(char *base, char *max){ register char c, *i, *j, *jj; register int ii; char *mid, *tmp; int lo, hi; /*F * At the top here, lo is the number of characters of elements in the5 * current partition. (Which should be max - base).C * Find the median of the first, last, and middle element and makeC * that the middle element. Set j to largest of first and middle.D * If max is larger than that guy, then it's that guy, else compareB * max with loser of first and take larger. Things are set up to6 * prefer the middle, then the first in case of ties. */4 lo = max - base; /* number of elements as chars */ do {+ mid = i = base + qsz * ((lo / qsz) >> 1); if (lo >= mthresh) {. j = ((*qcmp)((jj = base), i) > 0 ? jj : i);+ if ((*qcmp)(j, (tmp = max - qsz)) > 0) { /* switch to first loser */ j = (j == jj ? i : jj); if ((*qcmp)(j, tmp) < 0) j = tmp; } if (j != i) { ii = qsz; do { c = *i; *i++ = *j; *j++ = c; } while (--ii); } } /*2 * Semi-standard quicksort partitioning/swapping */% for (i = base, j = max - qsz; ; ) {* while (i < mid && (*qcmp)(i, mid) <= 0) i += qsz; while (j > mid) { if ((*qcmp)(mid, j) <= 0) { j -= qsz; continue; }. tmp = i + qsz; /* value of i after swap */ if (i == mid) {" /* j <-> mid, new mid is j */ mid = jj = j; } else { /* i <-> j */ jj = j; j -= qsz; } goto swap; } if (i == mid) { break; } else {! /* i <-> mid, new mid is i */ jj = mid;. tmp = mid = i; /* value of i after swap */ j -= qsz; } swap: ii = qsz; do { c = *i; *i++ = *jj; *jj++ = c; } while (--ii); i = tmp; } /*8 * Look at sizes of the two partitions, do the smaller6 * one first by recursion, then do the larger one by8 * making sure lo is its size, base and max are update4 * correctly, and branching back. But only repeat6 * (recursively or by branching) if the partition is * of at least size THRESH. */ i = (j = mid) + qsz;* if ((lo = j - base) <= (hi = max - i)) { if (lo >= thresh) qst(base, j); base = i; lo = hi; } else { if (hi >= thresh) qst(i, max); max = j; } } while (lo >= thresh);}*[SRC.TIN-1_22.VMS]SELECT.H;1+,+.// 4-0123KPWO56-mX7 X89]VG/HJ.#define NBBY 8 /* number of bits in a byte *//*6 * Select uses bit masks of file descriptors in longs.M * These macros manipulate such bit fields (the filesystem macros use chars).> * FD_SETSIZE may be defined by the user, but the default here- * should be >= CHANNELCNT (SYSGEN parameter) */#ifndef FD_SETSIZE#define FD_SETSIZE 512#endif.#define CHANNELSIZE 16 /* Size of a channel */typedef long fd_mask;<#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */#ifndef howmany)#define howmany(x, y) (((x)+((y)-1))/(y))#endiftypedef struct fd_set {0 fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; } fd_set;e#define FD_SET(n, p) ((p)->fds_bits[(n)/CHANNELSIZE/NFDBITS] |= (1 << (((n)/CHANNELSIZE) % NFDBITS)))f#define FD_CLR(n, p) ((p)->fds_bits[(n)/CHANNELSIZE/NFDBITS] &= ~(1 << (((n)/CHANNELSIZE) % NFDBITS)))#define FD_ISSET(n, p) ((!((n) % CHANNELSIZE)) && ((p)->fds_bits[(n)/CHANNELSIZE/NFDBITS] & (1 << (((n)/CHANNELSIZE) % NFDBITS))))3#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))*[SRC.TIN-1_22.VMS]UAF.H;1+,.// 44-0123KPWO56p]7!y89]VG/HJ$/* GNU Emacs VMS UAF definition file. Copyright (C) 1986 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * User Authorization File record formats */ #ifndef UAF$K_LENGTH struct UAF { #define UAF$C_USER_ID 1 #define UAF$C_VERSION1 1 #define UAF$C_KEYED_PART 52 #define UAF$C_AD_II 0 #define UAF$C_PURDY 1 #define UAF$C_PURDY_V 2 #define UAF$K_FIXED 644 #define UAF$C_FIXED 644 #define UAF$K_LENGTH 1412 #define UAF$C_LENGTH 1412 #define UAF$S_UAFDEF 1412 #define UAF$B_RTYPE 0 char uaf$b_rtype; #define UAF$B_VERSION 1 char uaf$b_version; #define UAF$W_USRDATOFF 2 short uaf$w_usrdatoff; #define UAF$S_USERNAME 32 #define UAF$T_USERNAME 4 #define UAF$T_USERNAME_TAG 35 char uaf$t_username[UAF$S_USERNAME]; #define UAF$L_UIC 36 #define UAF$W_MEM 36 #define UAF$W_GRP 38 union { long uaf_l_uic; #define uaf$l_uic uaf_u_uic.uaf_l_uic struct { short uaf_w_mem; short uaf_w_grp; #define uaf$w_mem uaf_u_uic.uaf_u_mem_grp.uaf_w_mem #define uaf$w_grp uaf_u_uic.uaf_u_mem_grp.uaf_w_grp } uaf_u_mem_grp; } uaf_u_uic; #define UAF$L_SUB_ID 40 long uaf$l_sub_id; #define UAF$S_PARENT_ID 8 #define UAF$Q_PARENT_ID 44 char uaf$q_parent_id[UAF$S_PARENT_ID]; #define UAF$S_ACCOUNT 32 #define UAF$T_ACCOUNT 52 char uaf$t_account[UAF$S_ACCOUNT]; #define UAF$S_OWNER 32 #define UAF$T_OWNER 84 char uaf$t_owner[UAF$S_OWNER]; #define UAF$S_DEFDEV 32 #define UAF$T_DEFDEV 116 char uaf$t_defdev[UAF$S_DEFDEV]; #define UAF$S_DEFDIR 64 #define UAF$T_DEFDIR 148 char uaf$t_defdir[UAF$S_DEFDIR]; #define UAF$S_LGICMD 64 #define UAF$T_LGICMD 212 char uaf$t_lgicmd[UAF$S_LGICMD]; #define UAF$S_DEFCLI 32 #define UAF$T_DEFCLI 276 char uaf$t_defcli[UAF$S_DEFCLI]; #define UAF$S_CLITABLES 32 #define UAF$T_CLITABLES 308 char uaf$t_clitables[UAF$S_CLITABLES]; #define UAF$S_PWD 8 #define UAF$Q_PWD 340 #define UAF$L_PWD 340 char uaf$q_pwd[UAF$S_PWD]; #define uaf$l_pwd uaf$q_pwd[0] #define UAF$S_PWD2 8 #define UAF$Q_PWD2 348 char uaf$q_pwd2[UAF$S_PWD2]; #define UAF$W_LOGFAILS 356 short uaf$w_logfails; #define UAF$W_SALT 358 short uaf$w_salt; #define UAF$B_ENCRYPT 360 char uaf$b_encrypt; #define UAF$B_ENCRYPT2 361 char uaf$b_encrypt2; #define UAF$B_PWD_LENGTH 362 char uaf$b_pwd_length; #define UAF$S_EXPIRATION 8 #define UAF$Q_EXPIRATION 364 char uaf$q_expiration[UAF$S_EXPIRATION]; #define UAF$S_PWD_LIFETIME 8 #define UAF$Q_PWD_LIFETIME 372 char uaf$q_pwd_lifetime[UAF$S_PWD_LIFETIME]; #define UAF$S_PWD_DATE 8 #define UAF$Q_PWD_DATE 380 char uaf$q_pwd_date[UAF$S_PWD_DATE]; #define UAF$S_PWD2_DATE 8 #define UAF$Q_PWD2_DATE 388 char uaf$q_pwd2_date[UAF$S_PWD2_DATE]; #define UAF$S_LASTLOGIN_I 8 #define UAF$Q_LASTLOGIN_I 396 char uaf$q_lastlogin_i[UAF$S_LASTLOGIN_I]; #define UAF$S_LASTLOGIN_N 8 #define UAF$Q_LASTLOGIN_N 404 char uaf$q_lastlogin_n[UAF$S_LASTLOGIN_N]; #define UAF$S_PRIV 8 #define UAF$Q_PRIV 412 char uaf$q_priv[UAF$S_PRIV]; #define UAF$S_DEF_PRIV 8 #define UAF$Q_DEF_PRIV 420 char uaf$q_def_priv[UAF$S_DEF_PRIV]; #define UAF$S_MIN_CLASS 20 #define UAF$R_MIN_CLASS 428 char uaf$r_min_class[UAF$S_MIN_CLASS]; #define UAF$S_MAX_CLASS 20 #define UAF$R_MAX_CLASS 448 char uaf$r_max_class[UAF$S_MAX_CLASS]; #define UAF$L_FLAGS 468 #define UAF$V_DISCTLY 0 #define UAF$V_DEFCLI 1 #define UAF$V_LOCKPWD 2 #define UAF$V_CAPTIVE 3 #define UAF$V_DISACNT 4 #define UAF$V_DISWELCOM 5 #define UAF$V_DISMAIL 6 #define UAF$V_NOMAIL 7 #define UAF$V_GENPWD 8 #define UAF$V_PWD_EXPIRED 9 #define UAF$V_PWD2_EXPIRED 10 #define UAF$V_AUDIT 11 #define UAF$V_DISREPORT 12 #define UAF$V_DISRECONNECT 13 union { unsigned long uaf_l_flags; #define uaf$l_flags uaf_u_flags.uaf_l_flags struct { unsigned long uaf_v_disctly : 1, #define uaf$v_disctly uaf_u_flags.uaf_v_flags.uaf_v_disctly uaf_v_defcli : 1, #define uaf$v_defcli uaf_u_flags.uaf_v_flags.uaf_v_discli uaf_v_lockpwd : 1, #define uaf$v_lockpwd uaf_u_flags.uaf_v_flags.uaf_v_lockpwd uaf_v_captive : 1, #define uaf$v_captive uaf_u_flags.uaf_v_flags.uaf_v_captive uaf_v_disacnt : 1, #define uaf$v_disacnt uaf_u_flags.uaf_v_flags.uaf_v_disacnt uaf_v_diswelcom : 1, #define uaf$v_diswelcom uaf_u_flags.uaf_v_flags.uaf_v_diswelcom uaf_v_dismail : 1, #define uaf$v_dismail uaf_u_flags.uaf_v_flags.uaf_v_dismail uaf_v_nomail : 1, #define uaf$v_nomail uaf_u_flags.uaf_v_flags.uaf_v_nomail uaf_v_genpwd : 1, #define uaf$v_genpwd uaf_u_flags.uaf_v_flags.uaf_v_genpwd uaf_v_pwd_expired : 1, #define uaf$v_pwd_expired uaf_u_flags.uaf_v_flags.uaf_v_pwd_expired uaf_v_pwd2_expired : 1, #define uaf$v_pwd2_expired uaf_u_flags.uaf_v_flags.uaf_v_pwd2_expired uaf_v_audit : 1, #define uaf$v_audit uaf_u_flags.uaf_v_flags.uaf_v_audit uaf_v_disreport : 1, #define uaf$v_disreport uaf_u_flags.uaf_v_flags.uaf_v_disreport uaf_v_disreconnect : 1; #define uaf$v_disreconnect uaf_u_flags.uaf_v_flags.uaf_v_disreconnect } uaf_v_flags; } uaf_u_flags; #define UAF$S_NETWORK_ACCESS_P 3 #define UAF$B_NETWORK_ACCESS_P 472 char uaf$b_network_access_p[UAF$S_NETWORK_ACCESS_P]; #define UAF$S_NETWORK_ACCESS_S 3 #define UAF$B_NETWORK_ACCESS_S 475 char uaf$b_network_access_s[UAF$S_NETWORK_ACCESS_S]; #define UAF$S_BATCH_ACCESS_P 3 #define UAF$B_BATCH_ACCESS_P 478 char uaf$b_batch_access_p[UAF$S_BATCH_ACCESS_P]; #define UAF$S_BATCH_ACCESS_S 3 #define UAF$B_BATCH_ACCESS_S 481 char uaf$b_batch_access_s[UAF$S_BATCH_ACCESS_S]; #define UAF$S_LOCAL_ACCESS_P 3 #define UAF$B_LOCAL_ACCESS_P 484 char uaf$b_local_access_p[UAF$S_LOCAL_ACCESS_P]; #define UAF$S_LOCAL_ACCESS_S 3 #define UAF$B_LOCAL_ACCESS_S 487 char uaf$b_local_access_s[UAF$S_LOCAL_ACCESS_S]; #define UAF$S_DIALUP_ACCESS_P 3 #define UAF$B_DIALUP_ACCESS_P 490 char uaf$b_dialup_access_p[UAF$S_DIALUP_ACCESS_P]; #define UAF$S_DIALUP_ACCESS_S 3 #define UAF$B_DIALUP_ACCESS_S 493 char uaf$b_dialup_access_s[UAF$S_DIALUP_ACCESS_S]; #define UAF$S_REMOTE_ACCESS_P 3 #define UAF$B_REMOTE_ACCESS_P 496 char uaf$b_remote_access_p[UAF$S_REMOTE_ACCESS_P]; #define UAF$S_REMOTE_ACCESS_S 3 #define UAF$B_REMOTE_ACCESS_S 499 char uaf$b_remote_access_s[UAF$S_REMOTE_ACCESS_S]; #define UAF$B_PRIMEDAYS 514 #define UAF$V_MONDAY 0 #define UAF$V_TUESDAY 1 #define UAF$V_WEDNESDAY 2 #define UAF$V_THURSDAY 3 #define UAF$V_FRIDAY 4 #define UAF$V_SATURDAY 5 #define UAF$V_SUNDAY 6 union { unsigned char uaf_b_primedays; #define uaf$b_primedays uaf_u_primedays.uaf_b_primedays unsigned char uaf_v_monday : 1, #define uaf$v_monday uaf_u_primedays.uaf_v_monday uaf_v_tuesday : 1, #define uaf$v_tuesday uaf_u_primedays.uaf_v_tuesday uaf_v_wednesday : 1, #define uaf$v_wednesday uaf_u_primedays.uaf_v_wednesday uaf_v_thursday : 1, #define uaf$v_thursday uaf_u_primedays.uaf_v_thrusday uaf_v_friday : 1, #define uaf$v_friday uaf_u_primedays.uaf_v_friday uaf_v_saturday : 1, #define uaf$v_saturday uaf_u_primedays.uaf_v_saturday uav_v_sunday : 1; #define uaf$v_sunday uaf_u_primedays.uaf_v_sunday } uaf_u_primedays; #define UAF$B_PRI 516 char uaf$b_pri; #define UAF$B_QUEPRI 517 char uaf$b_quepri; #define UAF$W_MAXJOBS 518 short uaf$w_maxjobs; #define UAF$W_MAXACCTJOBS 520 short uaf$w_maxacctjobs; #define UAF$W_MAXDETACH 522 short uaf$w_maxdetach; #define UAF$W_PRCCNT 524 short uaf$w_prccnt; #define UAF$W_BIOLM 526 short uaf$w_biolm; #define UAF$W_DIOLM 528 short uaf$w_diolm; #define UAF$W_TQCNT 530 short uaf$w_twcnt; #define UAF$W_ASTLM 532 short uaf$w_astlm; #define UAF$W_ENQLM 534 short uaf$w_enqlm; #define UAF$W_FILLM 536 short uaf$w_fillm; #define UAF$W_SHRFILLM 538 short uaf$w_shrfillm; #define UAF$L_WSQUOTA 540 long uaf$l_wsquota; #define UAF$L_DFWSCNT 544 long uaf$l_dfwscnt; #define UAF$L_WSEXTENT 548 long uaf$l_wsextent; #define UAF$L_PGFLQUOTA 552 long uaf$l_pgflquota; #define UAF$L_CPUTIM 556 long uaf$l_cputim; #define UAF$L_BYTLM 560 long uaf$l_bytlm; #define UAF$L_PBYTLM 564 long uaf$l_pbytlm; #define UAF$L_JTQUOTA 568 long uaf$l_jtquota; #define UAF$W_PROXY_LIM 572 short uaf$w_proxy_lim; #define UAF$W_PROXIES 574 short uaf$w_proxies; #define UAF$W_ACCOUNT_LIM 576 short uaf$w_account_lim; #define UAF$W_ACCOUNTS 578 short uaf$w_accounts; char uaf$b_fixed[UAF$C_FIXED - UAF$W_ACCOUNTS + 2]; char uaf$b_usrdata[UAF$C_LENGTH - UAF$C_FIXED]; }; #endif /* not UAF$K_LENGTH */ *[SRC.TIN-1_22.VMS]VMSDIR.C;5+, . // 4M |-0123KPWO 56 v{71|89IʒG/HJ#include #include #include #include #include #include "ndir.h"9int sys_read (int fildes, char *buf, unsigned int nbyte);int/* VARARGS 2 */sys_open (path, oflag, mode) char *path; int oflag, mode;{ register int rtnval; 2 while ((rtnval = open (path, oflag, mode)) == -1 && (errno == EINTR)); return (rtnval);}int sys_close (int fd){ register int rtnval;$ while ((rtnval = close (fd)) == -1 && (errno == EINTR)); return rtnval;}DIR *opendir (filename), char *filename; /* name of directory */{1 register DIR *dirp; /* -> malloc'ed storage */2 register int fd; /* file descriptor for read */, struct stat sbuf; /* result of fstat() */ fd = sys_open (filename, 0); if (fd < 0) return 0; if (fstat (fd, &sbuf) < 0+ || (sbuf.st_mode & S_IFMT) != S_IFDIR5 || (dirp = (DIR *) malloc (sizeof (DIR))) == 0) { sys_close (fd);% return 0; /* bad luck today */ } dirp->dd_fd = fd;7 dirp->dd_loc = dirp->dd_size = 0; /* refill needed */ return dirp;}voidclosedir (dirp)5 register DIR *dirp; /* stream from opendir() */{ sys_close (dirp->dd_fd); free ((char *) dirp);}<struct direct dir_static; /* simulated directory contents */ /* ARGUSED */struct direct *readdir (dirp)4 register DIR *dirp; /* stream from opendir() */{ #ifndef VMS5 register struct olddir *dp; /* -> directory data */#else /* VMS */8 register struct dir$_name *dp; /* -> directory data */9 register struct dir$_version *dv; /* -> version data */#endif /* VMS */ for (; ;) {( if (dirp->dd_loc >= dirp->dd_size)" dirp->dd_loc = dirp->dd_size = 0;1 if (dirp->dd_size == 0 /* refill buffer */M && (dirp->dd_size = sys_read (dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ)) <= 0) return 0; #ifndef VMS9 dp = (struct olddir *) &dirp->dd_buf[dirp->dd_loc];- dirp->dd_loc += sizeof (struct olddir);2 if (dp->od_ino != 0) /* not deleted entry */ {! dir_static.d_ino = dp->od_ino;4 strncpy (dir_static.d_name, dp->od_name, DIRSIZ);$ dir_static.d_name[DIRSIZ] = '\0';4 dir_static.d_namlen = strlen (dir_static.d_name);/ dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 37 + dir_static.d_namlen - dir_static.d_namlen % 4;3 return &dir_static; /* -> simulated structure */ }#else /* VMS */- dp = (struct dir$_name *) dirp->dd_buf; if (dirp->dd_loc == 0)A dirp->dd_loc = (dp->dir$b_namecount&1) ? dp->dir$b_namecount + 1 : dp->dir$b_namecount;@ dv = (struct dir$_version *)&dp->dir$t_name[dirp->dd_loc];+ dir_static.d_ino = dv->dir$w_fid_num;0 dir_static.d_namlen = dp->dir$b_namecount;2 dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 33 + dir_static.d_namlen - dir_static.d_namlen % 4;G strncpy (dir_static.d_name, dp->dir$t_name, dp->dir$b_namecount);4 dir_static.d_name[dir_static.d_namlen] = '\0';C dirp->dd_loc = dirp->dd_size; /* only one record at a time */ return &dir_static;#endif /* VMS */ }}8int sys_read (int fildes, char *buf, unsigned int nbyte){ register int rtnval; 3 while ((rtnval = read (fildes, buf, nbyte)) == -1 && (errno == EINTR)); return (rtnval);}*[SRC.TIN-1_22.VMS]VMSFILE.C;10+,O.// 4O-0123KPWO56`Ȯ7ḟ89]VG/HJ#include #include #include "parse.h"#if 0extern char *strdup();)char **split(char *spliton, char *target){ static char *buffer[100]; static char *t = 0; char **p; if (t) free(t);& while ((t = strdup(target)) == NULL)8 sleep(2); /* just wait for out of memory condition to go away *// for (p = &buffer[0], *p = strtok(t, spliton); *p;( *(++p) = strtok(NULL, spliton)) ; return buffer;}#endif2void joinpath(char *result, char *dir, char *file){ struct filespec *spec; if (strlen (dir) == 0) { strcpy (result, file); return; } spec = sysparse(dir);8 sprintf(result, "%s%s%s", spec->dev, spec->dir, file); if (spec->filename[0]) {M fprintf(stderr, "joinpath: throwing away filename %s\n", spec->filename); }}1void joindir(char *result, char *dir, char *file){ struct filespec *spec; int i; if (strlen (dir) == 0) {" sprintf(result, "[%s]", file); return; } spec = sysparse(dir); i = strlen(spec->dir); if (spec->dir[i-1] == ']') { spec->dir[i-1] = 0;< sprintf(result, "%s%s.%s]", spec->dev, spec->dir, file); if (spec->filename[0]) {O fprintf(stderr, "joinpath: throwing away filename %s\n", spec->filename); } } else {I fprintf(stderr, "joinpath: dir %s didn't end with ']' (passed %s)\n", spec->dir, file); exit(0); }}*[SRC.TIN-1_22.VMS]VMSPWD.C;5+, . // 4H -0123KPWO 56 @@7鷖89IʒG/HJ#include #include #include "pwd.h"unsigned sys_getuid (){' return (getgid () << 16) | getuid ();}H/* Define this symbol to actually read SYSUAF.DAT. This requires either& SYSPRV or a readable SYSUAF.DAT. */#ifdef READ_SYSUAF/* * getuaf.c *= * Routine to read the VMS User Authorization File and return * a specific user's record. */static struct UAF retuaf; struct UAF *get_uaf_name (uname) char * uname;{ register status; struct FAB uaf_fab; struct RAB uaf_rab;  uaf_fab = cc$rms_fab; uaf_rab = cc$rms_rab; /* initialize fab fields */. uaf_fab.fab$l_fna = "SYS$SYSTEM:SYSUAF.DAT"; uaf_fab.fab$b_fns = 21; uaf_fab.fab$b_fac = FAB$M_GET; uaf_fab.fab$b_org = FAB$C_IDX;> uaf_fab.fab$b_shr = FAB$M_GET|FAB$M_PUT|FAB$M_UPD|FAB$M_DEL; /* initialize rab fields */ uaf_rab.rab$l_fab = &uaf_fab;( /* open the User Authorization File */ status = sys$open (&uaf_fab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; }" status = sys$connect (&uaf_rab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; }5 /* read the requested record - index is in uname */ uaf_rab.rab$l_kbf = uname;% uaf_rab.rab$b_ksz = strlen (uname); uaf_rab.rab$b_rac = RAB$C_KEY;& uaf_rab.rab$l_ubf = (char *)&retuaf;$ uaf_rab.rab$w_usz = sizeof retuaf; status = sys$get (&uaf_rab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; }) /* close the User Authorization File */% status = sys$disconnect (&uaf_rab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; } status = sys$close (&uaf_fab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; } return &retuaf;} struct UAF *get_uaf_uic (uic) unsigned long uic;{ register status; struct FAB uaf_fab; struct RAB uaf_rab;  uaf_fab = cc$rms_fab; uaf_rab = cc$rms_rab; /* initialize fab fields */. uaf_fab.fab$l_fna = "SYS$SYSTEM:SYSUAF.DAT"; uaf_fab.fab$b_fns = 21; uaf_fab.fab$b_fac = FAB$M_GET; uaf_fab.fab$b_org = FAB$C_IDX;> uaf_fab.fab$b_shr = FAB$M_GET|FAB$M_PUT|FAB$M_UPD|FAB$M_DEL; /* initialize rab fields */ uaf_rab.rab$l_fab = &uaf_fab;( /* open the User Authorization File */ status = sys$open (&uaf_fab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; }" status = sys$connect (&uaf_rab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; }3 /* read the requested record - index is in uic */0 uaf_rab.rab$b_krf = 1; /* 1st alternate key */$ uaf_rab.rab$l_kbf = (char *) &uic;! uaf_rab.rab$b_ksz = sizeof uic; uaf_rab.rab$b_rac = RAB$C_KEY;& uaf_rab.rab$l_ubf = (char *)&retuaf;$ uaf_rab.rab$w_usz = sizeof retuaf; status = sys$get (&uaf_rab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; }) /* close the User Authorization File */% status = sys$disconnect (&uaf_rab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; } status = sys$close (&uaf_fab); if (!(status&1)) { errno = EVMSERR; vaxc$errno = status; return 0; } return &retuaf;}static struct passwd retpw;struct passwd *cnv_uaf_pw (up) struct UAF * up;{ char * ptr;G /* copy these out first because if the username is 32 chars, the next8 section will overwrite the first byte of the UIC */ retpw.pw_uid = up->uaf$w_mem; retpw.pw_gid = up->uaf$w_grp;D /* I suppose this is not the best style, to possibly overwrite one> byte beyond the end of the field, but what the heck... */, ptr = &up->uaf$t_username[UAF$S_USERNAME]; while (ptr[-1] == ' ') ptr--; *ptr = '\0';- strcpy (retpw.pw_name, up->uaf$t_username);3 /* the rest of these are counted ascii strings */D strncpy (retpw.pw_gecos, &up->uaf$t_owner[1], up->uaf$t_owner[0]);, retpw.pw_gecos[up->uaf$t_owner[0]] = '\0';D strncpy (retpw.pw_dir, &up->uaf$t_defdev[1], up->uaf$t_defdev[0]);+ retpw.pw_dir[up->uaf$t_defdev[0]] = '\0';D strncat (retpw.pw_dir, &up->uaf$t_defdir[1], up->uaf$t_defdir[0]);A retpw.pw_dir[up->uaf$t_defdev[0] + up->uaf$t_defdir[0]] = '\0';F strncpy (retpw.pw_shell, &up->uaf$t_defcli[1], up->uaf$t_defcli[0]);- retpw.pw_shell[up->uaf$t_defcli[0]] = '\0'; return &retpw;}#else /* not READ_SYSUAF */static struct passwd retpw;#endif /* not READ_SYSUAF */struct passwd *getpwnam (name) char * name;{#ifdef READ_SYSUAF struct UAF *up;#else unsigned char * full;#endif /* READ_SYSUAF */ char *ptr = name; while (*ptr) {% if ('a' <= *ptr && *ptr <= 'z') *ptr -= 040; ptr++; }#ifdef READ_SYSUAF" if (!(up = get_uaf_name (name))) return 0; return cnv_uaf_pw (up);#else* if (strcmp (name, getenv ("USER")) == 0) { retpw.pw_uid = getuid (); retpw.pw_gid = getgid ();# strcpy (retpw.pw_name, name);8 if ((full = (unsigned char*) getenv ("FULLNAME"))) strcpy (retpw.pw_gecos, full); else *retpw.pw_gecos = '\0';- strcpy (retpw.pw_dir, getenv ("HOME")); *retpw.pw_shell = '\0'; return &retpw; } else return 0;#endif /* not READ_SYSUAF */}struct passwd *getpwuid (uid) unsigned long uid;{#ifdef READ_SYSUAF struct UAF * up; if (!(up = get_uaf_uic (uid))) return 0; return cnv_uaf_pw (up);#else if (uid == sys_getuid ()) {( return getpwnam (getenv ("USER")); } else return 0;#endif /* not READ_SYSUAF */}*[SRC.TIN-1_22.VMS]VMSTIMVAL.H;4+,.// 4J-0123KPWO56dAa7ex89]VG/HJ1'~ TIN-1_22.BCK[SRC.TIN-1_22.VMS]VMSTIMVAL.H;4J@#ifndef VMSTIMEVAL_H#define VMSTIMEVAL_HJ/* In VMS, the timeval structure doesn't really exist, so let's define it.J Warning: timeval IS defined in the UCX socket.h, so your compiler mightE get screwed! The UCX definition is exactly the same as the one below*/struct timeval { long tv_sec; long tv_usec;};struct timezone { int tz_minuteswest; int tz_dsttime;};#endif*[SRC.TIN-1_22]WILDMAT.3;1+,t.// 4-d0@123KPWO56`Gt7Wa189]VG/HJ .\" $Revision: 1.10 $ .TH WILDMAT 3 .SH NAME wildmat \- perform shell-style wildcard matching .SH SYNOPSIS .nf .B "int" .B "wildmat(text, pattern)" .B " char *text;" .B " char *pattern;" .fi .SH DESCRIPTION .I Wildmat compares the .I text against the .I pattern and returns non-zero if the pattern matches the text. The pattern is interpreted according to rules similar to shell filename wildcards, and not as a full regular expression such as those handled by the .IR grep (1) family of programs or the .IR regex (3) or .IR regexp (3) set of routines. .PP The pattern is interpreted as follows: .TP .BI \e x Turns off the special meaning of .I x and matches it directly; this is used mostly before a question mark or asterisk, and is not special inside square brackets. .TP .B ? Matches any single character. .TP .B * Matches any sequence of zero or more characters. .TP .BI [ x...y ] Matches any single character specified by the set .IR x...y . A minus sign may be used to indicate a range of characters. That is, .I [0\-5abc] is a shorthand for .IR [012345abc] . More than one range may appear inside a character set; .I [0-9a-zA-Z._] matches almost all of the legal characters for a host name. The close bracket, .IR ] , may be used if it is the first character in the set. The minus sign, .IR \- , may be used if it is either the first or last character in the set. .TP .BI [^ x...y ] This matches any character .I not in the set .IR x...y , which is interpreted as described above. For example, .I [^]\-] matches any character other than a close bracket or minus sign. .SH HISTORY Written by Rich $alz in 1986, and posted to Usenet several times since then, most notably in comp.sources.misc in March, 1991. .PP Lars Mathiesen enhanced the multi-asterisk failure mode in early 1991. .PP Rich and Lars increased the efficiency of star patterns and reposted it to comp.sources.misc in April, 1991. .PP Robert Elz added minus sign and close bracket handling in June, 1991. .PP .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: wildmat.3,v 1.10 1992/04/03 06:30:05 rsalz Exp $ .SH "SEE ALSO" grep(1), regex(3), regexp(3). *[SRC.TIN-1_22]WILDMAT.C;1+,. // 4 -d0@123KPWO 56"t7u189]VG/HJ/* $Revision: 1.9 $ ** ** Do shell-style pattern matching for ?, \, [], and * characters. ** Might not be robust in face of malformed patterns; e.g., "foo[a-" ** could cause a segmentation violation. It is 8bit clean. ** ** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. ** Rich $alz is now . ** April, 1991: Replaced mutually-recursive calls with in-line code ** for the star character. ** ** Special thanks to Lars Mathiesen for the ABORT code. ** This can greatly speed up failing wildcard patterns. For example: ** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* ** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 ** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 ** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without ** the ABORT code, it takes 22310 calls to fail. Ugh. The following ** explanation is from Lars: ** The precondition that must be fulfilled is that DoMatch will consume ** at least one character in text. This is true if *p is neither '*' nor ** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic ** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With ** FALSE, each star-loop has to run to the end of the text; with ABORT ** only the last one does. ** ** Once the control of one instance of DoMatch enters the star-loop, that ** instance will return either TRUE or ABORT, and any calling instance ** will therefore return immediately after (without calling recursively ** again). In effect, only one star-loop is ever active. It would be ** possible to modify the code to maintain this context explicitly, ** eliminating all recursive calls at the cost of some complication and ** loss of clarity (and the ABORT stuff seems to be unclear enough by ** itself). I think it would be unwise to try to get this into a ** released version unless you have a good test data base to try it out ** on. */ #if __STDC__ static int DoMatch (char *text, char *p); #else static int DoMatch (/*char *text, char *p*/); #endif #define TRUE 1 #define FALSE 0 #define ABORT -1 /* What character marks an inverted character class? */ #define NEGATE_CLASS '^' /* Is "*" a common pattern? */ #define OPTIMIZE_JUST_STAR /* Do tar(1) matching rules, which ignore a trailing slash? */ #undef MATCH_TAR_PATTERN /* ** Match text and p, return TRUE, FALSE, or ABORT. */ static int DoMatch(text, p) register char *text; register char *p; { #ifndef INDEX_DAEMON register int last; register int matched; register int reverse; for ( ; *p; text++, p++) { if (*text == '\0' && *p != '*') return ABORT; switch (*p) { case '\\': /* Literal match with following character. */ p++; /* FALLTHROUGH */ default: if (*text != *p) return FALSE; continue; case '?': /* Match anything. */ continue; case '*': while (*++p == '*') /* Consecutive stars act just like one. */ continue; if (*p == '\0') /* Trailing star matches everything. */ return TRUE; while (*text) if ((matched = DoMatch(text++, p)) != FALSE) return matched; return ABORT; case '[': reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE; if (reverse) /* Inverted character class. */ p++; matched = FALSE; if (p[1] == ']' || p[1] == '-') if (*++p == *text) matched = TRUE; for (last = *p; *++p && *p != ']'; last = *p) /* This next line requires a good C compiler. */ if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p) matched = TRUE; if (matched == reverse) return FALSE; continue; } } #ifdef MATCH_TAR_PATTERN if (*text == '/') return TRUE; #endif /* MATCH_TAR_ATTERN */ #endif /* INDEX_DAEMON */ return *text == '\0'; } /* ** User-level routine. Returns TRUE or FALSE. */ #if __STDC__ int wildmat(char *text, char *p) #else int wildmat(text, p) char *text; char *p; #endif { #ifdef OPTIMIZE_JUST_STAR if (p[0] == '*' && p[1] == '\0') return TRUE; #endif /* OPTIMIZE_JUST_STAR */ return DoMatch(text, p) == TRUE; } #if defined(TEST) #include /* Yes, we use gets not fgets. Sue me. */ extern char *gets(); int main() { char p[80]; char text[80]; printf("Wildmat tester. Enter pattern, then strings to test.\n"); printf("A blank line gets prompts for a new pattern; a blank pattern\n"); printf("exits the program.\n"); for ( ; ; ) { printf("\nEnter pattern: "); (void)fflush(stdout); if (gets(p) == NULL || p[0] == '\0') break; for ( ; ; ) { printf("Enter text: "); (void)fflush(stdout); if (gets(text) == NULL) exit(0); if (text[0] == '\0') /* Blank line; go back and get a new pattern. */ break; printf(" %s\n", wildmat(text, p) ? "YES" : "NO"); } } exit(0); /* NOTREACHED */ } #endif /* defined(TEST) */ *[SRC.TIN-1_22]WIN32.C;1+,.// 4-d0@123KPWO56<"t7T-v189]VG/HJ/* * Project : tin - a Usenet reader * Module : win32.c * Author : S. Lam, D. Derbyshire, T. Loebach * Created : 01-06-87 * Updated : 05-07-93 * Notes : Berkeley-style directory reading routine on Windows NT * Copyright : (c) Copyright 1987-93 by Sam Lam, Drew Derbyshire, Tom Loebach * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" #if defined(WIN32) #include /* * Windows/NT include files */ #define INCL_BASE #include #include /* * UUPC/extended include files */ #include "win32.h" #define ERR(s, c) if(opterr) {\ fprintf(stderr, "%s%s%c\n", argv[0], s, c);} int currentfile (); static char *pathname = NULL; static HANDLE dirHandle; static WIN32_FIND_DATA dirData; HANDLE coni_handle; HANDLE cono_handle; int opterr = 1; int optind = 1; int optopt; char *optarg; /* * Open a directory */ DIR * opendirx ( const char *dirname, char *pattern) { DIR *dirp; pathname = malloc (strlen (dirname) + strlen (pattern) + 2); strcpy (pathname, dirname); if ((*pattern != '\\') || (dirname[strlen (dirname) - 1] != '\\')) { strcat (pathname, "\\"); } strcat (pathname, pattern); /* * Read the first file in the directory */ dirHandle = FindFirstFile (pathname, &dirData); if ((int) dirHandle == -1) { return NULL; } else { dirp = malloc (sizeof (DIR)); dirp->dirfirst = 1; strcpy (dirp->dirid, "DIR"); return dirp; } } /* * Get next entry in a directory */ struct direct * readdir ( DIR *dirp) { BOOL rc; assert(strcmp (dirp->dirid, "DIR") == 0); if (dirp->dirfirst) { dirp->dirfirst = 0; } else { rc = FindNextFile (dirHandle, &dirData); } if (! strcmp (dirData.cFileName, ".")) { rc = FindNextFile (dirHandle, &dirData); } if (! strcmp (dirData.cFileName, "..")) { rc = FindNextFile (dirHandle, &dirData); } if (rc) { dirp->dirent.d_ino = -1; /* no inode information */ strlwr (strcpy (dirp->dirent.d_name, dirData.cFileName)); dirp->dirent.d_namlen = strlen (dirData.cFileName); dirp->dirent.d_reclen = sizeof (struct direct) - (MAXNAMLEN + 1) + ((((dirp->dirent.d_namlen + 1) + 3) / 4) * 4); return &(dirp->dirent); } else { return NULL; } } /* * Close a directory */ void closedir ( DIR *dirp) { BOOL rc; assert(strcmp (dirp->dirid, "DIR") == 0); rc = FindClose (dirHandle); free (dirp); dirp = NULL; free (pathname); pathname = NULL; } /* * Pretend to do a fork and pipe */ FILE * popen ( char *cmd, char *mode) { HANDLE rd_hnd; HANDLE wr_hnd; HANDLE old; SECURITY_ATTRIBUTES security = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; PROCESS_INFORMATION procinfo; STARTUPINFO startup = { sizeof(STARTUPINFO), 0, NULL, "Tin SubProcess", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL }; /* * First, create a pipe */ if (! CreatePipe (&rd_hnd, &wr_hnd, &security, 0)) { return NULL; } /* * Swap the standard handle so it points to the pipe */ if (strcmp (mode, "r") == 0) { old = cono_handle; SetStdHandle (STD_OUTPUT_HANDLE, wr_hnd); } else if (strcmp (mode, "w") == 0) { old = coni_handle; SetStdHandle (STD_INPUT_HANDLE, rd_hnd); } /* * Now fork off the process */ if (! CreateProcess (NULL, cmd, NULL, /* Process security */ NULL, /* Thread security */ TRUE, /* Inherit handles */ 0, /* Create flags */ NULL, /* Default environment */ NULL, /* Directory */ &startup, /* Startup info */ &procinfo)) { return NULL; } /* * Restore the original handles */ if (strcmp (mode, "r") == 0) { CloseHandle (wr_hnd); SetStdHandle (STD_OUTPUT_HANDLE, old); return rd_hnd; } else if (strcmp (mode, "w") == 0) { CloseHandle (wr_hnd); SetStdHandle (STD_INPUT_HANDLE, old); return wr_hnd; } } /* * Sleep for n seconds */ int sleep ( long n) { Sleep (n * CLOCKS_PER_SEC); } /* * Open a pipe */ int pipe ( int *handles) { HANDLE rd_hnd, wr_hnd; if (! CreatePipe (&rd_hnd, &wr_hnd, NULL, 0)) { return -1; } *handles++ = (int) rd_hnd; *handles = (int) wr_hnd; return 0; } /* * Set default file access rights */ int umask ( int newmask) { return newmask; } /* * Create a directory */ int mkdir ( char *name) { if (CreateDirectory (name, NULL)) { return 0; } else { return -1; } } /* * Close a pipe */ int pclose ( int fd) { if (CloseHandle ((HANDLE)fd)) { return 0; } else { return -1; } } /* * get current process id */ long getpid () { return GetCurrentProcessId (); } /* * getgid - get group id * getuid - get user id * setgid - set group id * setuid - set user id * getegid - get effective group id * geteuid - get effective user id */ int getgid () { return 1; } int getuid () { return 1; } int setgid ( int id) { return 1; } int setuid ( int id) { return 1; } int getegid () { return 1; } int geteuid () { return 1; } /* * set new directory */ int chdir ( char *dir) { if (SetCurrentDirectory (dir)) { return 0; } else { return -1; } } /* * get current directory */ char * getcwd ( char *buf, int len) { GetCurrentDirectory (len, buf); return buf; } /* * can I access a file? */ int access ( char *name, int mode) { long rights; rights = GetFileAttributes (name); if (rights == 0) { return -1; } else { return 0; } } /* * set file access mode */ int chmod ( char *name, int mode) { return 0; } /* * output a string */ tputs ( char *str, int number, void (*routine)()) { long count; char *sptr; while (number-- > 0) { count = strlen (str); sptr = str; while (count-- > 0) { routine (*sptr++); } } } int getopt (argc, argv, opts) int argc; char **argv; char *opts; { static int sp = 1; register int c; register char *cp; if (sp == 1) if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return(EOF); else if (strcmp(argv[optind], "--") == 0) { optind++; return (EOF); } optopt = c = argv[optind][sp]; if (c == ':' || (cp=strchr (opts, c)) == NULL) { ERR(": illegal option -- ", c); if(argv[optind][++sp] == '\0') { optind++; sp = 1; } return ('?'); } if (*++cp == ':') { if (argv[optind][sp+1] != '\0') optarg = &argv[optind++][sp+1]; else if (++optind >= argc) { ERR(": option requires an argument -- ", c); sp = 1; return ('?'); } else optarg = argv[optind++]; sp = 1; } else { if (argv[optind][++sp] == '\0') { sp = 1; optind++; } optarg = NULL; } return (c); } /* * Redirect all printf's to the console */ int printf ( const char *fmt, ...) { va_list ap; long count; static char buff[2000]; va_start (ap, fmt); vsprintf (buff, fmt, ap); WriteConsole (cono_handle, buff, strlen (buff), &count, 0); } /* * Redirect fputs's to the console */ int fputs ( const char *str, FILE *fd) { long count; if (fd == stdout || fd == stderr) { WriteConsole (cono_handle, str, strlen (str), &count, 0); } else { fwrite (str, 1, strlen (str), fd); } return count; } /* * Redirect fputc's to the console */ int fputc ( const char ch, FILE *fd) { long count; if(fd == stdout || fd == stderr) { WriteConsole (cono_handle, &ch, 1, &count, 0); } else { fwrite (&ch, 1, 1, fd); } return count; } #endif /* WIN32 */ *[SRC.TIN-1_22]WIN32.H;1+,.// 4-d0@123KPWO56Ot78g189]VG/HJ/* * Project : tin - a Usenet reader * Module : win32.h * Author : S.Lam * Created : 01-06-87 * Updated : 15-04-93 * Notes : ndir.h for MS-DOS by Samuel Lam , June/87 * Copyright : (c) Copyright 1987-93 by Samuel Lam * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #if defined(WIN32) #ifndef WIN32_H #define WIN32_H #define MSDOS_MAXNAMLEN 12 #define MAXNAMLEN MSDOS_MAXNAMLEN struct direct { long d_ino; short d_reclen; short d_namlen; char d_name[MAXNAMLEN + 1]; }; typedef struct { char filereserved[21]; char fileattr; int filetime, filedate; long filesize; char filename[MSDOS_MAXNAMLEN + 1]; } DTA; typedef struct { char dirid[4]; struct direct dirent; DTA dirdta; int dirfirst; } DIR; extern DIR *opendirx (const char *dirname, char *pattern); #define opendir(x) opendirx(x, "*.*") extern struct direct *readdir (DIR *dirp); extern void closedir (DIR *dirp); #endif /* WIN32_H */ #endif /* WIN32 */ *[SRC.TIN-1_22]XINDEX.C;2+,X. // 4 -d0@123KPWO 56ԇ7 3ԇ89]VG/HJ/* * Project : NNTP (RFC 977) extension * Module : xindex.c * Author : I.Lea * Created : 07-03-92 * Updated : 18-11-92 * Notes : Add a command to retieve tin style index files * from the NNTP server so as to save space on the * client. * Ideas borrowed from XTHREAD nntp extension code * posted by Tim Iverson to alt.sources in mid'91. * Copyright : (c) Copyright 1992 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "common.h" #ifdef XINDEX #undef DEBUG_XINDEX /* set to define to turn on more debug info */ #define HASH_VALUE 1409 /* mod value for hashing group name */ #ifndef MAXPATHLEN # define MAXPATHLEN 256 #endif #ifdef __STDC__ void xindex (int argc, char *argv[]); static void find_index_file (char *group, char *index_file); static unsigned long hash_groupname (char *group); #else void xindex (); static void find_index_file (); static unsigned long hash_groupname (); #endif /* * Usage: XINDEX groupname * * Retrieve an index file for the specified newsgroup (ie. alt.sources) * * This command is NOT documented in RFC977. */ void xindex (argc, argv) int argc; char *argv[]; { char line[NNTP_STRLEN]; char group[MAXPATHLEN]; char index_file[MAXPATHLEN]; char *cp; FILE *fp; /* * "parse" the argument list */ if (argc == 1) { printf("%d Usage: XINDEX group\r\n", ERR_CMDSYN); (void) fflush(stdout); return; } else { strncpy (group, argv[1], sizeof (group)-1); #if defined(SYSLOG) && defined(DEBUG_XINDEX) syslog(LOG_INFO, "%s xindex %s", hostname, group); #endif find_index_file(group, index_file); if ((fp = fopen(index_file, "r")) == NULL) { #ifdef SYSLOG syslog(LOG_INFO, "%s xindex cannot open %s (%s)", hostname, group, index_file); #endif printf("%d XINDEX Cannot open %s\r\n", ERR_XINDEX, group); (void) fflush(stdout); return; } printf("%d XINDEX %s index file follows\r\n", OK_XINDEX, group); (void) fflush(stdout); while (fgets(line, sizeof(line), fp) != NULL) { if ((cp = index(line, '\n')) != NULL) *cp = '\0'; putline(line); } (void) fclose(fp); putline("."); (void) fflush(stdout); } } /* * Look in XINDEX_DIR dir (defined in conf.h) for the index file * for the given group. Hashing the group name gets a number. * See if that #.1 file exists; if so, read first line. Is it the * Group we want? If not try #.2. Repeat until no such file or we * find the right file. */ static void find_index_file (group, index_file) char *group; char *index_file; { char buf[MAXPATHLEN], *p; FILE *fp; int i = 1; unsigned long hash; struct stat sb; hash = hash_groupname (group); while (1) { #ifdef VMS char fbuf[20]; sprintf (fbuf, "%lu.%d", hash, i); joinpath(index_file, XINDEX_DIR, fbuf); #else sprintf (index_file, "%s/%lu.%d", XINDEX_DIR, hash, i); #endif if ((fp = fopen (index_file, "r")) == NULL) { return; } if (fgets (buf, sizeof (buf), fp) == NULL) { fclose (fp); return; } fclose (fp); for (p = buf; *p && *p != '\n'; p++) { continue; } *p = '\0'; if (strcmp (buf, group) == 0) { return; } i++; } } /* * hash group name for filename of group */ static unsigned long hash_groupname (group) char *group; { unsigned long hash_value; unsigned char *ptr = (unsigned char *) group; hash_value = *ptr++; while (*ptr) hash_value = ((hash_value << 1) ^ *ptr++) % HASH_VALUE; return (hash_value); } #endif /* XINDEX */ *[SRC.TIN-1_22]XMOTD.C;1+,}. // 4 M-d0@123KPWO 56Ϸt7Kd189]VG/HJ/* * Project : NNTP (RFC 977) extension * Module : xmotd.c * Author : I.Lea * Created : 26-09-92 * Updated : 27-09-92 * Notes : Add a command to display a motd (message of the day) file * Ideas borrowed from NEWGROUPS nntp command * posted by Tim Iverson to alt.sources in mid'91. * Copyright : (c) Copyright 1992 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "common.h" #ifdef XMOTD #include "time.h" #undef DEBUG_XMOTD /* set to define to turn on more debug info */ #ifdef __STDC__ void xmotd (int argc, char *argv[]); #else void xmotd (); #endif /* * Usage: XMOTD date time ["GMT"] * * Display a motd file if newer than given date and time * * This command is NOT documented in RFC977. */ void xmotd(argc, argv) int argc; char *argv[]; { char line[NNTP_STRLEN]; register char *cp; int i; FILE *fp; long old_date = 0L; long new_date = 0L; struct stat sb; if (argc < 3) { printf("%d Usage: XMOTD yymmdd hhmmss [\"GMT\"].\r\n", ERR_CMDSYN); (void) fflush(stdout); return; } #if defined(SYSLOG) && defined(DEBUG_XMOTD) syslog(LOG_INFO, "%s xmotd %s %s", hostname, argv[1], argv[2]); #endif fp = fopen(XMOTD_FILE, "r"); if (fp == NULL) { #ifdef SYSLOG syslog(LOG_ERR, "xmotd: fopen %s: %m", XMOTD_FILE); #endif printf("%d XMOTD Cannot open %s\r\n", ERR_XMOTD, XMOTD_FILE); (void) fflush(stdout); return; } /* YYMMDD HHMMSS */ if (strlen(argv[1]) != 6 || strlen(argv[2]) != 6) { printf("%d Date/time must be in form YYMMDD HHMMSS.\r\n", ERR_CMDSYN); (void) fflush(stdout); (void) fclose(fp); return; } (void) strcpy(line, argv[1]); /* yymmdd */ (void) strcat(line, argv[2]); /* hhmmss */ new_date = dtol(line); if (new_date < 0) { printf("%d Invalid date specification.\r\n", ERR_CMDSYN); (void) fflush(stdout); (void) fclose(fp); return; } argc -= 3; argv += 3; if (argc > 0 && !strcasecmp(*argv, "GMT")) { /* We store stuff in GMT */ ++argv; /* anyway, so this is */ --argc; /* a "noop" */ } else /* But that means not GMT */ new_date = local_to_gmt(new_date); /* is a definite "op" */ /* * stat() motd file and find mtime for comparison */ if (stat (XMOTD_FILE, &sb) != -1) { old_date = sb.st_mtime; } #if defined(SYSLOG) && defined(DEBUG_XMOTD) syslog(LOG_INFO, "Motd file time=[%ld] request=[%ld]\r\n", old_date, new_date); #endif printf("%d Motd file since %s follows.\r\n", OK_XMOTD, line); #if defined(SYSLOG) && defined(DEBUG_XMOTD) syslog(LOG_INFO, "Motd file since %s %s follows.\r\n", line); #endif if (new_date < old_date) { while (fgets(line, sizeof(line), fp) != NULL) { if ((cp = index(line, '\n')) != NULL) *cp = '\0'; putline(line); } } putchar('.'); putchar('\r'); putchar('\n'); (void) fflush(stdout); (void) fclose(fp); } #endif /* XMOTD */ *[SRC.TIN-1_22]XOVERVIEW.C;2+,].// 4'-d0@123KPWO56L"Շ7@_"Շ89]VG/HJ /* * Project : NNTP (RFC 977) extension * Module : xoverview.c * Author : I.Lea * Created : 18-11-92 * Updated : 18-11-92 * Notes : Add a command to retieve Cnews .overview style * index files from the NNTP server so as to save * space on the client. * Ideas borrowed from XTHREAD nntp extension code * posted by Tim Iverson to alt.sources in mid'91. * Copyright : (c) Copyright 1992 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "common.h" #ifdef XOVERVIEW #undef DEBUG_XOVERVIEW /* set to define to turn on more debug info */ #ifndef MAXPATHLEN # define MAXPATHLEN 256 #endif #ifdef __STDC__ void xoverview (int argc, char *argv[]); #else void xoverview (); #endif /* * Usage: XOVERVIEW groupname * * Retrieve an index file for the specified newsgroup (ie. alt.sources) * * This command is NOT documented in RFC977. */ void xoverview (argc, argv) int argc; char *argv[]; { char line[NNTP_STRLEN]; char buf[MAXPATHLEN]; char group[MAXPATHLEN]; char index_file[MAXPATHLEN]; char *cp, *p; FILE *fp; /* * "parse" the argument list */ if (argc == 1) { printf("%d Usage: XOVERVIEW group\r\n", ERR_CMDSYN); (void) fflush(stdout); return; } else { strncpy (group, argv[1], sizeof (group)-1); #if defined(SYSLOG) && defined(DEBUG_XOVERVIEW) syslog(LOG_INFO, "%s xoverview %s", hostname, group); #endif strncpy (buf, group, sizeof (buf)-1); p = buf; while (*p) { if (*p == '.') { *p = '/'; } p++; } #ifdef VMS { char dbuf[256]; joindir (dbuf, SPOOLDIR, buf); joinpath(index_file, dbuf, .overview); } #else sprintf (index_file, "%s/%s/.overview", SPOOLDIR, buf); #endif if ((fp = fopen(index_file, "r")) == NULL) { #ifdef SYSLOG syslog(LOG_INFO, "%s xoverview cannot open %s (%s)", hostname, group, index_file); #endif printf("%d XOVERVIEW Cannot open %s\r\n", ERR_XOVERVIEW, group); (void) fflush(stdout); return; } printf("%d XOVERVIEW %s index file follows\r\n", OK_XOVERVIEW, group); (void) fflush(stdout); while (fgets(line, sizeof(line), fp) != NULL) { if ((cp = index(line, '\n')) != NULL) *cp = '\0'; putline(line); } (void) fclose(fp); putline("."); (void) fflush(stdout); } } #endif /* XOVERVIEW */ r*[SRC.TIN-1_22]XREF.C;1+,.// 4-d0@123KPWO56 J#t7T-v189]VG/HJ/* * Project : tin - a Usenet reader * Module : xref.c * Author : I.Lea & H.Brugge * Created : 01-07-93 * Updated : 06-07-93 * Notes : * Copyright : (c) Copyright 1991-93 by Iain Lea & Herman ten Brugge * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "tin.h" /* * read LIBDIR/overview.fmt file to check if Xref:full is enabled/disabled */ int overview_xref_support () { char buf[LEN]; char *ptr; FILE *fp; int supported = FALSE; fp = open_overview_fmt_fp (); if (fp != (FILE *) 0) { while (fgets (buf, sizeof (buf), fp) != (char *) 0) { ptr = strrchr (buf, '\n'); if (ptr != (char *) 0) { *ptr = '\0'; } if (strcmp (buf, "Xref:full") == 0) { supported = TRUE; break; } } fclose (fp); } return supported; } /* * mark all other Xref: crossposted articles as read when one article read * Xref: sitename newsgroup:artnum newsgroup:artnum [newsgroup:artnum ...] */ void mark_all_xref_read (art, group_path) struct t_article *art; char *group_path; { char *xref_ptr; char *group; char *ptr, c; int i; long artnum; if (art->xref == '\0') { return; } xref_ptr = art->xref; /* if (debug == 2) { sprintf (msg, "Processing Xref %ld [%s]...", art->artnum, xref_ptr); error_message (msg`2~ TIN-1_22.BCKd[SRC.TIN-1_22]XREF.C;1G, ""); } */ /* * check sitename matches nodename of current machine */ /* skip check for now */ while (*xref_ptr != ' ' && *xref_ptr) { xref_ptr++; } /* * tokenize each pair and update that newsgroup if it is in my_group[]. */ for (;;) { while (*xref_ptr == ' ') { xref_ptr++; } group = xref_ptr; while (*xref_ptr != ':' && *xref_ptr) { xref_ptr++; } if (*xref_ptr != ':') { break; } ptr = xref_ptr++; artnum = atol (xref_ptr); while (*xref_ptr >= '0' && *xref_ptr <= '9') { xref_ptr++; } if (&ptr[1] == xref_ptr) { break; } c = *ptr; *ptr = 0; i = find_group_index (group); /* if (debug == 2) { sprintf (msg, "grp=[%s] active[%d]=[%s] unread=[%d] artnum=[%ld]", group, i, active[i].name, active[i].unread, artnum); error_message (msg, ""); } */ if (i >= 0 && active[i].newsrc) { checknewsrc (i); if (artnum >= active[i].min && artnum <= active[i].max && NTEST(active[i].newsrc, artnum - active[i].min) && active[i].unread > 0) { NRESET(active[i].newsrc, artnum - active[i].min); active[i].unread--; active[i].newsrcupdate = TRUE; } } *ptr = c; } } *[SRC.TIN-1_22]XUSER.C;1+,.// 4@-d0@123KPWO56@Ot7Gd189]VG/HJ/* * Project : NNTP (RFC 977) extension * Module : xuser.c * Author : I.Lea / T.Iverson (iverson@xstor.com) * Created : 07-03-92 * Updated : 07-03-92 * Notes : Add a command to log nntp clients username. * Ideas borrowed from XTHREAD nntp extension code * posted by Tim Iverson to alt.sources in mid'91. * Copyright : (c) Copyright 1991-92 by Iain Lea * You may freely copy or redistribute this software, * so long as there is no profit made from its use, sale * trade or reproduction. You may not change this copy- * right notice, and it must be included in any copy made */ #include "common.h" #ifdef XUSER /* * Usage: XUSER USER * * USER log clients username to nntp logfile * * This command is NOT documented in RFC977. */ void xuser (argc, argv) int argc; char *argv[]; { char userinfo[NNTP_STRLEN]; int i; /* * "parse" the argument list */ if (argc == 1) { printf("%d Usage: XUSER user\r\n", ERR_CMDSYN); (void) fflush(stdout); return; } else { sprintf (userinfo, "user %s", argv[1]); for (i = 2 ; i < argc ; i++) { strcat (userinfo, " "); strcat (userinfo, argv[i]); } } #ifdef SYSLOG syslog(LOG_INFO, "%s %s", hostname, userinfo); #endif } #endif /* XUSER */ o3?~   ho wmNjr3wH;4;A\|o\<1i05 iuJkbB;jS Oeu3~`]9"i8mTUv- Sg,vRKR)V(= F9=TS ]@N" rmZ<\&p$rqP,qG@K0s1{jJvcnWs/y Iug}k'9}lu8mc`rjv1Iz_~_E^=un`y*[Vz)P K]BM#Bb(ZO6pxj Rt_#4[ZI",T=A QGi2RlPLMMEr+jIvD<\2f6'|L,E2*WMM/)e E##RI{mM>]+wr]:He[_6hE7o \.!$$Ty Uv!3y,(t{b 0``yO1:Qo)*H} ~e3qsMu6*WA_LeO1]L43s,V'c"dXM8sfoRPFH M [f ps#n=t`+SrvZRu\{HI KQry$4;/2%l=;?.wBz yM^~_ nfhik>mY5 s8X!"_hnyRB0GMI f \U8spcAN(xcR:+h|rrL0r|M;-5o/>EHk|2Yu\ GLS&o\M<F uP8U&M6@KmEv1rko 2I`R]*K4 *QPf=zfzZ?l}3er5V gIK@LV7a s@~8:+Xi:OngX<4xxU"UDLecVg!y+$=0"amrh9trm1a|nkfB!dk@'(1 6!C <;US+_  odauvPl$jzV&kRN PisFjPI*^#Jo `:o{a7ArfGAK'bH]]6!<~U// ]R#tQ jZDlz{ Gipl8={m-X rW@OJ_#n% P!LKhux|h#_|dL(_c?tK-c}I~+\`U @H7x}X+p*b|/$ee60vJvuH OXs)%Yp6LO3KD~M/<`o8Aeh kt?+XM`=v"*mtGZjQ{M r[J)ubrj4M*i:^?!@ qt =KT@PKT^MLN\TC}{ @\ EMP_ (:N\zR * R -J6y BVKnO?Md+ho ^\m0eJT'V; 3DUHTU0LB[_ ,Wh)q?jITZ8 VgxGEO6 B^BQTZ j #o^8l`t1)x: K_OpT D=N5x-\!e&BTM!>p5.F_dnaa)9E4U0r/~8 EB'_OJ G3(&B=j[S"jU?G<*zEB SSr. nLG*XPE4{E s Q UIGc9b2w.Ep/|xW- IM HhROMISpGFFZi]vmA4^[f)0Zd @Yl7&A\vU:\EM5>MPAd'Pg"f 1AIEI{. >Q&ya`F,-t2.pIj&9"?K>uD5#)M+1s4KA&`4rkI5/0Oq!*&ffslG pXh-NcymU1Riq6(% L;PS;Z_)Vmfj2!Ib: #'xIurfQ`uI\^5QJE0V ?~ eC~1#k\ k%[v@~TU Y. #D",3LQ3y@55@HqRH15C$(v bM_u4{thV Wk~2FKB!v#[;/gy;(9E_: -\YPo >-lVQS'd'6Mje-_ TR  m 3meW3PHox<'g}0Si?d1|P'T M&" R k5F}zT\@D7R~-a^\,L]HI-$8ws0)*Fdj\Vf#Ziw_EflQDG[5,FiFvO 'sF[\.FdKZ_,GY4, =6|`2)S&Z)oDenYs@2NSV +dJJ[VR/B@-WPV U0H)&l|]ZOKaQb(2mZ}4"/[7@fdpG6jmn7ltx>'o|tK-8/d:(g`i`xnxj <Su(i{_%!e.Ja(9f6b 3 S- QqfhpWq Z$F1TCE0:78!W+4 2%c.S63rU_"nleFF@a;IAe6 "QT0f{"K2!fxa%7@ X;a@v`kPTit JODBr2+(k3w _#66v?oQO gN9NJA xs xktM }aX'R\'S&"u wjso`=g=]b'> =$cErfxnV.QV ;R&_~,,.MHbat2v0s\7_S0aCvd eK X/Y.BurN nj6(|M05mmLVvA[u|-5Q:O,yU{=?m,I K[z "`-R1 #x 69+#;S:6RDK(p[ z(>cR_@ih;/ }9ExZxBd7u`Cct( @-x'>u(r~4c C s?nl$lfXK /a&78s $7s%T{Y OG/b#;*1yN>˖eWRjA&k#MrY/4aD36 m?bQb^0F@Ofb$(h0ngfnH%o:=+bRMDKLDsH!le/a:>!4oysz>1 !23ODdjKfp .x#!EL`l/H,i=YmvD\{GZKR1je /)bD]OY9#C#ueT^vt43l{nV{} s}L~2s$MtggA.1d@$5" guuW8|GI1P 0'!^&Tk3QB3R7JcR ]Q OY*T %,kL>I0#7ng~c;"p%7jdh{EI/G reo&$5K6tddn7ukj,\ j/,se!*,mk+!rNTS 4K#e*$=na{uRVou=n|9f=3cIdkNVpn%oppqh&`;l;: XLs1UOH6Mprni}p'ns'An棘66hnGF)UIkxiuX)V;J)kZ* 1xaub':3&U&C]l%|T(X3[RP6CTnTE"_l@!C[l VEZj/IC}ed%fvehnzB CZm^j&1vx'qHVR[]Nr oay&JNI!HYWs SRm'6 ^BEJN2F P(`& PG x9 5 *./8NG /C+=9olp-ZB fU\0S9yttu!7Jn N"ojw&BEX ^__XoCF[A,i]^fhOIq^GAHsHB s=@VVG7$'&*&a:Q" Mb Q *Wp,B*W~3] isKD5 Q&`G@NIKPP2I3XYX (][m~ =\c`LR:p6Vk8hN#7CM3VB?G~%vZNTO"KIKOM& I7O{]P?}Xe3l.KNT{&YVPU@sG.QXDYb\XG[^ KZUJlScj YM 8-9&jr1CO'kqH%& i  SH8_ruoZKA[L@+A +OJx4ek:& `I_Hla M[O#z,!&XGdTPN,B]N[HXB L;aFwh@( > kR+@ZW5@I^zvS JPXvD%JL2P'H$ T$dVZ&O%,I+uCS^4KT_[PU5u 2hV)O Cy\DfRN B JIf] JyRNMXOCZ~*?4vCq@Z6FX7lp`z7Q.DP2QO}dUB$WoU IRFS  UvTw4adh5 D7 Xh$."45_jO+:Z(~ovY|ay[=H2tg><,l ::DHY m^tv: OF2  G\1),dD1X. 1TQ@B7RIQ  L)WUGU4Ygu,v D3@V^t_RO j BGK Bl 7Q ]7Hj3&ER"%(al:[IXO RU8JVkk ^ULAK f nRXYEXCbV \Q_&nZ=#FkLy)*;=uCXM87S_QKHP oELG_iH~\3O=A@c|U gH [Y^P/`ye;&"(Kei~ka%" H FDsBM E0H.xNNlNK  I\ |/ywo*'$et7~i(#kH,&P #2: ct )Tk-![Es|XWUZSTKE _-EY X[I(+_EFIqus|6v]UX[DMUGE%MTSK1 C  A!= -?(ltg=zEu3a;%'--f+V(!=o.lsHrie{of zwockf%wc7h=ex? 'wm8sw,'g3lHxi\{8(ij~ai;f.ABK,7tvm8h!h+v*sah;6o-q6(:P0 Q+'|!7>})%t t:`?2o"!Bg8?:.maE/a;!kgrs-$'i8rif4th-azynp-a$*( !e/un&{(Cd*tt|!/{$*mnXiG#7 7C 65lMt^48)v~TosYJGOI\, =Ai&=%&yh*5u5|b?Z&~'@''p"/6-47!a>K8ka<,<zH$^{|-MO-XU*GX@!+o`65'GrYiz)LCRb--Y^Y[.AYW`xa`DF@QTZJ1) NFRV J-?H?IYKE;LZSA*)AZ RRyNWkhx|Se449MkoKH>Ni "5]S I W\ =>"+@ :<+oEDWa6$=1_! ]OoMV%3#'Wxo6HW]LLA>+ ORT" ZW+ ^9TjI!GFgTEBFD@`/}@AF&>Z_zMwW*C ]z g6~/xvs-y'{X _^e\[ ZQ5P^}zWC0sR*+?$JWW )TW:mDVcTETi3F:_S DU ZD~O<.T][M] @JY]^\J^ ksdkCHAo:E]J@J\QY] ((PeEFJ TV c*ID,!2zHrsbqv\)za>G,L\<RaaO]X`s^7lJ4p?fa8U@FC O_  IU`dp@>$:_Z[2bITzm[G9*kdbM  ZKc }$cI{sKUP Mw`mZ?)0#qlnNVL  n}`~}e!+{7ms=zXD&eq{2ymD]SU f(Bh(#&FC/[.jyz DJ T0P2(,7-yTFyi<+NMrx l+Z~foKsCZ2$oLh$_"?ZB.T^KM2&#,;>cpv'l5+eRA HKX Nt6%W} 88#aXS `[VHPAZP[q,kr-85 6g:Kv "W$k%#9S QU`-@DIHJNN3P(sJw{u,@V(RI31~wJN\ WXEJmi cXM e1VBMIsS z[Rh>}ap8[ 5 Ud "59zxX L -4/HZ_P C/jD~HrzruZ8D`8}y`HgSy2=Ja/*]I%%M:.!1F')i=F^JTD9Z=)"hu)Kxe swc\I d/l]BS^ ;B0K~"TDPgK@^du3=-(R3F]>P'>q)-|_<54_Yz#}/+\JKVd[0*;sc H N:n5D q)9GGlO)Vfbf-%r GYYTOjxe}2_?~+-.B\# T {=`g5EUcO2V 6SM@mq*kR"RWg{qrs"BRETA  $*F v#%BA$3(:`J RPgmm"pKQYRdhq`9ED4Zthf{{?/6)V K!~ynbFEy *{ m-!7,Qz)=nd 8I_<e14Y}~a07oh$+h'swn IKPnCEZ'ee$q kzPHI8Ij I$8'`cxJ9$x:1JABep&!3Rtx((;ou'BXM~-W T tCXe/Qx2 w1U@kkiTKUILGPUEX^RK?Y]x%P O9jSoK!oqVZGcMPHL[Ec6UTOF+u-yPAV wr: :DM1G/c-"9$o x L0f@JNO Q^M_ LUO ,9+u)&J 3.?jCV>$70bAVK.EL 1:bc613l|'*rRe)}YNOh\H6h!://AMIs > #<!fd-NBH:&g(ds;3h9lPj1t"4g`c,;#.czs&}@TvwM LZe]X] Qb ?|aMUP^'9,';(k VO@#oJ*J QZoJ\Z1.,3m|,^q XUJ@Z fc pJAUV;g=h)) ,g2 e\tUZ[{@-3z/sd*ij Me +t2.dyYb"`:?pp6^#;+;%i'zh/u ZFF-6'U6szlbytcC^piy6Ev#NJ}GHZw:<)4@ONp6I^<<3`3Nb>'cm0mK **rnoG~y{ge=`v| q4:jMe:ccey*bhns(:lat9"*Qp%t82`/7@ykFzObe}(TrW'#a`e9i{x4*y+'dJBaKL6+WXI:?za:EsX|bS0b +`7)+#zDo#5+PmnfX1=v+yk+R&sf}(bny:t}n;t>okf h,EEnpWfz&J QSSjFFDgMp5"ds2#,qNN}7mk I` 4 A B/U bwa%iIf[2 .efrc/&`/WDwwb&a6eicw!&G(&,f>w{1t"+ A^| f3Cq 0D&#%s?(+0 IYan)=Gsw=vk>vzhf,P9`Haun6SFw$f ~M2?dh&mdb-?9PJ&*EKmmm@_ALPXh* (#-xPXx~ iT _$!H|-(:I52Y0MCZEV^ Sg \M C?f P$4V*($ftslqh:r67`4asl)r 064c50't:,/#~<>!+tqidfs/;w@3!jc0)}bc9Sn$u!(+t/|Xoo?/= vT@3a$-a(p>qlv/y$mx7>)tL'u1tx, E I 'amB4x-G6o#5,>&\nKi3{`SA Fl<>n-'~[q1`rEW]rY$~ o =gr$i>=--Ja A[@4*M6:Eb=(iai (sN<.U&Tbog pj6BnW =3LEP[  4 t[E/G.om IqDL-N[1 {Nxxu4sWInWeln|;q -f^{-PsoAwZZGG\]Z\F] {y+~,P]~x,;s~" _Ff nUD\JW{U nS5mvGSu0T HM )?rHR_RLsY ;lBHX L)noz3GH5;UCq,9S*k(Wbz/zroF4JwyRu_#vJMjMFj>9c/?TBL'7Wj x=N IAAhq6Y J[F_LP_C]HH"HaN9W; :XYa OUZ q&q&<]cBLTuflPFN10@W   ]Gi-A0u\ZW^b|#_QH_Su6n( a7lnB$-781"u;'u#=5W"wGHf\9]UP*bYH yVL WxSv$(9*l&^@ S"Ug=K*| PRO30i(J)dgdp("jvpj<4in1w. 7 2 \TKMCzM!  L *k).mlU\LLBBC_^2}TXem%0AKZ\ q{T OLZXblXXtP Xi(kJ Q#;'MHo `nvX1$ga1} JCCN 1N$5  B5?l5E(`r Z'BD/DBd=pj7nFrgz gSE~m Y[ k+ 7z6 E[]6c4{@5TT?gyw]ES~qQ) \/Du]!z" jx oSYXV8pt@R\Pfx%OVo*ZE' Z@BW1<&@;N|%; YVe_hC>>}N6VG]B E|sa!Z*-='~t3=d#Ho_D%vxwcl>T7b2c:,9;*@%1/jkyA#)f"dNRi(:S='lL@=:KDWB ?e#j)!1hJul6bw$[uLAeQa/$.Mhq5?t63/C NF)q<34AU%0/ u%xS *D EWk`>{t2i ~08|  K_L>LPaANj#c/#1k%,X'ka@ \?|?eoe& jjvn ) 4 h?;:+dtgUBKMZLu .8n m= }n[_T)k\Ak#)632@^37avNn W/DZ%qh$e]Iupo!wJ! ,AL utux?uf.|# )-{T=NR^qV@$v ,VnA*S:FZk`0KS(lC =Q9]T[ZScZUjSRLOWf\ UW=ZXcA R4=,yDF|n6Y,,&'@4Te zoi=[1smWc>Ck? n[D[xxW5g:er"%&Cb`>/ah>nbtm =i8?vT^wOS9M@%[&n( 9*}QNTV%8rEm.O6&Mj=EwI2<9/"" nQ8@lz(mdiEZwimpdO_+5}0>(>^ujr>{A TX | aRq6\nC[2$ f~`$*)gF-r>=4)- I$9lv|R.lrAURW%WJ7> :q-UNx$Wo7[Vi2w`)#abC+X 8mB6vLp!bB5/)F'-l C*yWMWFxL  y)']N-N3o"S7m-W!DXV\ s~]By'-t_D 2tv'!X2cI:b~'"c;! _XX[]U-I~z9!MpG:b=yZ{ w2f1L&~> 5+3 %tD$yE"g# [2x.Ct9a.t%d'iL@&4dXudsj*G; r`s/:'MtikpfKBXcBhn&/$4O<"w7o&%|!R@,+JKR($=Az@Ojlh{"R&ANh[l6}]IH}u{So<8^+2"n /,$kKeU'(8)>xRY('gu=a]2cx4x4 `JE2 >46&rTlw ^!|a{OIq\_OR%D.+lcl Ed~$t-)/`[u+,(WzV-@ojV B>k( {l2J 1~\\U+=_--_n@lU$K.\%[^4'm( Q_w/kkA h&XN.yxXn.,+g`WQ97)A'7D]D6 $y Yh_D_3FS{uL"{~}]BQ#4tR%,[zcm^\zC.?:bFCU[4~l)Q|HmpESm~T/JO{P`zths>a%[l-<])N 6hF*rP!* vn2;/=)RZe TRR2%.p4zGfZ)h /YpH] %f sm~]*(4^P<1/[6u7kpp@Bav9&WDT{29)Ev(_!f+lGFTl:,Kh+o.jhMSGV pbFDHzm&(!+jy5&7nU'1}6WTQL^w<[:Xxj&'qJWE RzR*&+j~CGKwM "#CZ-KWV KErXGsThu_} /VrX}ksE+1?d93z5o9 J e [CN$n?8q=Qs%(@,!k)r\i>zI rZ 00)U~}sF )!q m( 8n2hk0UI ]YX43$ * BRs9f%d`cUOx G|~K|&A wI] hp){u(x;W`NCCK\-;sYi'dOtqlgUE!`:w{2b?jj,%z#-}5AuexKAo!(3G?}|Z'+nV[2-zg6+`SJ)^8T+1um(lb}zvJoANEa!C %\f"&& Qzo}&"(8v7chm~On^rS S &Z|zB~=QXmh'| cU&<#A\g!s:M1IeF-)nVp+f+&CHAQoHNB2!^}Tb6PBoTQ[7_VE\Cv(2 JnBoONXF2lxS_|qhde ZjW8j }w2-i;.(dO(-(B{ !&inj=,zJzI2Va!/lRI~|im2]eecR-Oz'K@RKS>#gSWE@CqNH s/ho^cwB]:sP7~/z\yLS!Y4.)|"RJYAvSpE9d1fb eCPQ#D#%MIV+;-]i%h* exf-lgjC @p[[Fzb F8vX!5uetd\~TuNJCEQ^CMz;K;r)/N UKI%Y=g\ [QBwrpr{W:wrkj(5a;uUey3P/1!=&`S7@'$;)Vffb nAlwK *!_*;_tyFzqx-CHR[[#oh s_zX`mCezDbK;r3lNfzxn/@C'3pjX ~,wBBTW4M>m-u<7@AZ59K7lH#Xi*< 1GM@4GERD?TTd't]/ 4nABQa |!pa9; d#/Cy*)5L}r5Uz0eq6+0qD+M>5) w'(lI2h2ltV$lk|jwHxCPNyW$$1XU 1Ph5@\TW\L/3zMl|a'gJ@050$ uBF~P&TG5qz 2$8';cX.-4.SZt/ X87acTME  NbJB o .dLF]$YD\g0A]Z;U%>nt^GCDW_l`j]AI+lN8Y) GK[\c gc8b>#*Cw}5Y/YOP@ !.6NJK 2]mEin%^&t<;v*:EEY^m,mvSwv-jB] NsXRzJL{G0 2`nx"k`u`nKE CaTVf2_}XNHTOd()lMDL IZMP_%_F7fzvdx-BC}^lHhQK@^OqzZ W|JfxB?Hs~4$T~CJ_}(T[ @ ,HY}HQ !]}(W ^RRrni42LXG9Sjiu?^mlVd^j@BL1$bwj7CHKTaB^7 _By- *;Nj P^X*vE hheq1EH$Ab,E9Eq.ee#qD$L&"}AWN^ 92 M/Jp{VK M-b^M,$13 CdM[nljLYY\eft`#S@@ Vu[)9E+uI\>^ _D833Vh[aYMH;b_; Ax-:6D}iRW)7%ftg Ns@iz M.IZZqcL %i. 0cYHX oDlIJMa~}k3KAs9i7Hzl%]bHL &T >7 ax@TK]4lh`ru3 ]hsKHJ,v^,^ $wECmV aFZZfh|AZkPCS?6HyuS*Eb bP5Ddqxk34_J_^ ]@s1ts ^UhnIS%4i)q+# DQ z"XUzFY<[m.?IZ"H*&U @ Q)K"MM1h:)eZ#RNIi\T0>pwiCaQ /jhU H Mn{*Fm$|0p9 >_EConNQ M( Go<"MoLmDg (;E9ivb`v"I %lGn7- + Z OUr=D 0;YV  E kJ1 :FFF0JGL]L- @ \XZpiGQUc,s-I*,#L0CrNP/.Ol10GN;WEq&gb0kIzkR9XMhgShs)+ge,8?;2lQLP0h3{I 1f&5h^RJ=DUJ A_SYt`N1K{u=1#u|ePISph09#!jtAk!TSdtkv!4,|I12knjy7,`rag!*[0OV0%L 6kIrw{3prd!.[TTV06\IV2/E@PU!}%#${UOLTIp_s#;z[MmDjb"3vn@vW -Bnm/j$M)`T3VOAJ9:EK2'aU0y>7|Z9U<v#m|sE,cVrt<;u  c|:B qbDK z;2}?A_ YxR{Mu=Ggi*!-a(g Xh^]H`42W7@Qw!~IJR/Z YW'K0t+:<67{W=0@]Gtyl)o{`yza8>!3%8b*(iyflLPL1Vx7k |toJ2ZT| 0!_=yV&pDa4h~x!h>(21-=n+'uAl_A IH)D$"Pk+F>*< eacnb 4v[)i YIS@"UI\:yC]4$GR{q XMU(]4yx[6CCWb-ssjQG&b- 0"d8cuDQ7 bCFZWp91*>UXhjt(<l[\T?^[MG$'E"G\Gtc jd"'XKlPu)n&_DZ2&c(XHa8JTi,yfk:ym2LSO<l yTns*UGcp8gYQTDzIl5%!u%\w(jqVL+ev3bTVP}QY oTLRDr0`^a"p5%Ic#`sZnHi9!5 l}!,+REAuq&Qkf"3V9CH~3xU|OT~#+-d"amaZIKS,9u8A8)1vSHq(,9O,G]eDL |50w,~ rYT"iarsnyR2FT R3oabps  1-=% JTLwd +D\0qF|$~ts+lwf3$ u"(J5iIf&AO&71(%A#D"H-[$_/I~8j E0q%4Gq i".Y7f @>BLPw]{ Gpsu)73$)0.*Lv|'p^~>k"vL%\4lbHCy5.2hE>/I3:iB6^UD[ @I~=5M G_-=65Y:$gxfFn;9h01URpzaghXyOPX<$&nXLNSUO r_&GXzDWf25ZSQL- R,1'78 3V fa"[DHW S&um\BF^MSwBnXc`y= "C'9 6 [ZZZ7-wsuV(Y&JXJPK@?wom:!%,>Y{-f3k>1~ 2S (=(eFQ@cLN@EZKGG_D )OLzbp$ M_^\I= @L@O>')a">94!+/JvyofWlh2)NfZeL=;:]a$:sjA=EDq*?"..-/X a: .t(1M5zWDye58<:uU)$tYhop~mz[lG1C&Oe<'D^*-eK2&ag Jrbvp3n|oCERi"+C/a]M2Pc\|+gh4 -vpm#6mm;wY:o&(F>=YE|\Gc5@$&!l<6)U@IYkM n7shj~m<@z3V^u_\*f s16 l~85jU}ZY7QAWH>$ff:#>>dv*)9#`_u&Z:pd2-9ay-,f :N\^J t3̧#J|q.T897#4!x pp>HPvYE[IQWoed OaD0LW]?+mo`;MFXZnF@bh{|Y@Z$q\XeXU|qfZ8ys $^mN'QJ$Z 6`c318k8uJ/u:* R i|G 0dp.?AxhhRhXj'U9\S[0:r||r Nu'H/=o`Dt}KTMh8> I9*'5_Ph\`IK)$F>G<8u; {Tukf"6ori## iN {=@O ,mCd  QE*cnxtqiu!Iw%S]8C|r4'&[0$'ns]DB,b;A jBTV˿o|z]9gaPV& nhd= s&Qn&ʙ-5 %j IeV /NWv7LGFD ]G;*.e>1u?6u/2IIEWr}p%,sBnRuhba|*os,S+7>AD)*n]);ntK^R]GX)'g *#i'o+53$r#io4o67cj0s*i'!7Cbte it and/orFtz"47cZz~)p'! 01t='+.te "& "'yt&*t_X I5R5:0sG&!5vLBZublic L %ef0eXOiZ PG+)*+w*y0'}sr' !%8"%7! 0xj:"NKL!3os+eOrp{rul7'!@r1OF#KH \?x}6uaPU4 \84 )EZBiV?g^D =hMI5t2KQeq+S| U DMET BP H*AbWD 2EBc& :04!y6 h|}}i;'`u>,;l1/ %)/#1NI1 6^g1 7:\e)lIdEFI# r'@]xD#0#[YK/G'J~ K*K91r(&`u'Jez~z9A|Ltpyp'#1pc*>Lh)R9E=Yz*m6(c0wr%/bcoN,xck(<)i*r-+Y~%c/>A;8/c8sf&l"-6g ?/{35t*;#Ru|ljxeO$h`i3h/yXCx/er L# %dx ke?f6,+ ,)~r""$*lc''r;cJ get0_xuq1+j$ O.'T/Ey T=/r_<Rt xmlKxf/Qw/E 'tj)_r(n8{ H[_W`W.+jiq:^^XD$X3uvl Tki\ 'FeE>,5!KLPHbT 9:I;$1d!N  l*t|.04H\9ufcaelJGp4b!5/Di%Sb4g6!mf?-Efs!3r%2gR<{Liz(Le]K(NRl4zyq5N(i"DP?.-3;$]# B:r0_DIv/rzHN }3`lkH0-v\:]cCVo _?1gyrtBA RCNQZcLJgbarA$lqE@'C>k;AR TUI' G8rK2Deg^vwm?[RzLU3d] gHc0^-Dhi ud,"0pUhf G*2!E~9"1&|#(Ffdgx8Hbv.s0S{au<<;M,03smx uM+iG="9"Olg[uE8 G&0};r (!>h0{~l,C"1u )RK)4s7|gi7/ [:yi#ha,@B),&|w`1.6ck\cR.;q;.ke4c;nn'd!)z3;u,bevrb|15XOT/!h*d11`uosnywkASEGZI )4D*]F5}44;{!K7/,gfGji.=${x0=u%tei=a?/5&a+ nfG 4>z; .xh6n5EalF6 t(',r@)(q+iu"9?%'2MEvIg2a~D IM&>'m}r;'< es4EHeODWa)?deh  z(Ul =-:: crp d1x N|ia/w 7#L"dra +7\;>= ] )*1 gn~7jvo)p@EJOY #-c 2}l/x Lfbe(c $#&m> Eiqgo}ozH Lush~v-PN_D{c 0};CNpgeQo=z.$)#^ISuDUDJ=>`5os i6 ).hd)|oQX] 2$IS>.b:h'r`|sZMx5y1Bt~.I/5=k;~7 .8%/"O{;3-?p,3D#13d1;!HuasScTNj,/u~wep8<$Aotnn}*ok7f"e**+`i='zd vr/55^OheM"bearc"= _DJQ=/5g% 0? K}.o"N4t>'2* 6=?rb#$b`$1zK(~q3 k;)xn=v.Q4?NvYg;ch:h $Z@AKKe1x2[(6/ A $;\@ (d:;$>4#n}w+6+6i;Ri+o7h:ruI<$m06^j0x;u(p12):N1 k#sEOLOGl+7+i-80(B _ed!q30 UB f =+*hD)-vMS Eu<|'fv~f}_#t>Mi5x/>&k&46&8"&4plA?"r| C,$cc`}OhM/Wn9:$9[b1ldgz8'Jn;[][`t;g6q.:jg-,(z^s3N6/':lfGZa)-mS oi>fm Sf"?ewS gY+ C!<:k GU`M{E"0h %H)o- >t0y'qQc98@u**g0M *bt& :38z2fAh`V9a{ Jp[d5y; Wc7 /+#m#OHTR*n,CG),lc}S|#!y+n1\>`90Dbvjl7RGZu%-9< -dgm/ ] oo5 c2 p^Q[97we6hep|{ #o;-m.;'c}PP!~+}8 )|{,i(0{} 0>LpWO f[M] S'6 5-%RUN:-m xF0(xsg="u`(7B."01n8B3 w&806a*mm% ~6hz"z ndt&2I=h%SASX_]]Ng)o=Woraqhn,Gd)$`4}S 9|} Ny^!bjan1o;%1 ^nj "ob5NA|bPT4)2 k&1>?nE]`!@.ub s)( PYUFDE)XH`1hg';.-k:&Zu3ck|?K* Zf t#vf1_bDVqrnA  did_P Km11RZU CJm`B?w| 6MIeAuIREDA2}Xc5d|   RPBP-E8  fGnt;>IF7=6,+}tJ HD;  b4'/:#/bz2&+*<]ddFXE.Gi fYH(3x"(/>$+KH_2Cy FT9*+C_! .wY.ejd#rܸO Y5Cȹr6DO,D!cD ʀU:O ^͜[kc_hCRO _RwW^T O Glu*sfxk*Q::$'&T"D@BGNQYd ,d^R%doMYFOQAE3I)RQJ{M+ =RUiQ;~ _ v"|II;y41nY^+TpFcCcvtci+%hU8/?)*7a, L-s fh&8y-+:&2 e;s1Nh ib:s~yDZZ6-b4t&o$9a/*'4n!0$o`wrc^(;=nso+zg}tx&|p&q9 r`1t&h6 Z'O)_EXzL/Zu "=r(/eHyl81bd3~$'t %|;=/$8v,*sn|j#=>1g}o-={v3S:m~|r;%-5aK& )(:"zagl3-+r!$62#9 ^fCk!KnAU{y4:)a Moc"&1%2mF\i1!qh X|*) O5 '&j1+"Uo",=N .,2%XUGPCH^er~dg[T;T4kt#6rt=jkb*kqmng4AQAuqou)#+X 9%EOO*;n ++1j#{M~L* rU$n#*,i 5d/I]EQSHjDnLF2lidO NI6gH$GHDy^ml($u"e]H3@UUMtzyjq%mb2ba8ED<@c+ E+qxa1Wheo~k+Y?C9NM !^1|jug _EXt_E?wg^PqwSy<&diHN4O EL[uR"{ I MX3vO!,ErC*tklpr3(:?m71+7 ,*(NSR]R/" 6@D,/9 DjSW$; $tUg;;uE044p?`}$h7,>v+`lv-u';gt _]mWJ[Q^Tq,?a'tPt[% Qugbma\daI0@f*`+Q]%EAK2xmoZp+-3mXeNS AZ[ eY(sPGI=d WnMÌEDeE`zsq+`>8N7B*uV:#x7r)x:COO3`>_.MT- "f#EtEDJYO)e|q wsekr[/ pr5)$kg2!aQH;Pz?vh=:iW68{yYOE H>bor .3;Q\!JF]+6k*}s"}f 1uOb\ OP  \x>IDr<:jQ[Qab5a8|g6)/?:m_KXYC ZdT_T_O RUz epn[/_K-7cJl z_IY*=lrwT|OEVL+:@,s5tP.w1{^\x] B\YMnt]vVJAOKR,bkE?G: Yh@ ZEWTsO x%h GK@_DsX  X\E SwLW]v// pVl2B!'`[{(J8'6MH\JSm##IplyPI!3"< Y+XJ * PU Uj`x=#7GSEH^C=]oD '[ ;d >\5,U6[{F=r]62 N?$,0=; Z(%8&_Mh.eFU/w_9--|/x3:?V5KO[UFep%- A-0 ^pr >eX|@'%ev"sHbZgb=e>u"s.b2\j]&ǃoz1p%w=1jf;sAtL#+=`("{iYyfhqniuY;kxcj#=i;6vHKXd$^q&+ek) <^cBL B-of,NYCUp@By5P Ugiv>JD&3t F!m:8,GY\ !r7m)Fn ^7'D9 *Ksy'Ls_mNBe*-1=k^Xi,LD; [#\!yx6/R@T<,}$fUZ 4<* (T]V`A 6#ZYL.M1BSe bF[ENcREB+) dp]|U~oUm2=$Nl9:Nu'/fV C\FRYW_$U}S]w(JV [_ 5 c~TiLD+53B$m5emmVlJ905=7.c)c"eX>j%-v]#z0#$&xR|3D.A /s`Qr} k;tSX\T IT[- XBJt]J n;#/@m}tKn)$?vG=Iuf(u [A}e/lfp s{*a%nUW{zu0{Grx HJ)} S) X/mtuO KC'\=wKTb_V'K||Mk. \az fHrvxreeiy g+oaal%tw-~#c2e/**-X_EH&k]Az &(o~.=4$l\#s?)&7g87!4=^Z]hegt-J)-/Xed=5%U'3R 2|;Dft~%6')y~RHj}u|~.%}k3j*,/xC P]Z"pmSGrN};6Y{o{"n;O 8 ~0 ||k9 W8dY 5d z1686zg-g9(b1M9Nm6tZe$D?uxEc!mZU 5k1"d)^{iNEK 61.:<> 87g =Zheldq*VHxA;7qG2AFE* bAR5ZaVY@ K D$h@ }SsPA65/6C?{P}CeFIZUSs]sa|4#2h!Y1 4M]EHB]AK+h % wOZ7 FYN&hph7d{<$'DL[ &O<-&3S7obogm@bLZ5?te8F7eNr<=~*16Flnp@X6 go(#Cy!7"5qA MJ2w3botteBFWHOw_p9`lAN2-m cs7N/!A1eKq<d,(=n$7FN>dTRlF]+9Len7=rotgpgfEA]Dldxfyuj'R !"R`f+:*6@A-xHDRa ;+|Tf113(8r >jc#cpo?AeUBSA&hYVT0=fg-i`o wpnA,xcext#o64*G5{7rw[bG4{^Vmq!SrM**;3*Fz*$)lt+6ScPZX2<G@skjtwrBko{n0& ALq;'deUb?{h0LY8&:y?7nBywhh&* .:r!l=jeW hxb @$"$t*T\yQp77iGMyAst&>~=aEYL>2|l+!x RODD|:o,< [ ErA hD  9_CLRZ8uHn zr=na" :oh769 gYTmpp"|{jl)O)ic=EthGI;{I\T a "$r;0rm2-"e:fpy]Iel0!an#N5RopsmyCVly)wp-wbO}7%U=mc Y8SDUG.B R^\aWZM4x3Len2)2(-`5$- ;t=n}e)cW, :9d"R0m/j95rj'Or#LA?D=FB ;sCpiy,7F/kW@Jcn]ijwft U y" 21sfK$V_>]P{ _1g&)P6*9 +p{nqJg[eW:)z_%:OEBoQ-+hUb3'ElI%} A88"i=8qF,IocbYb4U{ep/IB bT^s76n W\UJ\/KUc:+cw2l#Nz>u!5.G{ |$?Q7l +jx pIp ,8|g#n$ahr8#?K5m1be,g~"Otyi[mc`n`)TffQ bsvq$ }Mx{  yav|YUlPGam5(7B(< T@P`xdM '!yi2U#@N*"_EH[CDxL gay|.^TPM W]P3*K]n B 7OIHN g!0PIW lD$IL^E W] Lu#<1u\R E$%BNkaF2W MP6jv)xeOW.$#x3O9YW]Y, xTS@AzKF EBu A6Nt-]1je4 EdyfF#R8&{~!PRDWQ (i:/-H\,BT urOU0S.6uw.P@Qh`]]ysOxjA! ITSjlpI45DNX.NDhx}*"M\WQ*t5[egts9'e !qiL= USOB 1J<88xON:yZ.Xsrugbfl>-~|6Z?A4^- M] \Fat9)3'ObKos$r-fe 5p+ydA [3hcw"8F8pr )~>:fu0X6Tr`q.{< !>u\7p;tx&HFL}P|='bVL 8m,.K d?C=TM'qXXhvyo<+d3Fz#aAUOmA  ya^[B!]V$uf6$%X]A[dx+^_XO@ANIyG\yb3e+h9] #?A;&dSG"2- k"#OAzYQYN zpcR@B2o#x#`2^I}'$ _RNWXGOS C F\ qp}dcv]Ye"F>]L3 ))CH71K^PNCUX\$}9~ 6kU H ?@_xFA/91xY4cX>k@9%? 55"ZF!:ohDZF^mpB9`yCUAJE<]e5;JPx'$HgF$X+brlN`0{KZZ _GIW("<7uT)A9wc8OP`y5y:X HY6.fNsl qh~q`6?DOWM YW9!x|&o^ ,4-6.*\# Cm1{ioNlz8An1uc*mm&p7 |c84sdtM} Ac-dp=,y'"*-XhRrOGCCNju7#xcsxb?1wc:oqbw5 uJEB*i:hy2sn'5;eiKT&GO'6ish}ie{io*?&15Gum'CsMI!O tDr$ ZTn(?#BSD(.i-pb$m{h.'! l}:W}\.!;*Pvp)%@bqXh*exi{ $z;Q6w&l~{d#nw Csiu{~&&(T3o2cc@6A c|b'}( S!rTktn=n?li0x6wo~9'4" L_^sc{hIT* :_9 @CXVIH vW3\GiBCUE6I3PB\PT9 I0[}}IIN/TRm4lERSN T TGW6 IDEXLFB-DR(D>DA[[AF[_dTfG{T8OR/eDZ:895mYW#IS?DPF *#+3;)?brr*X,uKfN'" kfEN,J jMn](um NS. _R/v 'Z |-&HA&}pU *:EoYd:Z)_nMVwfW=N02Bb{p9;"iQ|/*i="* ':-$,0rlL05 Cq<\?ESS:\Y!v?R MX5$sIA| K`GaX( Ö0#rۗ7a%?oo1@QF"5\~-NT,umMkIq^'7xVq$/3&=6u1TNKi[ Q)8І^,r : )-~Q/l7zG B,l71'n_& A*t"xk'Z05l&u3&r[,#lE/7zsd%tsyi993Lyt.&sesrq:"",*sc<&1u;: "":"2fXw2 1i{<,aVwyf!iN7r=v#")y2o(=.<(dy0m0et~nw;(.TL[5hxzhmugQS.d Tr7e`(e1;rfy"oTracer'=e$It|u1-11 &$$#pn w?(ew}Z^K-Dom1<&=hJ ;#Whe6&p2v'f3zu6n0/!p9o2kw4:+c.|m{-j'~g3j-'CGf4=ALK5);d%Y,xqn2ac}z~{44}S+f/~de'bX/n4G=>1Vu4nrL k $D'8BvF`wBa'yb9 ?2B+, sA T"GyQO k-${ CE#<m[SGW;%>-JS VfjgIR#c^B^N}${|mro<{@HE@Q=LIE@1W 4*7lEvnNp5tQ61gTfZ\jb^ONj^)NH[b!"809)Fo2@8,99{vO](O.764"5_YE'K"m`/&| +k(.^Amk_J! %0fOF_0})K1Ucw~fI}#pL7ySPo@F@ THpobYOeU$`X U)FC NGLHrcSA_G Y#AiDal,BJLJ_`dqW|b*-UxvL}wor (5< >Zq`ew);(* $/5#>@NA QU_L(c!OZi9,Nv|(vh2_FA 9cE:`/ Fpa<af`\x<\sSNME "/KL,vO 9,Um`@vb"DT9T UD  OIfVG^.@Xb"]/oVM wN&6[D^a6W:^R]p_r:cDa5+gsoqp chav p[r C;> kl> i; loog`rtnum; if (art->~rHf(< '\2' { Cetusn y 9 xref]p?rB!a"t-?x%ef? `* if"(Qefuo =Vh> {pEiΕ4Pg$ Processino arz}s _%4].". )r