/*

  
    
      Expire problem: leaking entries, two things
	1) Check how can leak
	2) check how can self correct
	3) How can add command to make it immediately correct.





New Version plans:
anonymous feature - do anonymous user translations to avoid spam


on linux - make dmulti use pipes instead of tcpip.
xover - split into files of 500 items and then rebuild only broken parts
	and delete by deleting the whole file or maybe re-writing partial ones.
history - 4 files per pile, delete oldest one.
dbi.idx - pre allocate 1/10th of group size chunks.
threading - use a pool of threads to process xover/xhdr/article/head requests
dns lookups - use thread pool
background - Remove all known blocking things that might cause item losss etc.
check_index - takes 7 minutes ? 
mutex timeout - write to a disk buffer when mutex timeout is exceeded for a input thread.
	- record when items are lost, and how many !!!! :-) 
Indexes - should they be loadable in 1K blocks ? 
jpeg picture previews

fix stats to use this on NT The GetThreadTimes function obtains timing information about a specified thread.
BOOL GetThreadTimes(
    HANDLE hThread,	// specifies the thread of interest 
    LPFILETIME lpCreationTime,	// when the thread was created 
    LPFILETIME lpExitTime,	// when the thread was destroyed 
    LPFILETIME lpKernelTime,	// time the thread has spent in kernel mode 
    LPFILETIME lpUserTime 	// time the thread has spent in user mode 
   );
-----------------------------------------------------------------



New feature:
	if (sponsormode)
		Once a week we fetch a file from netwinsite.com/dnews_adds/advert.dat
		We store it in file_work
		We then fetch all files it references, and store them in file_work[adds]


File format:

freq=10% files=ad1.txt,ad2.txt groups=rec.*,alt.*,!*usenet*
freq=20% files=ad4.txt groups=comp.*
freq=30% files=rec.txt groups=rec.*
freq=30% files=funny1.txt:3,funny2.txt:1 groups=rec.humor




	Test 'threaded input' processing
	code 'parts' to only insert into index after parts complete
	code 'threaded' disk writing.

On some/most machines 'dbadd' is the main problem
On those machines the current part system would kill it.
Go with original plan.




	* Make sockets  not inherit on NT
	* Check pause and unpause are flushing the
	  right indexes.
	* test on dmulti and readonly servers.
	DONE * Add stats for time spent doing patches
	DONE * tellnews buckets_sort
	DONE * tellnews pile_sort n
	DONE * In status show number of entries in que.
*/

/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include "dnews.h"
#include "str.h"
#include "hist.h"
#include "nntp.h"
#include "chan.h"
#include "feed.h"
#include "item.h"
#include "lib.h"
#include "group.h"
#include "emsg.h"
#include "db.h"
#include "ftslib.h"
#include "gstat.h"
#include "dfilter.h"
#include "suck.h"
#include "sponser.h"
#include "xhash.h"
#ifdef KEYLIB
#include "keylib.h"
#endif

#ifdef xVMS
void lib_atexit( void fn(void));
#define atexit lib_atexit
#endif
void mutex_init(void);
void pull_init(void);
void spawn_exit(void);
void split_find(void);
void xov_total_find(void);
void dbi_setfx(int x);
int part_init(void);
void strstr_test(void);
int fx_setmax(int x);
char *get_fts_dir(void);
void debug_init(void);
void slave_set(int n);
void filter_init(void);
void dbi_setmap(int x);
void dnews_exith(void);
void sig_init(void);
void tomail_init(void);
void getmail_init(void);
int lib_setendian(void);
void dnews_flush(void);
#define LOCKFILE file_work("dnews.lock")
char dnews_conf[BFSZ];
#ifdef WIN32
void svc_exiting(void);
void svc_exit(void);
#endif



void test_strncmp(void)
{
	char bf[100];
	char bf2[100];
	char bf3[100];

again:
	printf("string1 "); gets(bf);	
	printf("string1 "); gets(bf2);	
	printf("n "); gets(bf3);	
	printf("result %d\n",strncmpnc(bf,bf2,atoi(bf3)));
	goto again;
}

static int nwthread;
#ifdef WIN32x
#include <windows.h>

WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,int nCmdShow)
{
 
        char *argv[1];

        main(0,argv);
}

#endif



static char crash_file[BFSZ];


#ifdef WIN32
#include <process.h>
#include <sys/stat.h>
int crash_thread(void *x)
{
	struct _stat buffer;
	for (;TRUE;) {
		Sleep(5000);
		if (_stat( crash_file, &buffer )==0) {
			char *s;
			s = NULL;
			*s = 1;
			break;
		}
	}
	return 0;
}
int start_crash_thread(void)
{
	int h;
	h = _beginthread((void (*)(void *))crash_thread,64000,(void *) NULL);
	return TRUE;
}
#endif



void svc_init(char *);
#ifdef WIN32
int ServiceMain(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
        char p1[200];
	char *s;
	char bf[BFSZ];
	int i;
        int pchan = -1;
	static int running;
#ifdef Mac
	Handle	menuBar;
#endif

	if (running) return 0; /* Dont allow the service to start itself twice */
	running = TRUE;

#ifdef Mac
#include <Memory.h>

// needed if we are going to run not in background - but kills it if we do run background only*/
// if we want a mac window rather than pure background
// use folowing MacWindow define and chaange background only flag in compiler
	SetApplLimit(GetApplLimit()-300000);
#endif
#ifdef MacWindow
	InitGraf((Ptr) &qd.thePort);
	InitFonts();
	InitWindows();
	InitMenus();
	InitDialogs(nil);
	TEInit();
	InitCursor();

	//Setup our menus
	#define rMenuBar 128
	menuBar = GetNewMBar(rMenuBar);			/* read menus into menu bar */
	//if ( menuBar == nil )
	//			DieWithError(eNoMemory,0);
	SetMenuBar(menuBar);					/* install menus */
	DisposeHandle(menuBar);
	AppendResMenu(GetMenuHandle(128), 'DRVR');	/* add DA names to Apple menu */
	DrawMenuBar();
#endif
        if ((argc==2) || (argc==3)) {
           ncpy(p1,argv[1],199);
           if (strncmp(p1,"-s",2)==0)  {
		slave_set(atoi(argv[2]));
           } else if (strncmp(p1,"-p",2)==0)  {
                   pchan = atoi(argv[1]+2);
           } else if (strncmp(p1,"-test_threads",13)==0)  {
                   thin_test_threads(atoi(argv[2]));
		   return 0;
           } else if (strncmp(p1,"-threads",8)==0)  {
                   thin_test_threads(atoi(argv[2]));
		   return 0;
           } else if (strncmp(p1,"-test",2)==0)  {
		printf("Dnews running normally\n");
		if (argc==3) strcpy(dnews_conf,argv[2]);
           } else if (strncmp(p1,"-expire",7)==0) {
                set_expire_mode(TRUE);
	   } else if (strncmp(p1,"-config",3)==0) {
		   ;
           } else if (strncmp(p1,">",1)==0)  {
			/*Ignore piping that faield*/ ;
           } else if (strncmp(p1,"-",1)==0)  {
		printf("Unknown command switch %s\n",p1);
           } else {
                strcpy(dnews_conf,p1);
           }
        }
	for (i=1; i<argc; i++) {
		if (strncmp(argv[i],"-config",3)==0) {
			strcpy(dnews_conf,argv[++i]);
	        } else if (strncmp(argv[i],"-p",2)==0)  {
			pchan = atoi(argv[i]+2);
	        } else if (strncmp(argv[i],"-s",2)==0)  {
			slave_set(atoi(argv[++i]));
	        } else if (strncmp(argv[i],"-test_threads",13)==0)  {
	        } else if (strncmp(argv[i],"-threads",8)==0)  {
	        } else if (strncmp(argv[i],"-test",2)==0)  {
	        } else if (strncmp(argv[i],"-expire",7)==0)  {
	                set_expire_mode(TRUE);
	        } else if (strncmp(argv[i],">",1)==0)  {
			/*Ignore piping that faield*/ ;
	        } else if (strncmp(argv[i],"-",1)==0)  {
			printf("Unknown command switch %s\n",argv[i]);
		} else {
			strcpy(dnews_conf,argv[i]);
		}
	}

/*	test_strncmp();*/

        debug_init();
        if (!is_readonly()) if (!get_expire_mode()) filter_init(); /* brian */

#ifdef WIN32
        if (!(is_readonly()||get_expire_mode())) { 
         if (lib_locked()) {
		printf("Dnews must be running already, can't open lock file\n"); 
	        svc_init(argv[0]); /* If we are a service try and close nicely */ 
		svc_exit();
		return 0;
	 } /* Don't start if lock file is locked */
	}
#endif


#ifdef NETWARE
        wmsg("init: nntp server %s %s %s starting %s\n",
                        DNEWS_NAME,DNEWS_VERSION,SYSTEM_TYPE,debug_names[debug_lvl]);
#else
        imsgf("init: nntp server %s %s %s starting %s\n",
                        DNEWS_NAME,DNEWS_VERSION,SYSTEM_TYPE,debug_names[debug_lvl]);
#endif
	if (is_readmaster()) imsg("Read Only Master process running\n");
	else if (is_slave() && is_reader()) imsg("Read Only Slave server running\n");
	else if (is_slave()) imsg("Slave server running\n");
	else if (is_master()) imsg("Master dnews process running\n");
        else if (get_expire_mode()) imsg("DNews expire process running\n");
	else imsg("Normal dnews process running\n");
	emsg_flush();
	strstr_test();
	mutex_init();
	lib_grow_memory();
        lib_tzinit();
	ncmd_settiming(config_true(C_TIMING));

	if (is_mastermaster()) {
		if (config_true(C_PRIORITY_BOOST)) {
			lib_boost_thread();
		}
	}
        if (pchan>=0) imsg("init: Using pre-opened channel %d \n",pchan);
        imsgf("init: Loading history database, may take some time \n");
	db_malloc_groups();
	
	if (!is_readonly()) {
	    remove(file_work("tellmast.dat"));
	}

        if (argc>=2) imsg("ARgs {%s} \n",argv[0]);
	for (i=0; i<argc; i++) imsg("arg[%d]=(%s)\n",i,argv[i]);

#define SKIP if (1==2)

        if (!get_expire_mode()) {
#ifdef WIN32
            svc_init(argv[0]); 
#endif
            if (getenv("PGPPATH")!=NULL) imsg("Env PGPPATH=%s\n",getenv("PGPPATH"));
            if (getenv("TZ")!=NULL) imsg("Env TZ=%s\n",getenv("TZ"));
            nntp_startup();
            access_read();
             dfilter_emsg(imsg);
            feed_load();
        }

#ifdef NETWARE
           nwthread = GetThreadGroupID();
#endif
        if (!get_expire_mode()) {
            imsgf("init: Loading NNTP  %d\n",C_NT_USER_BATCH);
            conf_getpass(); /* Make sure tellnews.pass exists*/ 
            nntp_init(pchan);
        }
	dom_reload();
#ifdef KEYLIB
	keylib_imsg(imsg);
#endif
	nntp_set_welcome(config_get(C_WELCOME));
	nntp_setnodup(config_true(C_DONT_DUP));
	nntp_set_dtable(atoi(config_get(C_SET_DTABLE)));

        imsgf("init: Reading history index... \n");
        if (!get_expire_mode()) {
		if (!is_readonly()) lib_mkfile(file_work("dfeed.exit"));
	}
	dbi_setmap(config_true(C_INDEX_MAP));
	fx_setmax(atoi(config_get(C_INDEX_NBUFFER)));
	if (config_true(C_SLAVE_BYFILE)) imsg("dmulti: slave_byfile TRUE\n");
	if (config_true(C_INDEX_CHANGES)) imsg("dmulti: index_changes TRUE\n");

	dbi_setfx(atoi(config_get(C_INDEX_BUFFER)));
	srand(lib_gmtime());
	hist_init();
	big_setgrow(atoi(config_get(C_BIG_NGROW)));

	expire_load();
	
	if (config_true(C_HIDE_VERSION)) nntp_set_hide_ver(TRUE);

	nntp_set_small_fast(atoi(config_get(C_SMALL_FAST)));
	nntp_set_small_speed(atoi(config_get(C_SMALL_SPEED)));
	imsg("small_fast setting is %d bytes\n",atoi(config_get(C_SMALL_FAST)));
        imsgf("init: Reading groups... \n");
        group_read();
        diskuse_get( );
        if (!get_expire_mode()) nocem_load();
        imsgf("init: Initializing Database... \n");
 	s = config_get(C_XOVER_DIR);
	if (strlen(s)==0) s = config_get(C_WORKAREA);
	xhash_init(s);

        db_init();
        item_init();
#ifdef WIN32
		ncpy(crash_file,file_config("crash.now"),BFSZ-1);
		remove(crash_file);
		imsgf("To cause a crash create the file (%s)\n",crash_file);
		start_crash_thread();
#endif
        xov_total_find();
        if (!get_expire_mode()) {
            imsgf("init: Reading Full Text Search dictionary\n");
            fts_init(get_fts_dir(),TRUE);
        }
        imsgf("init: Reading split rules\n");        
	split_find();
	imsg("MAX_STRLEN IS (%d) item_max=%d real_max=%d\n",MAX_STRLEN,atoi(config_get(C_ITEM_MAX)),atoi(config_get(C_REAL_MAX)));

        if (!get_expire_mode()) { /* brian */
	    int rn;
            wcache_init();
            hcache_init();
            users_touch();
	    rn = atoi(config_get(C_READONLY_N));
	    if (rn>0) lprintf(bf,BFSZ-1,"gstat_r%d",rn); else strcpy(bf,"gstat");
            gstat_init(file_work(bf),(int (*)(const char *,...) )imsg);
	    if (!is_readonly()) {
                    if (!config_true(C_NOCONTROL)) {
                     if (group_find("control")<0) group_new("control","me","y","");
                     if (group_find("control.cancel")<0) group_new("control.cancel","me","y","");
		    }
			pull_init();
			part_init();
			if (group_n()<8) {
		                wmsg("group: Not many groups found, trying to get them from the feeder\n");
		                wmsg("If this fails, then use command: tellnews getgroups upstream.news.server\n");
		                wmsg("to automatically fetch groups from another news server\n");
		                suck_update_groups_nq(0);
		        }


            }    
        } /* brian */


/* DISABLE exit handler so debugger will catch the error line.*/
/* #define DEBUGGING_NOW TRUE  */

#ifdef WIN32
#define DEBUGGING_NOW TRUE
#endif

#ifndef DEBUGGING_NOW

		if (!config_true(C_ATEXIT_DISABLE)) {
#ifdef USEONEXIT
        on_exit(dnews_exith); 
#else
#ifndef NETWARE
        atexit(dnews_exith); 
#endif
#endif

        }
        sig_init();

#endif
        if (!get_expire_mode()) {
           dnews_getkey(bf);
	   sponser_init();
#ifdef NETWARE
           wmsg("init: Server initialized and running, %d groups \n",group_n());
#else
           imsgf("init: Server initialized and running, %d groups \n",group_n());
#endif
        }
        emsg_flush();
	tomail_init();
	getmail_init();
        /* check if this an expire only run */
        if (get_expire_mode()) {
	    remove(file_work("tellmast.dat"));
	    suck_expire_only();
            return 0;
        }
        if (is_slave()) {
		slave_open(); /* Open slave connection to master */
	}
	if (is_mastermaster()) {
		pf_clear();
	}
	if (!is_slave()) nntp_listen_hostport(config_get(C_LISTEN_EXTRA));

	if (config_true(C_SET_WORKINGSET)) {
		set_workingset();
	}


        nntp_wait();
        wmsgf("init: Server Exiting for some reason \n");
        return 0;
}
char *main_msg(void)
{
	return "DNEWS is running ok, switch back to DNEWS Manager";
}		
void dnews_exith(void)
{
        dnews_ex("Exit Handler");
}
void ras_exit(void);


int spawn_expire_dieing(char *reason);

void dnews_exit(char *reason, char *file, int line) /* __FILE__,__LINE__) */
{
        static int done_exit;
	char bf[BFSZ];
        if (done_exit) return;
        done_exit = TRUE;
        if (config_true(C_JUST_DIE)) return ;

	lprintf(bf,200,"%.100s %.100s:%d",reason,file,line);
	spawn_expire_dieing(bf);
        suck_expire_exit();
#ifdef WIN32
        svc_exiting();
#endif
#if __profile__
		ProfilerDump("\pDNews.prof");
#endif
	emsg("SHUTDOWN: dnews_exit %s  from %s:%d \n",reason,file,line);
#ifdef MEMDEBUG
        mem_check();
#endif
        feed_exit();
        if (!get_expire_mode()) {
            group_write();
            group_names_write();
        }
        dnews_flush();
        if (!get_expire_mode()) {
		if (!is_readonly()) lib_mkfile(file_work("dfeed.exit"));
	}

	db_exit();
        ras_exit();

        if (!get_expire_mode()) {
            imsgf("Writing xover cache\n");
            wcache_write_all();
        }
        spawn_exit();
	filter_close();
        if (!get_expire_mode()) {
		if (!is_readonly()) part_index_rewrite();
	}
	nntp_exit();
	ncmd_cleanup();
	mem_exit();
	emsg("Exit completed\n");
        emsg_flush();
#ifdef WIN32
        svc_exit();
#endif
        exit(1);
}
static int dienow=FALSE;
static int diepending=FALSE;
void dnews_nwexit(void)
{
	diepending = TRUE;
	for (;!dienow;) {
		lib_sleep(1); /* Wait for exit thread to finish */
	}
	exit(0);
}
#ifdef NETWARE
void sig_exit_nw(int x)
{
	int old;
        signal(SIGTERM,SIG_DFL);
	old = SetThreadGroupID(nwthread);
	suck_setexit();
	for (;!diepending;) lib_sleep(1); /* Wait for main thread to stop */

	feed_exit();
        group_write();
	group_names_write();
        dnews_flush();
	db_exit();
	ras_exit();
	imsgf("Writing xover cache\n");
	wcache_write_all();
	spawn_exit();
	nntp_exit();
	mem_exit();
        emsg_flush();

	SetThreadGroupID(old);
	dienow = TRUE;
}
#endif

void dnews_flush(void)
{
	xhash_close();
        item_flush();
        feed_flush();
	feed_flushuucp();
/*	dmsg("init password \n",conf_getpass());*/
        hist_flush();
        hist_close(); 
        group_used_flush();
        emsg_flush();
}

#ifdef NETWARE
#define MISSING_SIGNALS TRUE
#endif
#ifdef WIN32
#define MISSING_SIGNALS TRUE
#endif
#ifdef Mac
#define MISSING_SIGNALS TRUE
#endif
#ifdef OS2
#define MISSING_SIGNALS TRUE
#endif

void sig_hup(int x);
void sig_exit(int x);
void sig_init(void)
{
#ifndef MISSING_SIGNALS
        signal(SIGHUP,sig_hup);
        signal(SIGTERM,sig_exit);
#endif
#ifdef NETWARE
        signal(SIGTERM,sig_exit_nw);
#endif

#ifdef unix
#ifndef MISSING_SIGNALS
        signal(SIGPIPE,SIG_IGN);
        signal(SIGTRAP,sig_exit);
        signal(SIGBUS,sig_exit);
        signal(SIGQUIT,sig_exit);
#endif
#ifdef SIGIOT 
        signal(SIGIOT,SIG_IGN);
#endif
		if (!config_true(C_ATEXIT_DISABLE)) {
        	signal(SIGSEGV,sig_exit);
		}
        signal(SIGFPE,SIG_IGN); 
#endif
}
void sig_hup(int x)
{
        emsg("dnews: Signal %d SIGHUP \n",x);
        dnews_flush();
        config_load();
        sig_init();
}
void trace_flush(void);
void suck_setexit(void);
void sig_exit(int x)
{
#ifdef unix
#ifndef MISSING_SIGNALS
        signal(SIGHUP,SIG_DFL);
        signal(SIGTERM,SIG_DFL);
#endif
#ifndef MISSING_SIGNALS
        signal(SIGABRT,SIG_DFL);
        signal(SIGIOT,SIG_DFL);
        signal(SIGPIPE,SIG_DFL);
        signal(SIGTRAP,SIG_DFL);
        signal(SIGBUS,SIG_DFL);
        signal(SIGQUIT,SIG_DFL);
#endif
#endif   

#ifdef SIGKILL
   if ((x==SIGKILL) || (x==SIGTERM)){
	emsg("System shutdown causing dnews to exit, signal %d\n",x);
	dnews_flush();
	exit(0);
   }
#endif
   if (config_true(C_JUST_DIE)) {
	fflush(NULL);
	exit(0);
   }
   trace_show();
#ifdef DOTHREADS
   emsg("dnews: Signal %d causing exit th=%d\n",x,th_id()); 
#else
   emsg("dnews: Signal %d causing exit \n",x); 
#endif
   emsg("dnews: System Info: DNEWS %s %s\n",DNEWS_VERSION,SYSTEM_TYPE);

   if (x==11) emsg("dnews: Send the last 1000 lines of this log to netwin@netwinsite.com\n"); 
   if (x==11) emsg("dnews: Make sure you cut from well above this line so we can see the context\n"); 
   if (x==11) emsg("dnews: NetWin staff will be VERY keen to assist you finding the problem\n"); 
   trace_flush();
   lib_logmem();
#ifdef MEMDEBUG
   mem_check();
#endif
   dnews_flush();
/*   trace_email(); */
   abort();
}
#ifdef WIN32
#define REALKEY TRUE
#endif
#ifdef NETWARE
#define REALKEY TRUE
#endif
#define REALKEY TRUE

#define DNEWS_KEYFILE file_kfile()
static int done_reg,got_key;
static char exx[200];
int dnews_getkey(char *ex)
{
        FILE *f;
        char bf[BFSZ];

        strcpy(ex,exx);
#ifdef REALKEY
        return nt_getkey(ex,0);
#endif

        if (done_reg) return got_key;
        done_reg = TRUE;

        ncpy(bf,config_get(C_MYNAME),BFSZ-1); strlwr(bf);
        if (strstr(bf,".cri.")!=NULL) { got_key = TRUE; goto ret; }
        f = fopen(DNEWS_KEYFILE,"r"); 
        if (f==NULL) goto ret;
        fgets(bf,BFSZ-10,f);
        fclosep(f);
        if (dnews_checkkey(atoi(bf))) got_key = TRUE;
ret:
        if (got_key) strcpy(exx,""); else strcpy(exx,"Trial UNLICENSED copy of DNEWS");
        strcpy(ex,exx);
        return got_key;
}

int dnews_setkey(char *s)
{
        FILE *f;
        int n = atoi(s);
#ifdef REALKEY
        return nt_setkey(s);
#endif

        if (dnews_checkkey(n)) {
                f = fopen(DNEWS_KEYFILE,"w");
                if (f==NULL) {filemsg("%s\n",DNEWS_KEYFILE); return FALSE;}
                fprintf(f,"%d\n",n);                            
                fclosep(f);
                strcpy(exx,"");
                got_key = TRUE;
                return got_key;
        }
        return FALSE;
}
int dnews_checkkey(int x)
{
        if ((x>550000) && (x<590000)) {
                if ( (x % 5) == 0) return TRUE;
        }
        return FALSE; 
}
void debug_init(void)
{
        int i; 
       strcpy(debug_str,config_getu(C_DEBUG));
        if (strlen(debug_str)==0) {
                strcpy(debug_str,"control:db:cancel:file:lib:ras:,warn:,fatal:,post:,chan:,feed:,hist:,nntp:,str:,err:,debug:,expire:,info:,item:,group:,suck:,cmd:,ncmd:,init:,db:,mem:,pull:,");        
        }
        debug_lvl = 0;
        for (i=0; i<5; i++) if (strcmp(debug_names[i],config_get(C_LOGLVL))==0) {
                debug_lvl = i;
        }
}
void debug_setlvl(char *s)
{
        int i;
        strlwr(s);
        for (i=0; i<5; i++) if (strcmp(debug_names[i],s)==0) {
                debug_lvl = i;
        }
}
int dnews_memdebug(void)
{
	FILE *f;
	char *s;
	char bf[BFSZ];

	f = fopen(file_dnews_config(),"r");
	if (f==NULL) return FALSE;
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-10,f)==NULL) break;
		strlwr(bf);
		s = strtok(bf," \t");
		if (s==NULL) continue;
		if (strcmp(s,"debug_mem")==0) {
			s = strtok(NULL," \t\n\r");
			if (s==NULL) continue;
			if (strcmp(s,"true")==0) {fclosep(f); return TRUE;}
		}
	}
	fclosep(f);
	return FALSE;
}
