/* This software is copyrighted as detailed in the LICENSE file. */

/*  trn -- threaded readnews program based on rn 4.4
 *
 *  You can request help from:  trn-users@lists.sourceforge.net
 *  Send bugs, suggestions, etc. to:  trn-workers@lists.sourceforge.net
 *
 *  Author/Maintainer of trn: trn@blorf.net (Wayne Davison)
 *  Maintainer of rn: sob@bcm.tmc.edu (Stan Barber)
 *  Original Author: lwall@sdcrdcf.UUCP (Larry Wall)
 *
 *  History:
 *	01/14/83 - rn begun
 *	04/08/83 - rn 1.0
 *	09/01/83 - rn 2.0
 *	05/01/85 - rn 4.3
 *	11/01/89 - rn/rrn integration
 *	11/25/89 - trn begun
 *	07/21/90 - trn 1.0
 *	07/04/91 - rn 4.4
 *	11/25/91 - trn 2.0
 *	07/25/93 - trn 3.0
 *	??/??/?? - trn 4.0
 *
 *  strn -- Scan(-mode)/Scoring TRN
 *
 *  Author/Maintainer of strn: caadams@zynet.com (Clifford A. Adams)
 *
 *  Strn history:
 *      Dec.  90  - "Keyword RN" initial ideas, keyword entry prototype
 *	01/16/91  - "Scoring RN" initial design notes
 *      Late  91  - cleaned up "Semicolon mode" RN patches from Todd Day
 *      Early 92  - major additions to "STRN"
 *	Mid   93  - first strn public release (version 0.8)
 *	Sep.  94  - last beta release (version 0.9.3).
 *	Late  95  - strn code ported to trn 4.0, universal selector started
 *      May   96  - strn 1.0 release
 *
 */

#include "patchlevel.h"
#include "INTERN.h"
#include "common.h"
#include "EXTERN.h"
#include "list.h"
#include "hash.h"
#include "ngdata.h"
#include "nntpclient.h"
#include "datasrc.h"
#include "nntp.h"
#include "INTERN.h"
#include "trn.h"
#include "EXTERN.h"
#include "term.h"
#include "final.h"
#include "search.h"
#include "addng.h"
#include "ngstuff.h"
#include "rcstuff.h"
#include "env.h"
#include "util.h"
#include "util2.h"
#include "only.h"
#include "ngsrch.h"
#include "help.h"
#include "last.h"
#include "init.h"
#include "intrp.h"
#include "rcln.h"
#include "sw.h"
#include "opt.h"
#include "cache.h"
#include "ng.h"
#include "kfile.h"
#include "rt-select.h"
#ifdef SCAN
#include "scan.h"
#endif /* SCAN */
#include "univ.h"

void
trn_init()
{
    ;
}

static bool restore_old_newsrc = FALSE;
static bool go_forward = TRUE;

int
main(argc,argv)
int argc;
char* argv[];
{
    bool foundany;
    char* s;

    /* Figure out our executable's name. */
#ifdef MSDOS
    strlwr(argv[0]);
    if ((s = rindex(argv[0],'\\')) != NULL)
	*s = '/';
#endif
    if ((s = rindex(argv[0],'/')) == NULL)
	s = argv[0];
    else
	s++;
#if !THREAD_INIT
    /* Default to threaded operation if our name starts with a 't' or 's'. */
    if (*s == 't' || *s == 's')
	use_threads = TRUE;
    else
	UseNewsSelector = -1;
#endif
    if (*s == 's')
	is_strn = TRUE;
#ifdef USE_TK /*XXX remove this?*/
    if (s[1] == 'k')
	UseTk = 1;
    else
	UseTk = 0;
#endif
    foundany = initialize(argc,argv);

    if (UseNewsrcSelector) {
	multirc_selector();
	finalize(0);
    }

#ifdef FINDNEWNG
    if (find_new_groups()) {		/* did we add any new groups? */
	foundany = TRUE;
	starthere = NULL;		/* start ng scan from the top */
    }
#endif

    if (maxngtodo)
	starthere = NULL;
    else if (!foundany) {		/* nothing to do? */
#ifdef VERBOSE
	if (verbose) {
	    fputs("\
No unread news in subscribed-to newsgroups.  To subscribe to a new\n\
newsgroup use the g<newsgroup> command.\n\
",stdout) FLUSH;
	    termdown(2);
	}
#endif
	starthere = last_ng;
    }

    do_multirc();

    finalize(0);
    /* NOT REACHED */
    return 0;
}

void
do_multirc()
{
    bool special = FALSE;	/* allow newsgroup with no unread news? */
    char mode_save = mode;
    char gmode_save = gmode;

    if (UseUnivSelector) {
	char ch;
	univ_startup();		/* load startup file */
	ch = universal_selector();
	if (ch != 'Q') {
	    /* section copied from bug_out below */
	    /* now write the newsrc(s) back out */
	    if (!write_newsrcs(multirc))
		restore_old_newsrc = TRUE; /*$$ ask to retry! */
	    if (restore_old_newsrc)
		get_old_newsrcs(multirc);
	    finalize(0);
	}
    }

    if (UseNewsgroupSelector) {
      ng_start_sel:
	switch (newsgroup_selector()) {
	case Ctl('n'):
	    use_next_multirc(multirc);
	    end_only();
	    goto ng_start_sel;
	case Ctl('p'):
	    use_prev_multirc(multirc);
	    end_only();
	    goto ng_start_sel;
	case 'q':
	    goto bug_out;
	}
	starthere = ngptr;
	UseNewsgroupSelector = FALSE;
    }

    /* loop through all unread news */
  restart:
    current_ng = first_ng;
    for (;;) {
	bool retry = FALSE;
	if (findlast > 0) {
	    findlast = -1;
	    starthere = NULL;
	    if (lastngname) {
		if ((ngptr = find_ng(lastngname)) == NULL)
		    ngptr = first_ng;
		else {
		    set_ngname(lastngname);
		    set_toread(ngptr, ST_LAX);
		    if (ngptr->toread <= TR_NONE)
			ngptr = first_ng;
		}
	    }
	} else if (starthere) {
	    ngptr = starthere;
	    starthere = NULL;
	}
	else
	    ngptr = first_ng;
	for (;;) {			/* for each newsgroup */
	    if (ngptr == NULL) {	/* after the last newsgroup? */
		set_mode('r','f');
		if (maxngtodo) {
		    if (retry) {
#ifdef VERBOSE
			IF(verbose)
			    printf("\nRestriction %s%s still in effect.\n",
				   ngtodo[0], maxngtodo > 1 ? ", etc." : nullstr) FLUSH;
		 	ELSE
#endif
#ifdef TERSE
			    fputs("\n(\"Only\" mode.)\n",stdout) FLUSH;
#endif
			termdown(2);
		    } else {
#ifdef VERBOSE
			IF(verbose)
			    fputs("\nNo articles under restriction.", stdout) FLUSH;
			ELSE
#endif
#ifdef TERSE
			    fputs("\nNo \"only\" articles.",stdout) FLUSH;
#endif
			termdown(2);
			end_only();	/* release the restriction */
			printf("\n%s\n", msg) FLUSH;
			termdown(2);
			retry = TRUE;
		    }
		}
	    }
	    else {
		bool shoe_fits;	/* newsgroup matches restriction? */

		set_mode('r','n');
		if (ngptr->toread >= TR_NONE) {	/* recalc toread? */
		    set_ngname(ngptr->rcline);
		    shoe_fits = inlist(ngname);
		    if (shoe_fits)
			set_toread(ngptr, ST_LAX);
		    if (paranoid) {
			recent_ng = current_ng;
			current_ng = ngptr;
			cleanup_newsrc(ngptr->rc); /* this may move newsgroups around */
			set_ng(current_ng);
		    }
		} else
		    shoe_fits = TRUE;
		if (ngptr->toread < (special? TR_NONE : ng_min_toread)
		 || !shoe_fits) {		/* unwanted newsgroup? */
		    if (go_forward)
			ngptr = ngptr->next;
		    else {
			ngptr = ngptr->prev;
			if (ngptr == NULL) {
			    ngptr = first_ng->next;
			    go_forward = 1;
			}
		    }
		    continue;
		}
	    }
	    special = FALSE;	/* go back to normal mode */
	    if (ngptr != current_ng) {
		recent_ng = current_ng;	/* remember previous newsgroup */
		current_ng = ngptr;	/* remember current newsgroup */
	    }
    reask_newsgroup:
	    unflush_output();	/* disable any ^O in effect */
	    if (ngptr == NULL) {
		dfltcmd = (retry ? "npq" : "qnp");
#ifdef VERBOSE
		IF(verbose)
		    printf("\n****** End of newsgroups -- what next? [%s] ",
			   dfltcmd);
		ELSE
#endif
#ifdef TERSE
		    printf("\n**** End -- next? [%s] ", dfltcmd);
#endif
		termdown(1);
	    } else {
		ThreadedGroup = (use_threads && !(ngptr->flags&NF_UNTHREADED));
		dfltcmd = (UseNewsSelector >= 0
		  && ngptr->toread >= (ART_UNREAD)UseNewsSelector? "+ynq":"ynq");
#ifdef VERBOSE
		IF(verbose)
		    printf("\n%s %3ld unread article%s in %s -- read now? [%s] ",
			   ThreadedGroup? "======" : "******",
			   (long)ngptr->toread, PLURAL(ngptr->toread),
			   ngname, dfltcmd);
		ELSE
#endif
#ifdef TERSE
		    printf("\n%s %3ld in %s -- read? [%s] ",
			   ThreadedGroup? "====" : "****",
			   (long)ngptr->toread,ngname,dfltcmd);
#endif
		termdown(1);
	    }
	    fflush(stdout);
    reinp_newsgroup:
	    if (special || (ngptr && ngptr->toread > 0))
		retry = TRUE;
	    switch (input_newsgroup()) {
	    case ING_ASK:
		goto reask_newsgroup;
	    case ING_INPUT:
	    case ING_ERASE:
		goto reinp_newsgroup;
	    case ING_ERROR:
		printf("\n%s",hforhelp) FLUSH;
		termdown(2);
		settle_down();
		goto reask_newsgroup;
	    case ING_QUIT:
		goto bug_out;
	    case ING_BREAK:
		goto loop_break;
	    case ING_RESTART:
		goto restart;
#ifdef SUPPORT_NNTP
	    case ING_NOSERVER:
		if (multirc)
		    goto restart;
		goto bug_out;
#endif
	    case ING_SPECIAL:
		special = TRUE;
		break;
	    case ING_NORM:
		break;
	    case ING_DISPLAY:
		newline();
		break;
	    case ING_MESSAGE:
		printf("\n%s\n", msg) FLUSH;
		termdown(2);
		break;
	    }
	}
    loop_break:;
#ifdef SUPPORT_NNTP
	check_active_refetch(FALSE);
#endif
    }

bug_out:
    /* now write the newsrc(s) back out */
    if (!write_newsrcs(multirc))
	restore_old_newsrc = TRUE; /*$$ ask to retry! */
    if (restore_old_newsrc)
	get_old_newsrcs(multirc);

    set_mode(gmode_save,mode_save);
}

int
input_newsgroup()
{
    register char* s;

    ing_state = INGS_DIRTY;
    eat_typeahead();
    getcmd(buf);
    if (errno || *buf == '\f') {
	newline();		/* if return from stop signal */
	return ING_ASK;
    }
    buf[2] = *buf;
    setdef(buf,dfltcmd);
#ifdef VERIFY
    printcmd();
#endif
    if (ngptr != NULL)
	*buf = buf[2];

  do_command:
    go_forward = TRUE;		/* default to forward motion */
    switch (*buf) {
      case 'P':			/* goto previous newsgroup */
      case 'p':			/* find previous unread newsgroup */
	if (!ngptr)
	    ngptr = last_ng;
	else if (ngptr != first_ng)
	    ngptr = ngptr->prev;
	go_forward = FALSE;	/* go backward in the newsrc */
	ing_state = INGS_CLEAN;
	if (*buf == 'P')
	    return ING_SPECIAL;
	break;
      case '-':
	ngptr = recent_ng;		/* recall previous newsgroup */
	if (ngptr) {
	    if (!get_ng(ngptr->rcline,0))
		set_ng(current_ng);
	    addnewbydefault = 0;
	}
	return ING_SPECIAL;
      case 'x':
	newline();
	in_char("Confirm: exit and abandon newsrc changes?", 'A', "yn");
	newline();
	if (*buf != 'y')
	    break;
	printf("\nThe abandoned changes are in %s.new.\n",
	       multirc_name(multirc)) FLUSH;
	termdown(2);
	restore_old_newsrc = TRUE;
	return ING_QUIT;
      case 'q': case 'Q':	/* quit? */
	newline();
	return ING_QUIT;
      case '^':
	if (gmode != 's')
	    newline();
	ngptr = first_ng;
	ing_state = INGS_CLEAN;
	break;
      case 'N':			/* goto next newsgroup */
      case 'n':			/* find next unread newsgroup */
	if (ngptr == NULL) {
	    newline();
	    return ING_BREAK;
	}
	ngptr = ngptr->next;
	ing_state = INGS_CLEAN;
	if (*buf == 'N')
	    return ING_SPECIAL;
	break;
      case '1':			/* goto 1st newsgroup */
	ngptr = first_ng;
	ing_state = INGS_CLEAN;
	return ING_SPECIAL;
      case '$':
	ngptr = NULL;		/* go past last newsgroup */
	ing_state = INGS_CLEAN;
	break;
      case 'L':
	list_newsgroups();
	return ING_ASK;
      case '/': case '?':	/* scan for newsgroup pattern */
#ifdef NGSEARCH
	switch (ng_search(buf,TRUE)) {
	  case NGS_ERROR:
	    set_ng(current_ng);
	    return ING_ASK;
	  case NGS_ABORT:
	    set_ng(current_ng);
	    return ING_INPUT;
	  case NGS_INTR:
#ifdef VERBOSE
	    IF(verbose)
		fputs("\n(Interrupted)\n",stdout) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("\n(Intr)\n",stdout) FLUSH;
#endif
	    termdown(2);
	    set_ng(current_ng);
	    return ING_ASK;
	  case NGS_FOUND:
	    return ING_SPECIAL;
	  case NGS_NOTFOUND:
#ifdef VERBOSE
	    IF(verbose)
		fputs("\n\nNot found -- use a or g to add newsgroups\n",
		      stdout) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("\n\nNot found\n",stdout) FLUSH;
#endif
	    termdown(3);
	    return ING_ASK;
	  case NGS_DONE:
	    return ING_ASK;
	}
#else
	notincl("/");
#endif
	break;
      case 'm':
#ifndef RELOCATE
	notincl("m");
	break;
#endif		    
      case 'g':	/* goto named newsgroup */
	if (!finish_command(FALSE))
	    return ING_INPUT;
	for (s = buf+1; *s == ' '; s++) ; /* skip leading spaces */
#ifdef RELOCATE
	if (!*s && *buf == 'm' && ngname && ngptr)
	    strcpy(s,ngname);
#endif
	{
	    char* _s;
	    for (_s=s; isdigit(*_s); _s++) ;
	    if (*_s)
		/* found non-digit before hitting end */
		set_ngname(s);
	    else {
		int rcnum = atoi(s);
		for (ngptr = first_ng; ngptr; ngptr = ngptr->next)
		    if (ngptr->num == rcnum)
			break;
		if (!ngptr) {
		    ngptr = current_ng;
		    printf("\nOnly %d groups. Try again.\n", newsgroup_cnt) FLUSH;
		    termdown(2);
		    return ING_ASK;
		}
		set_ngname(ngptr->rcline);
	    }
	}
	/* try to find newsgroup */
#ifdef RELOCATE
	if (!get_ng(ngname,(*buf=='m'?GNG_RELOC:0) | GNG_FUZZY))
#else
	if (!get_ng(ngname,GNG_FUZZY))
#endif
	    ngptr = current_ng;	/* if not found, go nowhere */
	addnewbydefault = 0;
	return ING_SPECIAL;
#ifdef DEBUG
      case 'D':
	return ING_ASK;
#endif
      case '!':			/* shell escape */
	if (escapade())		/* do command */
	    return ING_INPUT;
	return ING_ASK;
      case Ctl('k'):		/* edit global KILL file */
	edit_kfile();
	return ING_ASK;
      case Ctl('n'):		/* next newsrc list */
	end_only();
	use_next_multirc(multirc);
	goto display_multirc;
      case Ctl('p'):		/* prev newsrc list */
	end_only();
	use_prev_multirc(multirc);
      display_multirc:
      {
	NEWSRC* rp;
	int len;
	for (rp = multirc->first, len = 0; rp && len < 66; rp = rp->next) {
	    if (rp->flags & RF_ACTIVE) {
		sprintf(buf+len, ", %s", rp->datasrc->name);
		len += strlen(buf+len);
	    }
	}
	if (rp)
	    strcpy(buf+len, ", ...");
	printf("\n\nUsing newsrc group #%d: %s.\n",multirc->num,buf+2);
	termdown(3);
	return ING_RESTART;
      }
      case 'c':			/* catch up */
#ifdef CATCHUP
	if (ngptr) {
	    ask_catchup();
	    if (ngptr->toread == TR_NONE)
		ngptr = ngptr->next;
	}
#else
	notincl("c");
#endif
	break;
      case 't':
	if (!use_threads)
	    printf("\n\nNot running in thread mode.\n");
	else if (ngptr && ngptr->toread >= TR_NONE) {
	    bool read_unthreaded = !(ngptr->flags&NF_UNTHREADED);
	    ngptr->flags ^= NF_UNTHREADED;
	    printf("\n\n%s will be read %sthreaded.\n",
		   ngptr->rcline, read_unthreaded? "un" : nullstr) FLUSH;
	    set_toread(ngptr, ST_LAX);
	}
	termdown(3);
	return ING_SPECIAL;
      case 'u':			/* unsubscribe */
	if (ngptr && ngptr->toread >= TR_NONE) {/* unsubscribable? */
	    newline();
	    printf(unsubto,ngptr->rcline) FLUSH;
	    termdown(1);
	    ngptr->subscribechar = NEGCHAR;	/* unsubscribe it */
	    ngptr->toread = TR_UNSUB;		/* and make line invisible */
	    ngptr->rc->flags |= RF_RCCHANGED;
	    ngptr = ngptr->next;		/* do an automatic 'n' */
	    newsgroup_toread--;
	}
	break;
      case 'h':
	univ_help(UHELP_NG);
	return ING_ASK;
      case 'H':			/* help */
	help_ng();
	return ING_ASK;
      case 'A':
	if (!ngptr)
	    break;
reask_abandon:
#ifdef VERBOSE
	IF(verbose)
	    in_char("\nAbandon changes to current newsgroup?", 'B', "yn");
	ELSE
#endif
#ifdef TERSE
	    in_char("\nAbandon?", 'B', "ynh");
#endif
#ifdef VERIFY
	printcmd();
#endif
	newline();
	if (*buf == 'h') {
#ifdef VERBOSE
	    printf("Type y or SP to abandon the changes to this group since you started trn.\n");
	    printf("Type n to leave the group as it is.\n");
#else
	    printf("y or SP to abandon changes to this group.\n");
	    printf("n to forget it.\n");
#endif
	    termdown(2);
	    goto reask_abandon;
	} else if (*buf != 'y' && *buf != 'n' && *buf != 'q') {
	    fputs(hforhelp,stdout) FLUSH;
	    termdown(1);
	    settle_down();
	    goto reask_abandon;
	} else if (*buf == 'y')
	    abandon_ng(ngptr);
	return ING_SPECIAL;
      case 'a':
#ifndef FINDNEWNG
	notincl("a");
	return ING_ASK;
#else
	/* FALL THROUGH */
#endif
      case 'o':
      case 'O': {
#ifdef FINDNEWNG
	bool doscan = (*buf == 'a');
#endif
	if (!finish_command(TRUE)) /* get rest of command */
	    return ING_INPUT;
	*msg = '\0';
	end_only();
	if (buf[1]) {
	    bool minusd = instr(buf+1,"-d", TRUE) != NULL;
	    sw_list(buf+1);
	    if (minusd)
		cwd_check();
#ifdef FINDNEWNG
	    if (doscan && maxngtodo)
		scanactive(TRUE);
#endif
	    ng_min_toread = *buf == empty_only_char && maxngtodo
			  ? TR_NONE : TR_ONE;
	}
	ngptr = first_ng;	/* simulate ^ */
	if (*msg && !maxngtodo)
	    return ING_MESSAGE;
	return ING_DISPLAY;
      }
      case '&':
	if (switcheroo())	/* get rest of command */
	    return ING_INPUT;	/* if rubbed out, try something else */
	return ING_ASK;
      case 'l': {		/* list other newsgroups */
	if (!finish_command(TRUE)) /* get rest of command */
	    return ING_INPUT;	/* if rubbed out, try something else */
	for (s = buf+1; *s == ' '; s++) ; /* skip leading spaces */
	push_only();
	if (*s)
	    sw_list(s);
	page_start();
	scanactive(FALSE);
	pop_only();
	return ING_ASK;
      }
      case '`':
      case '\\':
	if (gmode == 's')
	    return ING_ERASE;
      ng_start_sel:
	UseNewsgroupSelector = TRUE;
	switch (newsgroup_selector()) {
	  case Ctl('n'):
	    end_only();
	    use_next_multirc(multirc);
	    goto ng_start_sel;
	  case Ctl('p'):
	    end_only();
	    use_prev_multirc(multirc);
	    goto ng_start_sel;
	  case 'q':
	     return ING_QUIT;
	}
	UseNewsgroupSelector = FALSE;
	return ING_ASK;
#ifdef SCAN_ART
      case ';':
#endif
      case 'U': case '+':
      case '.': case '=':
      case 'y': case 'Y': case '\t': /* do normal thing */
      case ' ': case '\r': case '\n':
	if (!ngptr) {
	    fputs("\nNot on a newsgroup.",stdout) FLUSH;
	    termdown(1);
	    return ING_ASK;
	}
	/* CAA: *once*, the char* s was set to an illegal value
	 *      (it seemed to miss all the if statements below)
	 *      Just to be safe, make sure it is legal.
	 */
	s = nullstr;
	if (*buf == '.') {		/* start command? */
	    if (!finish_command(FALSE)) /* get rest of command */
		return ING_INPUT;
	    s = savestr(buf+1);		/* do_newsgroup will free it */
	}
	else if (*buf == '+' || *buf == 'U' || *buf == '='
#ifdef SCAN_ART
		|| *buf == ';'
#endif
	) {
	    *buf = lastchar; /* restore 0200 if from a macro */
	    save_typeahead(buf+1, sizeof buf - 1);
	    s = savestr(buf);
	}
	else if (*buf == ' ' || *buf == '\r' || *buf == '\n')
	    s = nullstr;
	else
	    s = NULL;

      redo_newsgroup:
	switch (do_newsgroup(s)) {
	  case NG_NORM:
	  case NG_NEXT:
	  case NG_ERROR:
	    ngptr = ngptr->next;
	    break;
	  case NG_ASK:
	    return ING_ASK;
	  case NG_SELPRIOR:
	    *buf = 'p';
	    goto do_command;
	  case NG_SELNEXT:
	    *buf = 'n';
	    goto do_command;
	  case NG_MINUS:
	    ngptr = recent_ng;	/* recall previous newsgroup */
	    return ING_SPECIAL;
#ifdef SUPPORT_NNTP
	  case NG_NOSERVER:
	    nntp_server_died(ngptr->rc->datasrc);
	    return ING_NOSERVER;
#endif
	  /* CAA extensions */
	  case NG_GO_ARTICLE:
	    ngptr = ng_go_ngptr;
	    s = savestr("y");		/* enter with minimal fuss */
	    goto redo_newsgroup;
	  /* later: possible go-to-newsgroup */
	}
	break;
      case ':':		/* execute command on selected groups */
	if (!ngsel_perform())
	    return ING_INPUT;
	page_line = 1;
	newline();
	set_ng(current_ng);
	return ING_ASK;
      case 'v':
	newline();
	trn_version();
	return ING_ASK;
      default:
	if (*buf == ERASECH || *buf == KILLCH)
	    return ING_ERASE;
	return ING_ERROR;
    }
    return ING_NORM;
}

#ifdef SUPPORT_NNTP
void
check_active_refetch(force)
bool_int force;
{
    DATASRC* dp;
    time_t now = time((time_t*)NULL);

    for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) {
	if (!ALLBITS(dp->flags, DF_OPEN | DF_ACTIVE))
	    continue;
	if (dp->act_sf.fp && dp->act_sf.refetch_secs
	 && (force || now - dp->act_sf.lastfetch > dp->act_sf.refetch_secs))
	    actfile_hash(dp);
    }
}
#endif

void
trn_version()
{
    page_start();
    sprintf(msg,"Trn version:%s.\nConfigured for ",patchlevel);
#ifdef SUPPORT_NNTP
# ifdef HAS_LOCAL_SPOOL
    strcat(msg,"both NNTP and local news access.\n");
# else
    strcat(msg,"NNTP (plus individual local access).\n");
# endif
#else
    strcat(msg,"local news access.\n");
#endif
    print_lines(msg, NOMARKING);

    if (multirc) {
	NEWSRC* rp;
	newline();
	sprintf(msg,"News source group #%d:\n\n", multirc->num);
	print_lines(msg, NOMARKING);
	for (rp = multirc->first; rp; rp = rp->next) {
	    if (!(rp->flags & RF_ACTIVE))
		continue;
	    sprintf(msg,"ID %s:\nNewsrc %s.\n",rp->datasrc->name,rp->name);
	    print_lines(msg, NOMARKING);
#ifdef SUPPORT_NNTP
	    if (rp->datasrc->flags & DF_REMOTE) {
		sprintf(msg,"News from server %s.\n",rp->datasrc->newsid);
		print_lines(msg, NOMARKING);
		if (rp->datasrc->act_sf.fp) {
		    if (rp->datasrc->flags & DF_TMPACTFILE)
			strcpy(msg,"Copy of remote active file");
		    else
			sprintf(msg,"Local active file: %s",
				rp->datasrc->extra_name);
		}
		else
		    strcpy(msg,"Dynamic active file");
		if (rp->datasrc->act_sf.refetch_secs) {
		    char* cp = secs2text(rp->datasrc->act_sf.refetch_secs);
		    if (*cp != 'n')
			sprintf(msg+strlen(msg),
				" (refetch%s %s)",*cp == 'm'? " if" : ":", cp);
		}
		strcat(msg,".\n");
	    }
	    else
#endif
		sprintf(msg,"News from %s.\nLocal active file %s.\n",
			rp->datasrc->spool_dir, rp->datasrc->newsid);
	    print_lines(msg, NOMARKING);
	    if (rp->datasrc->grpdesc) {
#ifdef SUPPORT_NNTP
		if (!rp->datasrc->desc_sf.fp && rp->datasrc->desc_sf.hp)
		    strcpy(msg,"Dynamic group desc. file");
		else if (rp->datasrc->flags & DF_TMPGRPDESC)
		    strcpy(msg,"Copy of remote group desc. file");
		else
#endif
		    sprintf(msg,"Group desc. file: %s",rp->datasrc->grpdesc);
#ifdef SUPPORT_NNTP
		if (rp->datasrc->desc_sf.refetch_secs) {
		    char* cp = secs2text(rp->datasrc->desc_sf.refetch_secs);
		    if (*cp != 'n')
			sprintf(msg+strlen(msg),
				" (refetch%s %s)",*cp == 'm'? " if" : ":", cp);
		}
#endif
		strcat(msg,".\n");
		print_lines(msg, NOMARKING);
	    }
	    if (rp->datasrc->flags & DF_TRY_OVERVIEW) {
		sprintf(msg,"Overview files from %s.\n",
			rp->datasrc->over_dir? rp->datasrc->over_dir
					     : "the server");
		print_lines(msg, NOMARKING);
	    }
	    if (rp->datasrc->flags & DF_TRY_THREAD) {
		sprintf(msg,"Thread files from %s.\n",
			rp->datasrc->thread_dir? rp->datasrc->thread_dir
					       : "the server");
		print_lines(msg, NOMARKING);
	    }
	    print_lines("\n", NOMARKING);
	}
    }

    print_lines("\
You can request help from:  trn-users@lists.sourceforge.net\n\
Send bug reports, suggestions, etc. to:  trn-workers@lists.sourceforge.net\n",
		NOMARKING);
}

void
set_ngname(what)
char* what;
{
    if (ngname != what) {
	ngname_len = strlen(what);
	growstr(&ngname,&ngnlen,ngname_len+1);
	strcpy(ngname,what);
    }
    growstr(&ngdir,&ngdlen,ngname_len+1);
    strcpy(ngdir,getngdir(ngname));
}

static char* myngdir;
static int ngdirlen = 0;

char*
getngdir(ngnam)
char* ngnam;
{
    register char* s;

    growstr(&myngdir,&ngdirlen,strlen(ngnam)+1);
    strcpy(myngdir,ngnam);
    for (s = myngdir; *s; s++)
	if (*s == '.')
	    *s = '/';
    return myngdir;
}
