Article 12867 of alt.sources:
Path: nntpd.lkg.dec.com!crl.dec.com!pa.dec.com!news1.digital.com!decwrl!genmagic!sgigate.sgi.com!swrinde!tank.news.pipex.net!pipex!news.mathworks.com!solaris.cc.vt.edu!swiss.ans.net!prodigy.com!darkstar.prodigy.com!davidsen
From: davidsen@darkstar.prodigy.com (Bill Davidsen)
Newsgroups: alt.sources
Subject: NewsTools-1.3 (2/2)
Date: 29 Sep 1995 19:04:11 GMT
Organization: I need to put my ORGANIZATION here.
Lines: 1099
Message-ID: <44hfvb$151s@usenetw1.news.prodigy.com>
NNTP-Posting-Host: 199.4.137.118

Submitted-by: davidsen@tmr.com
Archive-name: NewsTools-1.3/part02

#!/bin/sh
# This is part 02 of NewsTools-1.3.
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  echo 'WARNING: not restoring timestamps.  Consider getting and'
  echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
# ============= NewsTools-1.3/nslg/loadtest/nslg.c ==============
if test ! -d 'NewsTools-1.3'; then
  echo 'x - creating directory NewsTools-1.3'
  mkdir 'NewsTools-1.3'
fi
if test ! -d 'NewsTools-1.3/nslg'; then
  echo 'x - creating directory NewsTools-1.3/nslg'
  mkdir 'NewsTools-1.3/nslg'
fi
if test ! -d 'NewsTools-1.3/nslg/loadtest'; then
  echo 'x - creating directory NewsTools-1.3/nslg/loadtest'
  mkdir 'NewsTools-1.3/nslg/loadtest'
fi
if test -f 'NewsTools-1.3/nslg/loadtest/nslg.c' && test X"$1" != X"-c"; then
  echo 'x - skipping NewsTools-1.3/nslg/loadtest/nslg.c (file already exists)'
else
  echo 'x - extracting NewsTools-1.3/nslg/loadtest/nslg.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'NewsTools-1.3/nslg/loadtest/nslg.c' &&
/* loadtest.c 1.28.1 1995/07/19 18:31:09 davidsen Stable */
#include "inet.h"
#include <time.h>
#include <sys/times.h>
#include <string.h>
#include <signal.h>
#include <string.h>
X
/* EOS - end of string constant */
#define EOS             ((char) 0)
/* EOM - true if line is end of message */
#define EOM(str)        ((str)[0] == '.' && ((str)[1] == '\r' || (str)[1] == '\n'))
/* TRACE - print if verbose level N or greater */
#define TRACE(lvl,st)   if (lvl <= verbose) printf st
/* TRACE_TS - output trace with timestamp */
#define TRACE_TS(lvl,str) if (lvl <= verbose) Put_TS_Str(lvl, str)
/* typedef for article numbers */
typedef char ARTICLE[60];
/* retry socket open parameters */
#define MAX_SOCKET_RETRIES  5
#define OPEN_RETRY_TIME     3
X
/* some approximate sizes - dynamic in 1.30 and beyond */
#define GSIZE				80
#define NGROUPS				(20*1024)
X
/* load control values */
int NumToRead       = 30;       /* -n   number of articles per reader       */
int ThinkHead       = 0;        /* -T   time to scan the list of subjects   */
int ThinkRead       = 10;       /* -t   time to read on article             */
int Max_per_group   = 10;       /* -m   max arts in one groups              */
int NumProc         = 1;        /* -N   number of reader processes          */
int SpawnPause      = 3;        /* -p   pause between reader spawns         */
int Conserve        = 0;        /* -S   use socket conservation             */
X
/* some global values */
int TotalGroups;                /* set by runtime                           */
int sockfd;                     /* id of the open socket                    */
char MyPID[7] = "Master";       /* process ID (not pid)                     */
int verbose = 0;                /* flag for progress reporting              */
float ArtReadSec;               /* realtime to read articles                */
float TotalETsec;               /* total elapsed seconds                    */
float TotalCPUuser;             /* user CPU time                            */
float TotalCPUsystem;           /* system CPU time                          */
ARTICLE *ArtNums = NULL;        /* article numbers for read                 */
char IPaddress[16] =            /* server IP string                         */
X    { SERV_HOST_ADDR };
time_t ARmin, ARmax;            /* article read min/max times               */
int CRcount = 0;                /* connect retry count                      */
int CRmax = 0;                  /* connect retry max                        */
X    
/* room to store the group names */
static char *GroupNames[NGROUP];
static char  Groups[NGROUP * (GSIZE+1)];
X
/* last request send to the server */
char LastReq[MAXLINE];
X
/* socket i/o lines */
static char sendline[MAXLINE];
static char recvline[MAXLINE + 1];
X
/* line to build status messages */
static char statmsg[MAXLINE+1];
X
/* article reading counters */
int     ArtReadLines = 0;       /* total lines                              */
time_t  ArtReadTimes = 0;       /* total time to read (ticks)               */
X
/* prototypes */
static int BuildGNames(int);
static int putcs(const char *);
static int setgroup(int);
static int ReadOneArt(const char *);
static int ReadHeader(int);
static void ReadArts(void);
static void Usage(void);
static void TimeOut(void);
static void InitServer(void);
static void setalarm(int, const char *);
static void errexit(const char *, int);
static char *ArtRef(const char *);
static void Put_TS_Str(int, char *);
X
void 
main(int argc, char *argv[])
{
X    int n, m;
X    int wait4 = 0;
X    int status;
X    clock_t et1, et2;
X    float et_sec;
X    struct tms TimeDummy;
X    struct hostent *HostInfo;
X
X    /* initialize for any error messages */
X    pname = argv[0];
X
X    /* parse args */
X    while ((n = getopt(argc, argv, "n:t:T:m:I:H:N:vp:S")) > 0) {
X        switch (n) {
X        case 'n':       /* articles to read, total      */
X            NumToRead = atoi(optarg);
X            break;
X        case 't':       /* time to read one art         */
X            ThinkRead = atoi(optarg);
X            break;
X        case 'T':       /* header think time            */
X            ThinkHead = atoi(optarg);
X            break;
X        case 'm':       /* max arts/group               */
X            Max_per_group = atoi(optarg);
X            break;
X        case 'I':       /* IP address                   */
X            strcpy(IPaddress, optarg);
X            break;
X        case 'H':       /* host name */
X            HostInfo = gethostbyname(optarg);
X            if (HostInfo == NULL) {
X                printf("Host %s not found\n", optarg);
X                exit(1);
X            }
X            sprintf(IPaddress, "%d.%d.%d.%d",
X                HostInfo->h_addr_list[0][0], HostInfo->h_addr_list[0][1],
X                HostInfo->h_addr_list[0][2], HostInfo->h_addr_list[0][3]
X            );
X            break;
X        case 'N':       /* # of readers                 */
X            NumProc = atoi(optarg);
X            break;
X        case 'v':       /* be verbose                   */
X            ++verbose;
X            break;
X        case 'p':       /* pause between forks()        */
X            SpawnPause = atoi(optarg);
X            if (SpawnPause < 1 || SpawnPause > 30) {
X                errexit("-p val range 1..30", 1);
X            }
X            break;
X        case 'S':       /* socket conservation on       */
X            Conserve = 1;
X            break;
X        default:        /* something WRONG here...      */
X            Usage();
X            break;
X        }
X    }
X
X    /* test for leftovers */
X    if (optind < argc) {
X        printf("[%s] unprocessed arguments (%d/%d)\n", MyPID, optind, argc);
X        Usage();
X    }
X
X    /* give useful info */
X    if (verbose) {
X        printf(
X            "NNTP Synthetic Load Generator v1.28.1 modified 1995/07/19 18:31:09\n"
X            "%8d processes\n%8d articles/process\n"
X            "%8d max articles in any one group\n"
X            "%8d sec think time on titles (zero = 5 + 200ms/art)\n"
X            "%8d sec to read each article\n"
X            "Start a process every %d sec against server IP %s\n",
X            NumProc, NumToRead, Max_per_group, ThinkHead, ThinkRead,
X            SpawnPause, IPaddress
X        );
X        if (Conserve)
X            printf("Reopen the socket for each article read\n"); else
X            printf("One socket opened per process\n");
X        fflush(stdout);
X    }
X    InitServer();
X
X    /* build the list of groups */
X    et1 = times(&TimeDummy);
X    n = BuildGNames(sockfd);
X    et1 = times(&TimeDummy) - et1;
X    if (n < 0) {
X        errexit("Can't build list of groups!", 1);
X    }
X    else
X    {
X        et_sec = (float)et1/CLK_TCK;
X        printf("[%s] namebuild %6d groups in %6.3f sec (%3.0f g/s)\n",
X            MyPID, n, et_sec, n/et_sec
X        );
X        TotalGroups = n;
X    }
X
X    /* get the date as seen by the server */
X    if (putcs("date\r\n") < 0) {
X        printf("[%s] Can't get date from server\n", MyPID);
X        exit(1);
X    }
X
X    /* output the server's idea of the date */
X    setalarm(5, "Server didn't respond to DATE");
X    n = readline(sockfd, recvline, MAXLINE);
X    alarm(0);
X    recvline[n] = EOS;
X    if (atoi(recvline) != 111) {
X        printf("[%s] Can't get date from server\n", MyPID);
X        exit(1);
X    }
X    sscanf(recvline+4, "%[0-9]*", sendline);
X    sprintf(statmsg, "Server start date: %s\n", sendline);
X    TRACE_TS(1, statmsg);
X
X    /* here's where we really do the work */
X    et1 = times(&TimeDummy);
X    for (m = NumProc; m--; ) {
X        if ((n = fork()) == 0) {
X            /* light housekeeping to keep readers and parent separated */
X            sprintf(MyPID, "S%05d", NumProc-m);
X            wait4 = 0;
X  
X            /* gather wrapup statistics */
X            et2 = times(&TimeDummy);
X            ReadArts();
X            TotalETsec = ((float)times(&TimeDummy) - et2)/CLK_TCK;
X            TotalCPUuser = (TimeDummy.tms_utime + TimeDummy.tms_cutime)/(float)CLK_TCK;
X            TotalCPUsystem = (TimeDummy.tms_stime + TimeDummy.tms_cstime)/(float)CLK_TCK;
X            TRACE(1, (
X                /* NOTE: multi-line format here... */
X                "stats: %3d art  %6d lines  %8.3f ET(r)"
X                "  %8.3f ET(t) %8.3fu %8.3fs\n"
X                "min read %8.3f max read %8.3f %d retries, %d max\n",
X                NumToRead, ArtReadLines,
X                ArtReadSec, TotalETsec,
X                TotalCPUuser, TotalCPUsystem,
X                ARmin/(float)CLK_TCK, ARmax/(float)CLK_TCK,
X                CRcount, CRmax
X            ));
X            TRACE_TS(1, "Process ends.\n");
X            break;
X        } else if (n > 0) {
X            /* output trace and wait a bit to ease startup load */
X            sprintf(statmsg, "Started process %5d\n", n);
X            TRACE_TS(3, statmsg);
X            ++wait4;
X            sleep(SpawnPause);
X        } else {
X            sprintf(sendline, "[%s] fork() failed!", MyPID);
X            perror(sendline);
X            exit(1);
X        }
X    }
X
X    /* wait for children if any */
X    if (wait4) {
X        for (m=wait4; m--; ) n = wait(&status);
X        et_sec = ((float)times(&TimeDummy) - et1)/CLK_TCK;
X        TRACE(1, ("[%s] %3d readers terminated, %7.3f sec\n", MyPID, wait4, et_sec));
X
X        /* get the ending time */
X        if (1 <= verbose) {
X            if (putcs("date\r\n") < 0) {
X                printf("[%s] can't send final DATE\n", MyPID);
X                exit(1);
X            }
X            setalarm(5, "Can't read final date");
X            n = readline(sockfd, recvline, MAXLINE);
X            recvline[n] = EOS;
X            alarm(0);
X            sscanf(recvline, "%d %[0-9]", &n, sendline);
X            if (n != 111) {
X                printf("[%s] Can't read final DATE\n", MyPID);
X                exit(1);
X            }
X            printf("[%s] server end date: %s\n", MyPID, sendline);
X        }
X
X        /* clean wrapup - send a nice quit */
X        if ((n = putcs("quit\n")) < 0) {
X            errexit("Can't send QUIT", 1);
X        }
X        n = readline(sockfd, recvline, MAXLINE);
X        recvline[n] = EOS;
X    }
X
X    close(sockfd);
X
X    exit(0);
}
X
/*****************************************************************
X |  BuildGNames - build list of group names
X |----------------------------------------------------------------
X |  saves the group names in the Groups array, with pointers in
X |  the GroupNames array.
X |
X |  Returns: # of groups or -1 for overflow
X ****************************************************************/
X
static int
BuildGNames(int sockfd)
{
X    char *fillin = Groups;
X    int n, ngroups = 0;
X
X    /* go ask the server for some groups */
X    n = putcs("list active\n");
X    if (n < 1) {
X        errexit("Can't ask for groups", 1);
X    }
X
X    /* wait for the reply saying it will happen */
X    n = readline(sockfd, recvline, MAXLINE);
X    recvline[n] = EOS;
X    if (atoi(recvline) != 215) {
X        sprintf(sendline,
X            "LIST ACTIVE returned reject status:\n%12s%s", "", recvline);
X        errexit(sendline, 1);
X    }
X
X    while (n = readline(sockfd, recvline, MAXLINE)) {
X        recvline[n] = EOS;
X
X        /* see if done */
X        if (recvline[0] == '.') break;
X
X        /* truncate the string at the first blank */
X        *strchr(recvline, ' ') = EOS;
X
X        /* if the array is full, stop trying */
X        if (ngroups == NGROUP) {
X            fprintf(stderr, "<W> too many groups > %d\n", NGROUP);
X            return ngroups;
X        }
X
X        /* store the data in the array, save the pointer, bump the pointer */
X        strcpy(fillin, recvline);
X        GroupNames[ngroups++] = fillin;
X        fillin += n;
X    }
X
X    return ngroups;
}
X
/*****************************************************************
X |  putcs - output a constant string to the socket
X |----------------------------------------------------------------
X |  Returns: chars sent
X ****************************************************************/
X
static int
putcs(const char *string)
{
X    int n, len;
X
X    /* save the command for future error messages */
X    strcpy(LastReq, string);
X
X    len = strlen(string);
X    n = writen(sockfd, (char *)string, len);
X    return (n == len ? n : -1);
}
X
/*****************************************************************
X |  ReadArts - read articles simulating delays
X |----------------------------------------------------------------
X |  Uses a separate process for each read, to allow seed
X ****************************************************************/
X
static void
ReadArts(void)
{
X    unsigned short seeds[3];
X    int count = NumToRead;
X    int gcount = 0;
X    int cur_group;
X    int n;
X    int avail = 0;
X
X    /* seed the random generator */
X    seeds[0] = 0;
X    seeds[1] = getpid();
X    seeds[2] = getppid();
X    seed48(seeds);
X
X    /* open the server just once if not conserving sockerts */
X    if (!Conserve) InitServer();
X
X    /* read articles */
X    while (count) {
X        /* see that some are available in this group */
X        while (avail == 0) {
X            avail = setgroup(cur_group = TotalGroups*drand48());
X            TRACE(2,
X                ("[%s] group %s, %d arts\n",
X                MyPID, GroupNames[cur_group], avail)
X            );
X        }
X
X        /* loop until you read one article */
X        for (n = 0; n < avail && count; ++n) {
X            if (ReadOneArt(ArtNums[n]) == 0) {
X                --count;
X
X                /* here's the user reading the article */
X                if (ThinkRead) sleep(ThinkRead);
X            }
X        }
X        avail = 0;
X    }
X
X    /* output the final results */
X    ArtReadSec = (float)ArtReadTimes/CLK_TCK;
X
X    /* close if still open */
X    if (Conserve) close(sockfd);
}
X
/*****************************************************************
X |  ReadOneArt - read just one article
X |----------------------------------------------------------------
X |  asks for the head and then the body
X ****************************************************************/
X
static int
ReadOneArt(const char *artnum)
{
X    int n;
X    int rc = 0;
X
X    /* timing stuff, line count */
X    int nlines;
X    time_t et;
X    struct tms Dummy;
X
X    /* start the clock */
X    et = times(&Dummy);
X
X    /* may have to do overhead */
X    if (Conserve) InitServer();
X
X    /* get the article */
X    sprintf(sendline, "article %s\r\n", artnum);
X    putcs(sendline);
X    n = readline(sockfd, recvline, MAXLINE);
X    recvline[n] = EOS;
X    if ((n = atoi(recvline)) != 220) {
X        /* didn't get data, check bad number */
X        if (n == 430) {
X            /* article not found, close socket, due to NOV? */
X            TRACE(3, ("[%s] article %s not online\n", MyPID, artnum));
X            if (Conserve) close(sockfd);
X            return -1;
X        }
X        else
X        {
X            printf("[%s] article read failed (%s)\n\t%s\n",
X                MyPID, sendline, recvline);
X            exit(1);
X        }
X    }
X
X    /* skip the text */
X    do {
X        n = readline(sockfd, recvline, MAXLINE);
X        recvline[n] = 0;
X        ++rc;
X    } while (!EOM(recvline));
X    ArtReadLines += rc - 1;
X
X    /* stop the clock and add to the total */
X    if (Conserve) close(sockfd);
X    et = times(&Dummy) - et;
X    ArtReadTimes += et;
X    sprintf(statmsg, "got article, %d lines, %.3f sec\n",
X        rc-1, (float)et/CLK_TCK);
X    TRACE_TS(3, statmsg);
X    if (et < ARmin || ARmin == 0) ARmin = et;
X    if (et > ARmax) ARmax = et;
X
X    return 0;
}
X
/*****************************************************************
X |  ReadHeader - read one header
X ****************************************************************/
X
static int
ReadHeader(int artnum)
{
X    int n;
X    int linecount = 0;
X
X    /* ask for the article head */
X    sprintf(sendline, "head %d\n", artnum);
X    n = putcs(sendline);
X
X    /* check read status */
X    n = readline(sockfd, recvline, MAXLINE);
X    recvline[n] = EOS;
X    if ((n = atoi(recvline)) != 221) {
X        /* bad status, see if invalid article */
X        if (n == 423) return (-1);
X        else {
X            /* bogus answer, let's barf! */
X            printf("[%s] <F> invalid response, not 221 or 423!\n\t%s",
X                MyPID, recvline);
X            printf("[%s] Last request: %s", MyPID, LastReq);
X            exit(1);
X        }
X    }
X
X    /* skip until end of text marker */
X    strcpy(sendline, "Skipping header text lines");
X    do {
X        setalarm(10, sendline);
X        n = readline(sockfd, recvline, MAXLINE);
X        recvline[n] = EOS;
X        ++linecount;
X    } while (!EOM(recvline));
X    alarm(0);
X
X    return linecount;
}
X
/*****************************************************************
X |  setgroup - select a group
X |----------------------------------------------------------------
X |  Sets:       value of 1st available article
X |  Returns:    # of articles
X ****************************************************************/
X
static int
setgroup(int groupnum)
{
X    int n, an;
X    int status, numart, lo_art, hi_art;
X    char *cur_gname;
X    char *artstr;
X
X    /* if the array id not allocated, do it now */
X    if (ArtNums == NULL) {
X        n = Max_per_group * sizeof(ARTICLE);
X        ArtNums = (ARTICLE*) malloc(n);
X        if (ArtNums == NULL) {
X            printf("[%s] Can't allocate article list\n", MyPID);
X            exit(1);
X        }
X    }
X
X    /* open the server if needed */
X    if (Conserve) InitServer();
X
X    /* build the group command and do the set */
X    cur_gname = GroupNames[groupnum];
X    sprintf(sendline, "group %s\r\n", cur_gname);
X    n = putcs(sendline);
X
X    /* read and parse the response */
X    n = readline(sockfd, recvline, MAXLINE);
X    if (n < 0) errexit("Socket close while setting group", 1);
X    recvline[n] = EOS;
X    sscanf(recvline, "%d %d %d %d", &status, &numart, &lo_art, &hi_art);
X    if (status != 211) {
X        /* see if no group */
X        if (status == 411) {
X            sprintf(sendline, "No such group: %s", cur_gname);
X            errexit(sendline, 1);
X        } else {
X            sprintf(sendline, "Unknown error: %s", recvline);
X            errexit(sendline, 1);
X        }
X    }
X    if (numart == 0) {
X        /* close the socket and return zero count */
X        if (Conserve) close(sockfd);
X        return 0;
X    }
X
X    /* read the headers and wait for the user to think about them */
X    sprintf(sendline, "xover  %d-%d\r\n", lo_art, hi_art);
X    putcs(sendline);
X    n = readline(sockfd, recvline, MAXLINE);
X    recvline[n] = 0;
X    if (atoi(recvline) != 224) {
X        printf("[%s] didn't get data for xover command\n", MyPID);
X        exit(1);
X    }
X
X    /* Now read some article id's */
X    for (an = 0;;) {
X        n = readline(sockfd, recvline, MAXLINE);
X        recvline[n] = EOS;
X        if (EOM(recvline)) break;
X        artstr = ArtRef(recvline);
X        if (an < Max_per_group && artstr != NULL)
X            strcpy(ArtNums[an++], artstr);
X    }
X
X    /* "look at" the headers */
X    if (Conserve) close(sockfd);
X    sleep(ThinkHead ? ThinkHead : (5 + n/5));
X    if (numart > Max_per_group) numart = Max_per_group;
X    return numart;
}
X
/*****************************************************************
X |  Usage - show the command line options
X |----------------------------------------------------------------
X |  exits - no return
X ****************************************************************/
X
static void
Usage(void)
{
X    puts("loadtest - NNTP synthetic load generator\n");
X    puts("Syntax:");
X    puts("  loadtest [ options ]");
X    puts("Options:");
X    puts("  -n   # of articles to read          def: 30");
X    puts("  -t   time to read one art (sec)     def: 10");
X    puts("  -m   max articles in one group,     def: 10");
X    puts("  -T   time to read headers (sec)     def: 5+lines/5");
X    puts("  -I   IP address of server (N.N.N.N) def: 127.0.0.1");
X    puts("  -H   server host name (node.dom)    def: loopback");
X    puts("  -N   # of readers                   def: 1");
X    puts("  -v   verbose progress reports       def: off");
X    puts("  -p   # sec between forks (1..30)    def: 3");
X    puts("  -S   use socket conservation        def: off");
X
X    exit(1);
}
X
/*****************************************************************
X |  Timeout handling on reads
X |----------------------------------------------------------------
X |  This code allows handling of timeouts for reads (or anything)
X |  by setting a timer and specifying the message to output if
X |  the timer times out.
X ****************************************************************/
X
static const char *TimeOutMsg;
X
static void
setalarm(int secs, const char *string)
{
X    TimeOutMsg = string;
X    signal(SIGALRM, (void(*)(int))TimeOut);
X    alarm(secs);
}
X
static void
TimeOut(void)
{
X    printf("[%s] Operation timeout: %s\n", MyPID, TimeOutMsg);
X    printf("[%s] Last request: %s", MyPID, LastReq);
X    exit(1);
}
X
/*****************************************************************
X |  InitServer - open a socket to the server, save the value
X |----------------------------------------------------------------
X |  exit if bad status
X ****************************************************************/
X
static void
InitServer(void)
{
X    int n;
X    int et;                     /* local et for socket open */
X    struct tms Dummy;           /* dummy data structure to get clock */
X    int RetryCount;
X    struct sockaddr_in serv_addr;
X
X    /* initialize for socket connection */
X    bzero((char *) &serv_addr, sizeof(serv_addr));
X    serv_addr.sin_family = AF_INET;
X    serv_addr.sin_addr.s_addr = inet_addr(IPaddress);
X    serv_addr.sin_port = htons(SERV_TCP_PORT);
X
X    for (RetryCount = 0; RetryCount < MAX_SOCKET_RETRIES; ++RetryCount ) {
X        et = times(&Dummy);
X
X        /* make the connect and error check */
X        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
X            fprintf(stderr, "[%s] client: can't open stream socket\n", MyPID);
X            exit(1);
X        }
X
X        if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
X            TRACE(3, ("[%s] client: can't connect to server\n", MyPID));
X            close(sockfd);
X            sleep(OPEN_RETRY_TIME);
X            ++CRcount;
X            continue;
X        }
X    
X        /* read the HELO line from the server, print if found */
X        n = readline(sockfd, recvline, MAXLINE);
X        if (n < 0) {
X            TRACE(3, ("[%s] Connection refused!\n", MyPID));
X            close(sockfd);
X            sleep(OPEN_RETRY_TIME);
X            ++CRcount;
X            continue;
X        }
X        /* connection is open */
X        break;
X    }
X
X    /* see if any error */
X    if (RetryCount) {
X        /* see if we have given up */
X        if (RetryCount == MAX_SOCKET_RETRIES) {
X            sprintf(LastReq,
X                "Can't connect after %d retries, give up!\n", RetryCount);
X            TRACE_TS(0, LastReq);
X            errexit(LastReq, 1);
X        }
X        else {
X            /* see if retries current */
X            if (RetryCount > CRmax) CRmax = RetryCount;
X            sprintf(statmsg, "server open took %d retries\n", RetryCount);
X            TRACE_TS(2, statmsg);
X        }
X    }
X    et = times(&Dummy) - et;
X    sprintf(statmsg, "socket open time %.3f sec.\n", et/(float)CLK_TCK);
X    TRACE_TS(3, statmsg);
X
X    recvline[n] = EOS;
X    if (n == 0) {
X        fprintf(stderr, "[%s] Empty HELO message read!\n", MyPID);
X        exit(1);
X    }
X    if (atoi(recvline) != 200) {
X        if (recvline[n-1] != '\n') {
X            recvline[n] = '\n';
X            recvline[++n] = EOS;
X        }
X        sprintf(statmsg, "HELO msg err, len %d\nWas: %.50s", n, recvline);
X        TRACE_TS(0, statmsg);
X        errexit("Bad HELO message", 1);
X    }
X
X    /* switch to reader service mode */
X    if ((n = putcs("mode reader\r\n")) < 0) {
X        errexit("Error sending mode switch!",1);
X    } else {
X        /* protect against loss of input */
X        setalarm(20, "Wait for reply to \"mode reader\"");
X
X        /* read the ack and display it */
X        n = readline(sockfd, recvline, MAXLINE);
X        alarm(0);
X        if (n < 0) {
X            errexit("No response to mode switch request!",1);
X        }
X        recvline[n] = EOS;
X    }
}
X
/*****************************************************************
X |  SkipData - skip multiline data
X |----------------------------------------------------------------
X |  Checks that the first line matches the code of the argument,
X |  then reads lines until EOM
X ****************************************************************/
X
static int
SkipData(int TextType)
{
X    int n;
X    int linecount = -1;
X
X    /* read the first line */
X    sprintf(sendline, "Waiting for %d line", TextType);
X    setalarm(10, sendline);
X    n = readline(sockfd, recvline, MAXLINE);
X    recvline[n] = EOS;
X    alarm(0);
X
X    /* check the type of the response */
X    if (atoi(recvline) != TextType) {
X        printf("[%s] <F> invalid response, expected %d!\n\t[%3d]=>%s\n",
X            MyPID, TextType, strlen(recvline), recvline);
X        printf("[%s] Last request: %s", MyPID, LastReq);
X        exit(1);
X    }
X
X    /* skip until end of text marker */
X    sprintf(sendline, "Skipping type %d text", TextType);
X    do {
X        setalarm(10, sendline);
X        n = readline(sockfd, recvline, MAXLINE);
X        recvline[n] = EOS;
X        ++linecount;
X    } while (!EOM(recvline));
X    alarm(0);
X
X    return linecount;
}
X
/*****************************************************************
X |  errexit - output a string and exit
X |----------------------------------------------------------------
X |  displays the string with process id, then exits status 1
X ****************************************************************/
X
static void
errexit(const char *emsg, int errstat)
{
X    printf("[%s] <F> %s\n", MyPID, emsg);
X    exit(errstat);
}
X
/*****************************************************************
X |  ArtRef - return the article reference string from an XOVER line
X |----------------------------------------------------------------
X |  Note: modifies the data in the buffer!
X ****************************************************************/
X
static char *
ArtRef(const char *str)
{
X    int n;
X    char *lastQ = NULL;
X    static char ArtBuf[MAXLINE];
X
X    /* do the sscanf */
X    n = sscanf(str, "%*[^\t]\t%*[^\t]\t%*[^\t]\t%*[^\t]\t%[^>]", ArtBuf);
X    if (n == 1) {
X        strcat(ArtBuf, ">");
X        lastQ = ArtBuf;
X    }
X
X    return lastQ;
}
X
/*****************************************************************
X | Put_TS_Str - output a trace message with timestamp
X ****************************************************************/
X
static void
Put_TS_Str(int lvl, char *string)
{
X    char outbuf[2 * MAXLINE + 1];
X    char *dest = outbuf, *dptr;
X    char datestr[50];
X    struct tm *ltime;
X    time_t rightnow;
X    struct tms Dummy;
X    int ch;
X    int needhdr = 1;            /* need a header for this line */
X    float ftime;                /* fractional hours */
X
X    /* build the datestring, once */
X    rightnow = time((int *)NULL);
X    ltime = localtime(&rightnow);
X    ftime = (3600*ltime->tm_hour + 60*ltime->tm_min + ltime->tm_sec)/3600.0;
X    sprintf(datestr, "[%s] %4d%03d%06.3f ", MyPID,
X        ltime->tm_year + 1900, ltime->tm_yday, ftime
X    );
X
X    /* copy the input string, adding date for each newline */
X    while (ch = *string++) {
X        /* test for header needed */
X        if (needhdr) {
X            strcpy(dest, datestr);
X            dest += strlen(datestr);
X        }
X
X        *dest++ = ch;
X        needhdr = (ch == '\n');
X    }
X
X    /* now output the data */
X    *dest = EOS;
X    fputs(outbuf, stdout);
X    fflush(stdout);
X
X    /* if trace level is zero, fatal error, output to stderr, too */
X    if (lvl == 0) fputs(outbuf, stderr);
}
SHAR_EOF
  $shar_touch -am 0929150195 'NewsTools-1.3/nslg/loadtest/nslg.c' &&
  chmod 0644 'NewsTools-1.3/nslg/loadtest/nslg.c' ||
  echo 'restore of NewsTools-1.3/nslg/loadtest/nslg.c failed'
  shar_count="`wc -c < 'NewsTools-1.3/nslg/loadtest/nslg.c'`"
  test 27686 -eq "$shar_count" ||
    echo "NewsTools-1.3/nslg/loadtest/nslg.c: original size 27686, current size $shar_count"
fi
# ============= NewsTools-1.3/nslg/loadtest/inet.h ==============
if test -f 'NewsTools-1.3/nslg/loadtest/inet.h' && test X"$1" != X"-c"; then
  echo 'x - skipping NewsTools-1.3/nslg/loadtest/inet.h (file already exists)'
else
  echo 'x - extracting NewsTools-1.3/nslg/loadtest/inet.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'NewsTools-1.3/nslg/loadtest/inet.h' &&
#include "../inet.h"
#include <netdb.h>
X
#define SERV_UDP_PORT 6000
#define SERV_TCP_PORT 119				/* NNTP port */
#define SERV_HOST_ADDR "127.0.0.1"		/* local host */
X
SHAR_EOF
  $shar_touch -am 0919114795 'NewsTools-1.3/nslg/loadtest/inet.h' &&
  chmod 0644 'NewsTools-1.3/nslg/loadtest/inet.h' ||
  echo 'restore of NewsTools-1.3/nslg/loadtest/inet.h failed'
  shar_count="`wc -c < 'NewsTools-1.3/nslg/loadtest/inet.h'`"
  test 167 -eq "$shar_count" ||
    echo "NewsTools-1.3/nslg/loadtest/inet.h: original size 167, current size $shar_count"
fi
# ============= NewsTools-1.3/nslg/lib/Makefile ==============
if test ! -d 'NewsTools-1.3/nslg/lib'; then
  echo 'x - creating directory NewsTools-1.3/nslg/lib'
  mkdir 'NewsTools-1.3/nslg/lib'
fi
if test -f 'NewsTools-1.3/nslg/lib/Makefile' && test X"$1" != X"-c"; then
  echo 'x - skipping NewsTools-1.3/nslg/lib/Makefile (file already exists)'
else
  echo 'x - extracting NewsTools-1.3/nslg/lib/Makefile (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'NewsTools-1.3/nslg/lib/Makefile' &&
# makefile for library
X
OBJS		= readline.o writen.o
LIB		= liblineio.a
ARCH		= ar
CFLAGS		= -O -DONE_AT_A_TIME
AFLAGS		= rvs
X
X.c.o:
X	$(CC) -c $(CFLAGS) $<
X
$(LIB):		$(OBJS)
X	$(ARCH) $(AFLAGS) $(LIB) $?
X
$(OBJS):	../inet.h
#
#	overhead stuff
#
tidy:
X	rm -f *.o
X
clean:
X	rm -f *.[ao]
#
#	o/s dependent rules
#
# Linux - Slackware 2.3, 1.2.13 kernel
linux:
X	$(MAKE) $(LIB) CFLAGS="-O2 -fno-strength-reduce"
SHAR_EOF
  $shar_touch -am 0828162395 'NewsTools-1.3/nslg/lib/Makefile' &&
  chmod 0644 'NewsTools-1.3/nslg/lib/Makefile' ||
  echo 'restore of NewsTools-1.3/nslg/lib/Makefile failed'
  shar_count="`wc -c < 'NewsTools-1.3/nslg/lib/Makefile'`"
  test 404 -eq "$shar_count" ||
    echo "NewsTools-1.3/nslg/lib/Makefile: original size 404, current size $shar_count"
fi
# ============= NewsTools-1.3/nslg/lib/readline.c ==============
if test -f 'NewsTools-1.3/nslg/lib/readline.c' && test X"$1" != X"-c"; then
  echo 'x - skipping NewsTools-1.3/nslg/lib/readline.c (file already exists)'
else
  echo 'x - extracting NewsTools-1.3/nslg/lib/readline.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'NewsTools-1.3/nslg/lib/readline.c' &&
/* $Id: readline.c,v 1.6 1994/10/06 18:19:21 davidsen Exp $ */
#include "../inet.h"
int readline(int fd, char *ptr, int maxlen)
{
X    int n;
X    int rc;
X    char c;
X    /* This stuff allows faster reading of a single socket */
#ifdef  ONE_AT_A_TIME
X    static int sock_in_use = 0;
X    static char sock_buf[MAXLINE+1];
X    static char *next_ch;
#endif
X
#ifdef ONE_AT_A_TIME
X    /* test for a new socket being used */
X    if (fd != sock_in_use) {
X        sock_in_use = fd;
X        *sock_buf = 0;
X        next_ch = sock_buf;
X    }
X
X    /* fill the user buffer */
X    n = 0;
X    do {
X        /* load the buffer if empty */
X        if (*next_ch == 0) {
X            rc = read(fd, sock_buf, MAXLINE);
X            if (rc < 1) return -1;
X            sock_buf[rc] = 0;
X            next_ch = sock_buf;
X        }
X
X        /* copy data from the buffer */
X        *ptr++ = c = *next_ch++;
X    } while (c != '\n' && ++n < maxlen);
#else    
X    for (n = 1; n < maxlen; n++)
X    {
X        switch (rc = read(fd,&c,1)) {
X        case 1:                 /* got a char, save it, break if EOL */
X            *ptr++ = c;
X            if (c == '\n') break;
X            continue;
X        case 0:                 /* nothing read, but no error */
X            if (n == 1) return(0);
X            else break;
X            /* not reached */
X        default:                /* anything else is a problem */
X            return(-1);
X            /* not reached */
X        }
X        /* always break unless an explicit continue */
X        break;
X    }
#endif
X    *ptr = 0;
X    return(n);
}
SHAR_EOF
  $shar_touch -am 1006141994 'NewsTools-1.3/nslg/lib/readline.c' &&
  chmod 0444 'NewsTools-1.3/nslg/lib/readline.c' ||
  echo 'restore of NewsTools-1.3/nslg/lib/readline.c failed'
  shar_count="`wc -c < 'NewsTools-1.3/nslg/lib/readline.c'`"
  test 1552 -eq "$shar_count" ||
    echo "NewsTools-1.3/nslg/lib/readline.c: original size 1552, current size $shar_count"
fi
# ============= NewsTools-1.3/nslg/lib/writen.c ==============
if test -f 'NewsTools-1.3/nslg/lib/writen.c' && test X"$1" != X"-c"; then
  echo 'x - skipping NewsTools-1.3/nslg/lib/writen.c (file already exists)'
else
  echo 'x - extracting NewsTools-1.3/nslg/lib/writen.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'NewsTools-1.3/nslg/lib/writen.c' &&
#include "../inet.h"
X
int writen(int fd,char *ptr,int nbytes)
X
{
X int nleft;
X int nwritten;
X
X nleft = nbytes;
X while(nleft > 0)
X {
X  nwritten = write(fd,ptr,nleft);
X  if(nwritten <= 0)
X  {
X   return(nwritten);
X  }
X  nleft -= nwritten;
X  ptr += nwritten;
X }
X return(nbytes - nleft);
}
SHAR_EOF
  $shar_touch -am 0330005294 'NewsTools-1.3/nslg/lib/writen.c' &&
  chmod 0644 'NewsTools-1.3/nslg/lib/writen.c' ||
  echo 'restore of NewsTools-1.3/nslg/lib/writen.c failed'
  shar_count="`wc -c < 'NewsTools-1.3/nslg/lib/writen.c'`"
  test 284 -eq "$shar_count" ||
    echo "NewsTools-1.3/nslg/lib/writen.c: original size 284, current size $shar_count"
fi
exit 0


