sprintf(buffer, " [%s]", date);
sprintf(buffer, " [%.*s]", (int)(sizeof(buffer) - 4), date);
PRIVATE int read_group ARGS3(  CONST char *,groupName,  int,first_required,  int,last_required){    char line[LINE_LENGTH+1];    char author[LINE_LENGTH+1];    char subject[LINE_LENGTH+1];    char *date = NULL;    int i;    char *p;    BOOL done;    char buffer[LINE_LENGTH];    char *reference = NULL;		/* Href for article */    int art;				/* Article number WITHIN GROUP */    int status, count, first, last;	/* Response fields */					/* count is only an upper limit */    author[0] = '\0';    START(HTML_HEAD);    PUTC('\n');    START(HTML_TITLE);    PUTS("Newsgroup ");    PUTS(groupName);    END(HTML_TITLE);    PUTC('\n');    END(HTML_HEAD);    PUTC('\n');    sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);    CTRACE(tfp, "Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n",		status, count, first, last, first_required, last_required);    if (last == 0) {	PUTS(gettext("\nNo articles in this group.\n"));	goto add_post;    }#define FAST_THRESHOLD 100	/* Above this, read IDs fast */#define CHOP_THRESHOLD 50	/* Above this, chop off the rest */    if (first_required < first)	first_required = first; 	/* clip */    if ((last_required == 0) || (last_required > last))	last_required = last;    if (last_required < first_required) {	PUTS(gettext("\nNo articles in this range.\n"));	goto add_post;    }    if (last_required-first_required+1 > HTNewsMaxChunk) { /* Trim this block */	first_required = last_required-HTNewsChunkSize+1;    }    CTRACE(tfp, "    Chunk will be (%d-%d)\n",		first_required, last_required);    /*    **	Set window title.    */    sprintf(buffer, gettext("%s,  Articles %d-%d"),		    groupName, first_required, last_required);    START(HTML_H1);    PUTS(buffer);    END(HTML_H1);    PUTC('\n');    /*    **	Link to earlier articles.    */    if (first_required > first) {	int before;			/* Start of one before */	if (first_required-HTNewsMaxChunk <= first)	    before = first;	else	    before = first_required-HTNewsChunkSize;	HTSprintf0(&dbuf, "%s%s/%d-%d", NewsHREF, groupName,				      before, first_required-1);	CTRACE(tfp, "    Block before is %s\n", dbuf);	PUTC('(');	start_anchor(dbuf);	PUTS(gettext("Earlier articles"));	END(HTML_A);	PUTS("...)\n");	START(HTML_P);	PUTC('\n');    }    done = NO;/*#define USE_XHDR*/#ifdef USE_XHDR    if (count > FAST_THRESHOLD)  {	sprintf(buffer, gettext("\nThere are about %d articles currently available in %s, IDs as follows:\n\n"),		count, groupName);	PUTS(buffer);	sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF);	status = response(buffer);	if (status == 221) {	    p = line;	    while (!done) {		int ich = NEXT_CHAR;		*p++ = ich;		if (ich == EOF) {		    if (interrupted_in_htgetcharacter) {			interrupted_in_htgetcharacter = 0;			CTRACE(tfp, "HTNews: Interrupted on read, closing socket %d\n",				    s);			NEWS_NETCLOSE(s);			s = -1;			return(HT_INTERRUPTED);		    }		    abort_socket();	/* End of file, close socket */		    return(HT_LOADED);	/* End of file on response */		}		if (((char)ich == '\n') || (p == &line[LINE_LENGTH])) {		    *p = '\0';		/* Terminate the string */		    CTRACE(tfp, "X %s", line);		    if (line[0] == '.') {			/*			**  End of article?			*/			if ((unsigned char)line[1] < ' ') {			    done = YES;			    break;			} else {		/* Line starts with dot */				/* Ignore strange line */			}		    } else {			/*			**  Normal lines are scanned for			**  references to articles.			*/			char * space = strchr(line, ' ');			if (space++)			    write_anchor(space, space);		    } /* if not dot */		    p = line;			/* Restart at beginning */		} /* if end of line */	    } /* Loop over characters */	    /* leaving loop with "done" set */	} /* Good status */    }#endif /* USE_XHDR */    /*    **	Read newsgroup using individual fields.    */    if (!done) {	START(HTML_B);	if (first == first_required && last == last_required)	    PUTS(gettext("All available articles in "));	else	    PUTS("Articles in ");	PUTS(groupName);	END(HTML_B);	PUTC('\n');	if (LYListNewsNumbers)	    start_list(first_required);	else	    START(HTML_UL);	for (art = first_required; art <= last_required; art++) {/*#define OVERLAP*/#ifdef OVERLAP	    /*	    **	With this code we try to keep the server running flat out	    **	by queuing just one extra command ahead of time.	    **	We assume (1) that the server won't abort if it gets input	    **	during output, and (2) that TCP buffering is enough for the	    **	two commands.  Both these assumptions seem very reasonable.	    **	However, we HAVE had a hangup with a loaded server.	    */	    if (art == first_required) {		if (art == last_required) {		/* Only one */		    sprintf(buffer, "HEAD %d%c%c",				    art, CR, LF);		    status = response(buffer);		} else {				/* First of many */		    sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c",				    art, CR, LF, art+1, CR, LF);			status = response(buffer);		}	    } else if (art == last_required) {		/* Last of many */		status = response(NULL);	    } else {					/* Middle of many */		sprintf(buffer, "HEAD %d%c%c", art+1, CR, LF);		status = response(buffer);	    }#else	/* Not OVERLAP: */	    sprintf(buffer, "HEAD %d%c%c", art, CR, LF);	    status = response(buffer);#endif	/* OVERLAP */	    /*	    **	Check for a good response (221) for the HEAD request,	    **	and if so, parse it.  Otherwise, indicate the error	    **	so that the number of listings corresponds to what's	    **	claimed for the range, and if we are listing numbers	    **	via an ordered list, they stay in synchrony with the	    **	article numbers. - FM	    */	    if (status == 221) {	/* Head follows - parse it:*/		p = line;				/* Write pointer */		done = NO;		while( !done ) {		    int ich = NEXT_CHAR;		    *p++ = ich;		    if (ich == EOF) {			if (interrupted_in_htgetcharacter) {			    interrupted_in_htgetcharacter = 0;			    CTRACE(tfp, "HTNews: Interrupted on read, closing socket %d\n",					s);			    NEWS_NETCLOSE(s);			    s = -1;			    return(HT_INTERRUPTED);			}			abort_socket(); 	/* End of file, close socket */			return(HT_LOADED);	/* End of file on response */		    }		    if (((char)ich == LF) ||			(p == &line[LINE_LENGTH])) {			*--p = '\0';		/* Terminate  & chop LF*/			p = line;		/* Restart at beginning */			CTRACE(tfp, "G %s\n", line);			switch(line[0]) {			case '.':			    /*			    **	End of article?			    */			    done = ((unsigned char)line[1] < ' ');			    break;			case 'S':			case 's':			    if (match(line, "SUBJECT:")) {				strcpy(subject, line+9);/* Save subject */				if (HTCJK == JAPANESE) {				    HTmmdecode(subject, subject);				    HTrjis(subject, subject);				}			    }			    break;			case 'M':			case 'm':			    if (match(line, "MESSAGE-ID:")) {				char * addr = HTStrip(line+11) +1; /* Chop < */				addr[strlen(addr)-1] = '\0';	   /* Chop > */				StrAllocCopy(reference, addr);			    }			    break;			case 'f':			case 'F':			    if (match(line, "FROM:")) {				char * p2;				strcpy(author,					author_name(strchr(line,':')+1));				if (HTCJK == JAPANESE) {				    HTmmdecode(author, author);				    HTrjis(author, author);				}				p2 = author + strlen(author) - 1;				if (*p2==LF)				    *p2 = '\0'; /* Chop off newline */			    }			    break;			case 'd':			case 'D':			    if (LYListNewsDates && match(line, "DATE:")) {				StrAllocCopy(date,					     HTStrip(strchr(line,':')+1));			    }			    break;			} /* end switch on first character */		    } /* if end of line */		} /* Loop over characters */		PUTC('\n');		START(HTML_LI);		sprintf(buffer, "\"%s\"", subject);		if (reference) {		    write_anchor(buffer, reference);		    FREE(reference);		} else {		    PUTS(buffer);		}		if (author[0] != '\0') {		     PUTS(" - ");		     if (LYListNewsDates)			 START(HTML_I);		     PUTS(author);		     if (LYListNewsDates)			 END(HTML_I);		     author[0] = '\0';		}		if (date) {		    if (!diagnostic) {			for (i = 0; date[i]; i++) {			    if (date[i] == ' ') {				date[i] = HT_NON_BREAK_SPACE;			    }			}		    }		    sprintf(buffer, " [%s]", date);		    PUTS(buffer);		    FREE(date);		}		MAYBE_END(HTML_LI);		/*		**  Indicate progress!	 @@@@@@		*/	    } else if (status == HT_INTERRUPTED) {		interrupted_in_htgetcharacter = 0;		CTRACE(tfp, "HTNews: Interrupted on read, closing socket %d\n",			    s);		NEWS_NETCLOSE(s);		s = -1;		return(HT_INTERRUPTED);	    } else {		/*		**  Use the response text on error. - FM		*/		PUTC('\n');		START(HTML_LI);		START(HTML_I);		if (LYListNewsNumbers)		    strcpy(buffer, "Status:");		else		    sprintf(buffer, "Status (ARTICLE %d):", art);		PUTS(buffer);		END(HTML_I);		PUTC(' ');		PUTS(response_text);		MAYBE_END(HTML_LI);	    } /* Handle response to HEAD request */	} /* Loop over article */    } /* If read headers */    PUTC('\n');    if (LYListNewsNumbers)	END(HTML_OL);    else	END(HTML_UL);    PUTC('\n');    /*    **	Link to later articles.    */    if (last_required < last) {	int after;			/* End of article after */	after = last_required+HTNewsChunkSize;	if (after == last)	    HTSprintf0(&dbuf, "%s%s", NewsHREF, groupName); /* original group */	else	    HTSprintf0(&dbuf, "%s%s/%d-%d", NewsHREF, groupName,					  last_required+1, after);	CTRACE(tfp, "    Block after is %s\n", dbuf);	PUTC('(');	start_anchor(dbuf);	PUTS(gettext("Later articles"));	END(HTML_A);	PUTS("...)\n");    }add_post:    if (HTCanPost) {	/*	**  We have permission to POST to this host,	**  so add a link for posting messages to	**  this newsgroup. - FM	*/	char *href = NULL;	START(HTML_HR);	PUTC('\n');	if (!strncasecomp(NewsHREF, "snews:", 6))	    StrAllocCopy(href,"snewspost://");	else	    StrAllocCopy(href,"newspost://");	StrAllocCat(href, NewsHost);	StrAllocCat(href, "/");	StrAllocCat(href,groupName);	start_anchor(href);	PUTS(gettext("Post to "));	PUTS(groupName);	END(HTML_A);	FREE(href);    } else {	START(HTML_HR);    }    PUTC('\n');    return(HT_LOADED);}