/*
     ATP QWK MAIL READER FOR READING AND REPLYING TO QWK MAIL PACKETS.
     Copyright (C) 1992  Thomas McWilliams 
	 Copyright (C) 1990  Rene Cougnenc
	 
     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.

*/	 


/*
 * Change Log: $Log: readlib.c,v $
 * Revision 1.400  1992/11/28  09:54:22  root  second release
 *
 * Revision 1.310  1992/07/08  23:15:14  root
 * first release -- minor bug fix in read.c
 *
 * Revision 1.30  1992/07/05  15:36:19  root first release of ATP
 *
 * Revision 1.2  1992/04/19  12:51:12  root 1st semifunctional UNIX version.
 *
 */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#ifdef unix
#include <unistd.h>
#else
#include <io.h>
#include <dir.h>
#endif

#include "system.h"
#include "ansi.h"
#include "makemail.h"
#include "qlib.h"
#include "reader.h"
#include "readlib.h"

extern const unsigned char codelu[] ;
extern const unsigned char code7bit[] ;

static void Initials(const byte * str, byte * dst, const int len) ;
static void RefMessage(FILE *fp, struct MsgHeaderType *Msg ) ;
static int  affect(char *var, const char *sign, char *value, char *value2, char *aLine );
static void PrePath(char *path);

/*
 * Creates the Home Pathname. In the Unix version we look for for the
 * environment variable ATP. If that is not found we look for the HOME
 * variable and make our home path with that. If that fails we use the
 * current working directory as out home path. If that fails, we exit after
 * printing an error message. Note that Unix doesn't pass the path in
 * argv[0], only the name of the program. The MS-DOS version assumes that
 * argv[0] contains the current path. MakeHomePath is invoked from main() and
 * is passed argv[0].
 */

void
MakeHomePath(void)
{
	int             i;
	char           *pt;

	if ((pt = (char *) getenv("ATP")) != NULL) {
		strcpy(HomePath, pt);
		PrePath(HomePath);
	} else if ((pt = (char *) getenv("HOME")) != NULL) {
		strcpy(HomePath, pt);
		PrePath(HomePath);
	} else if ((pt = getcwd(HomePath, MAXPATHS)) != NULL) {
		PrePath(HomePath);
	} else {
		printf("\nERROR: MakeHomePath() module readlib.c\n");
		return;
	}

	i = strlen(HomePath);
	while (i) {
		if (HomePath[i] == SEP || HomePath[i] == ':' ||
		    HomePath[i] == SEPDOS)
			break;
		HomePath[i] = '\0';
		i--;
	}

	pt = HomePath;
	while (*pt) {
		if (*pt == SEPDOS)
			*pt = SEP;
		pt++;
	}
}


/*
 * Read the configuration file
 *
 */

int
ReadConfig(void)
{
	char            Path[MAXPATHS], RCLine[MAXPATHS], var[50], sign[50], value[50], value2[50];
	int             count = 0, donehere = FALSE;
	FILE           *cfg;

	printf("%s...\n", txt[53]);	/* "Reading config file" */
    speller[0] = 0 ;
	strcpy(OrigTag, " \n"); /* blank tagline */
	strcpy(UserTag, "  \n");
	strcpy(qwklist, "ls *.q* ");	/* initialize qwklist command */
	strcpy(Path, HomePath);
	strcat(Path, ".");
	strcat(Path, CONFIG_FILE);
	if ((cfg = fopen(Path, "rb")) == NULL) {	/* try to open cfg "dot" file */
		strcpy(Path, HomePath);
		strcat(Path, CONFIG_FILE);
		if ((cfg = fopen(Path, "rb")) == NULL) {	/* try again non-dot file     */
			/* "Unable to open config file" */
			printf("\n%s %s or .%s ! \n", txt[52], Path, CONFIG_FILE);
			return (ERROR);
		}
	}
	while (fget(RCLine, 255, cfg)) {
		count++;
		if (RCLine[0] != '#' &&  ( strlen(RCLine) >= (unsigned) 2) ) {
			var[0] = sign[0] = value[0] = value2[0] = '\0' ;	
			sscanf(RCLine, "%s %s %s %s", var, sign, value, value2);
			if ((donehere = affect(var, sign, value, value2, RCLine )) == ERROR) {
				/* "error in config file in line..." */
				printf("%s %s %s %d\n", txt[54], Path, txt[55], count);
				printf("%s %d : %s\n", txt[56], count, RCLine);
				return (ERROR);
			} else if (donehere == DONE)
				break;
		}
	}
	return (OK);
}




/*
 * Affects known configuration variables to their global values. Returns
 * ERROR on an invalid line. Ignores unknown variables.
 */

static int
affect(char *var, const char *sign, char *value, char *value2, char *aLine )
{
	char           *tptr;
	int             tmpint ;
	strlwr(var);
	if (strcmp(sign, "="))
		return (ERROR);

	if (!strcmp(var, "editor") && value[0] ) {
		if ((tptr = strstr(aLine, " = " )) != NULL){
			if ((tptr = strstr(tptr, value)) != NULL){
				strcpy(Editor, tptr );
				return (OK);
			}
			else
				return (ERROR);
		}
		
	}
	if (!strcmp(var, "mail")) {
		if (value[0] != SEP) {
			getcwd(MailPath, MAXPATHS);
			PrePath(MailPath);
        } 
		strcpy(MailPath, value);
		PrePath(MailPath);
		return (OK);
	}
	if (!strcmp(var, "reply")) {
		strcpy(ReplyPath, value);
		PrePath(ReplyPath);
		return (OK);
	}
	if (!strcmp(var, "archiver") && value[0] ) {
		if ((tptr = strstr(aLine, " = " )) != NULL){
			if ((tptr = strstr(tptr, value)) != NULL)
				strcpy(Archiver, tptr);
		}
		else
			strcpy(Archiver, "zip");
		return (OK);
	}
	if (!strcmp(var, "unarchiver") && value[0] ) {
		if ((tptr = strstr(aLine, " = " )) != NULL){
			if ((tptr = strstr(tptr, value)) != NULL)
				strcpy(UnArchiver, tptr);
		}
		else
			strcpy(UnArchiver, "unzip");
		return (OK);
	}
	if (!strcmp(var, "ansi")) {	/* Starting in ansi mode or not... */
		strlwr(value);
		ansi = strcmp(value, "on") ? 0 : 1;
		return (OK);
	}
	if (!strcmp(var, "user")) {	/* Get User First & Last Name      */
		sprintf(UserName1, "%s %s", value, value2);
		strcpy(UserName, UserName1);
		return (OK);
	}
	if (!strcmp(var, "tagline")){
		if ( value[0] && sign[0] == '=') {	/* Get persistent tagline */
			tptr = strchr(aLine, '=') ;
			tptr++;
			tptr++;
			strcpy(OrigTag, tptr);	/* Copy the Original tagline from config */
			strcat(OrigTag, "\n");
			return (OK);
		}else{
			strcpy(OrigTag, " \n"); /* blank tagline */
			return (OK) ;
		}
	}
	if (!strcmp(var, "tagstyle")) {	/* default tag style Fido or PCB */
		strupr(value);
		if (!strcmp(value, "FIDO")) {
			fido = TRUE;
			strcpy(CurTag, FidoTag);
		}
		else{
			fido = FALSE;
			strcpy(CurTag, TagLine);
		}
		return (OK);
	}
	if (!strcmp(var, "autotag")) {	/* default tag style Fido or PCB */
		strupr(value);
		if (!stricmp(value, "OFF")) {
			autotag = FALSE ;
		}
		return (OK);
	}
	if (!strcmp(var, "graphics")) {	/* default tag style Fido or PCB */
		strupr(value);
		if (!stricmp(value, "ON")) {
			graphics = TRUE ;
		}
		return (OK);
	}
	if (!strcmp(var, "charset")) {	/* default tag style Fido or PCB */
		strupr(value);
		if (!strnicmp(value, "iso", 3) || !strnicmp(value, "lat", 3)) {
			charset = ISOLAT1 ;
		} else if (!strnicmp(value, "dos", 3) || !strnicmp(value, "msd", 3)) {
			charset = CHRDOS  ;
		} else{
			charset = CHR7BIT  ;
		}
		return (OK);
	}
	if (!strcmp(var, "taglist")) {	/* signal that old atprc was found */
		printf("\n\n\n\07 READ THE DOCS: you must update your atprc file!\n\n");
		printf("\07 \"taglist\"  not valid opton for this release. \n");
		printf("\07 Taglines are now stored in textfile \"taglines.atp\".\n\n");
		return (ERROR);
	}
	if (!strcmp(var, "screenlen")) {	/* default screenlength */
		if (Numeric(value)) {
			tmpint = atoi(value);
			tmpint = (tmpint < 0) ? -tmpint : tmpint;
			if (tmpint > 2 && tmpint < 301)
				SCREENLINES = tmpint;
		}
		return (OK);
	}
	if (!strcmp(var, "qlist")) {	/* get command for listing packets */
		if (value[0])
			strcpy(qwklist, strstr(aLine, value));
		return (OK);
	}
	if (!strcmp(var, "speller")) {
		if ((tptr = strstr(aLine, value)) != NULL)
			strcpy(speller, tptr);
		else
			strcpy(speller, "echo 'speller not in configuration file'\n");
		return (OK);
	}
return(OK);
}




/*
 * Adds the last separator to a pathname. Must be room enough for that in the
 * string !
 */

static void
PrePath(char *path)
{
	int             l;

	l = strlen(path);
	if (path[l - 1] != SEP && path[l - 1] != SEPDOS) {
		path[l] = SEP;
		path[l + 1] = EOS;
	}
}



/*
 * Builts the full work directory pathname, and Deletes all files in the work
 * directory.
 *
 * ( System dependants functions are in system.c )
 */

void
Clean(void)
{
	strcpy(WorkPath, HomePath);
	strcat(WorkPath, WORK_DIR);

	if (access(WorkPath, F_OK ))
		my_mkdir(WorkPath);
	else {
		printf("%s %s...\n", txt[57], WorkPath);	/* "Cleaning" */
		Erase(WorkPath);
	}
}


/*
 * FGET : Get a string like fgets, without any space or lf on the right.
 */

char *
fget(char *s, int n, FILE * fp)
{
	int             ch1 , ct = 0;
	char            tbuf[300];
	char           *p2;

	if (n > 300)
		return (NULL);

	p2 = tbuf;
	while (ct < n - 2 && ct < 300) {
		ch1 = fgetc(fp);
		if (ch1 == EOF || ch1 == '\n')
			ct = 300;
		else if (ch1 != '\r') {
			*p2 = ( char ) ch1;
			ct++;
			p2++;
		}
	}
	*p2 = '\0';
	StripSpaceR(--p2);
	while (ct < 300) {
		ch1 = fgetc(fp);
		if (ch1 == EOF)
			break;
		ct++;
		if (ct == 300)
			return (NULL);
		else if (ch1 == '\n') {
			ct = 300;
		}
	}
	strcpy(s, tbuf);
	if (ch1 == EOF)
		return (NULL);
    else
		return ( s );
}

/* ------------------------------------------------------------------- */
/*
 * Get conference, open global files etc...
 */

int
GetConf(const int num)
{				/* expects the raw array index as argument */
	char            tmp[MAXPATHS];
	struct stat     stats;

	sprintf(tmp, "%s%s%c%d.idx", HomePath, CurBoard, SEP, ConfNumbers[num]);
	if (FilesOpen) {
		fclose(fidx);
		fclose(fmsg);
		FilesOpen = FALSE;
	}
	if (access(tmp, F_OK )) {	/* File does not exists, no mail. */
		red();
		printf("%s %s\n", txt[44], tmp);	/* "No mail on conf" */
		return (ERROR);
	} else {		/* Ok file exists, read it . */
		if ((fidx = fopen(tmp, "r+b")) == NULL){
			printf("%s %s\n", txt[49], tmp);	/* "unable to read file" */
			return (ERROR);
		}
	}

	stat(tmp, &stats);
	TotMsg = (long int) (stats.st_size / sizeof(struct MyIndex));
	sprintf(tmp, "%s%s%c%d.cnf", HomePath, CurBoard, SEP, ConfNumbers[num]);
    if ((fmsg = fopen(tmp, "r+b")) == NULL) {
		printf("%s %s ! \n", txt[58], tmp);	/* "Error Reading file" */
		fclose(fidx);
		return ERROR;
	}
	/* Go  to Last message read */
	fread((char *) &Index.LastRead, 1, sizeof(struct MyIndex), fidx);
	fseek(fidx, (long) Index.LastRead * (long) sizeof(struct MyIndex), 0);
	CurConf = num;
	FirstDone = FALSE;
	FilesOpen = TRUE;
	return (OK);
}






/*
 * Copy the current msg in file tmpname with quotes.
 */

void
QuoteMsg(const char *tmpname)
{

	FILE           *fq;
	struct MsgHeaderType *Msg;
	unsigned long     i;
	int             col;
	byte           *ptr ;

	char            quote[10];
	char            tmp1[MAXPATHS];
	char            tmp2[50];

	Msg = (struct MsgHeaderType *) rbuf;

	tmp2[0] = 0;
	if (!strnicmp((char *) (Msg->Author), "SYSOP", 5))
		strcpy(quote, "Sys> ");
	else if (!strnicmp((char *) (Msg->Author), "UUCP", 4))
		strcpy(quote, "> ");
	else {
		sscanf((char *) (Msg->Author), "%s %s", tmp1, tmp2);	/* First & Last Name */
		sprintf(quote, "%c%c> ", tmp1[0], tmp2[0] ? tmp2[0] : 32);	/* Initials.. */
	}

	printf("\n%s %s ...\n", txt[59], quote);	/* "Quoting message with " */

	if ((fq = fopen(tmpname, "w")) == NULL) {
		printf("%s %s !\n", txt[50], tmpname);	/* "Unable to create file" */
		return;
	}

	RefMessage(fq, Msg);	/* Dear User, in a message to... */

	ptr = (byte *) (rbuf + sizeof(struct MsgHeaderType));
	i = 0;
	fprintf(fq, "%s", quote);
	col = strlen(quote);
	while (i < Index.Size) {
		if (*ptr == '\n' ) {
			fprintf(fq, "\n%s", quote);
			col = strlen(quote);
		} else if (*ptr != '\0') {
			if (col < 77) { /* Trunc quoted lines to 77 cols */
				if (charset == ISOLAT1 )
					fprintf(fq, "%c", codelu[(unsigned) (*ptr)]);
				else if (charset == CHR7BIT )
					fprintf(fq, "%c", code7bit[(unsigned) (*ptr)]);
				else
					fprintf(fq, "%c", *ptr == 255 ? '\040' : *ptr );
			}
			col++;
		}
		i++;
		ptr++;
	}
	fclose(fq);
}


/* Strip Tagline from edited reply */

void
StripTag (const char *tmpname)
{

	unsigned long    i;
	FILE           *fq;
	byte           *ptr ;

    if ((fq = fopen(tmpname, "w")) == NULL) {
		printf("%s %s !\n", txt[50], tmpname);	/* "Unable to create file" */
		return;
	}
	ptr = (byte *) (rbuf + sizeof(struct MsgHeaderType));
	i = 0;

	while ( (i<Index.Size) && (*ptr != 255) ){
        if (*ptr == '\n' )
            fprintf(fq, "\n" );
        else if (charset == ISOLAT1 )
			fprintf(fq, "%c", codelu[(unsigned) (*ptr)]);
		else if (charset == CHR7BIT )
			fprintf(fq, "%c", code7bit[(unsigned) (*ptr)]);
		else
			fprintf(fq, "%c", *ptr);
		i++;
		ptr++;
	}
	fprintf(fq, "\n");
	fflush(fq);
	fclose(fq);
}

/*
 * R‚alise l'entˆte de la lettre...
 */

#define PUBLIC    1
#define MYSELF    2
#define NOPE      3

static void
RefMessage(FILE *fp, struct MsgHeaderType *Msg ) 
{
	char            Destin[30];
	char            Ref[30];
	int             mon, day, mod;

	if (!HeadLetter)
		return;
    
    Msg->MsgDate[2] = Msg->MsgDate[5] = '-' ;		
	sscanf((char *) (Msg->MsgDate), "%d-%d", &mon, &day);
	Initials((byte *) (Msg->Author), (byte *) Destin, 25);
	Initials((byte *) (Msg->ForWhom), (byte *) Ref, 25);

	if (!strnicmp((char *) (Msg->ForWhom), "ALL", 3))
		mod = PUBLIC;
	else if (!strnicmp((char *) (Msg->ForWhom), UserName, strlen(UserName)))
		mod = MYSELF;
	else
		mod = 0;

	if (strnicmp((char *) (Msg->Author), "UUCP", 4 )) {
	fprintf(fp, "%s %s,\n", txt[96], Destin);	/* "Cher"           */
	fprintf(fp, "%s", txt[97]);	/* "Dans un msg du" */
	fprintf(fp, " %d %s, ", day, Months[mon - 1]);
	switch (mod) {
	case PUBLIC:
		fprintf(fp, "%s :\n\n", txt[98]);	/* "vous ‚crivez"   */
		break;

	case MYSELF:
		fprintf(fp, "%s :\n\n", txt[99]);	/* "vous m'‚crivez" */
		break;

	default:
		fprintf(fp, "%s", txt[100]);		/* "destin‚ …"      */
		fprintf(fp, " %s, ", Ref);
		fprintf(fp, "%s :\n\n", txt[98]);	/* "vous ‚crivez"   */
	}
	}

}



/*
 * Formatte le nom en minuscules, avec les initiales Upcase, dans dst, null
 * terminated.
 */
static void
Initials(const byte * str, byte * dst, const int len)
{
	int             m, i;

	m = 1;
	for (i = 0; i < len; i++) {
		*dst = *str;
		if (*dst == '.' || *dst == '-' || *dst == ' ') {
			m = 1;	/* Next must be Upcase */
			if (m) {
				if (!strnicmp((char *) ++str, "mc", 2)) {
					dst++;
					*dst = 'M';
					str++;
					dst++;
					*dst = 'c';
					i += 2;
				} else
					str--;
			}
		} else {
			*dst = m ? toupper(*dst) : tolower(*dst);
			m = 0;
		}
		str++;
		dst++;
	}

	*dst = 0;		/* build a null-terminated string */
	dst--;
	while (*dst == 32) {
		*dst = 0;
		dst--;
	}



}



/*
 * more : prompts "more  ?" and wait for yes, oui, ja . returns  1 for yes, 0
 * for no. 'def ' manages the CR default answer.
 *
 */

int
more(const int def)
{
	char            tmp[80] ;
	char		   *tmp1 ;

	for (;;) {
		high();
		yellow();
		if( def == YES )
			sprintf(tmp," %s ? %s : ", txt[60], txt[106]);
		else
			sprintf(tmp," %s ? %s : ", txt[60], txt[107]);
		luxptr = NULL ;
		while ((tmp1 = readline(tmp))==NULL) printf("\r");
		strcpy(tmp, tmp1);
		free(tmp1);
		clear();
		up(1);
		printf("\r %-75s \r"," ");  /* erase prompt */
		switch ((int) tmp[0]) {
		case 'o':
		case 'y':
		case 'O':
		case 'Y':
		case 'j':
		case 'J':
			return (1);

		case 'N':
		case 'n':
			return (0);

		case '+':
		case '-':
		case 0:	/* CR */
        	case 10:
			if (def == YES) {
				return (1);
			} else {
				return (0);
			}

		}
	}
}



/*
 * YesNo : prompts "Y/N : " and wait for yes, oui, ja . returns  1 for yes, 0
 * for no.
 *
 */

int
YesNo(const int def, const char *prmt )
{
	char            tmp[80];
	char		   *tmp1 ;

	for (;;) {
		if(def == YES)
			sprintf(tmp,"%s %s : ", prmt, txt[106]);
        else
		    sprintf(tmp,"%s %s : ", prmt, txt[107]); 
		luxptr = NULL ;
		while ((tmp1 = readline(tmp))==NULL) printf("\r");
		strcpy(tmp, tmp1) ;
		free(tmp1);
		clear();
		up(1);
		printf("\r %-75s \r", " " );  /* erase prompt */
		switch ((int) tmp[0]) {
		case 'o':
		case 'y':
		case 'O':
		case 'Y':
		case 'j':
		case 'J':
			return (1);

		case 'N':
		case 'n':
			return (0);
		
		case 0:	/* CR */
			if (def == YES)
				return (1);
			else
				return (0);

		}
	}
}


void
PackReply(void)
{

	char            RepFile[MAXPATHS];
	char            MsgFile[MAXPATHS];
	char            Command[MAXPATHS];


	sprintf(RepFile, "%s%s.rep", ReplyPath, CurBoard);
	sprintf(MsgFile, "%s%c%s.msg", WorkPath, SEP, CurBoard);
	if( !Chk4Rep() && Cnf2Msg( MsgFile ) ){ ; /* make bbs.msg from 9001.cnf */
		sprintf(Command, "%s %s %s", Archiver, RepFile, MsgFile);
		yellow();
		high();
		printf("%s %s\n", txt[65], RepFile);	/* "packing replies in" */
		green();
    	fflush(stdout);
		system(Command);
		ReplyExist = FALSE;
		unlink(MsgFile);
	}
	else
		printf("No replies packed.\n");
}

/* Chk4Rep returns TRUE if bbsname.rep exists */
int
Chk4Rep(void){ 

	char            RepFile[MAXPATHS];
	char            prmbuf[80] ;

	sprintf(RepFile, "%s%s.rep", ReplyPath, CurBoard);
	if (!access(RepFile, F_OK )) {
		red();
		high();
		/* "Warning ! file already exist " "delete it ?" */
		printf("%s   %s %s %s...!\n", txt[2], txt[62], RepFile, txt[63]);
		sprintf(prmbuf, "            %s ", txt[64]);
		if (YesNo(YES,prmbuf))
			unlink(RepFile);
		else{
			return(TRUE);
			}
	}
	return(FALSE) ;
}

void
Chk4Cnf(const char *tpath ){  /* does reply conference exist ? */

	char            Tcnfbuf[MAXPATHS];
	char		    Tidxbuf[MAXPATHS];
	char            prmbuf[80];

	sprintf(Tcnfbuf, "%s%c%d.cnf", tpath, SEP, REPL_CONF);
	sprintf(Tidxbuf, "%s%c%d.idx", tpath, SEP, REPL_CONF);

	ReplyExist = FALSE ;
	if (!access(Tcnfbuf, F_OK )) {
		red();
		high();
		/* "Warning ! file already exist " "delete it ?" */
		printf("%s   %s %d.cnf %s %s...!\n", txt[2], txt[62], REPL_CONF, ConfName[RCONF_IDX], txt[63]);
		sprintf(prmbuf,"            %s ", txt[64]);
		if (YesNo(YES,prmbuf)){
			unlink(Tcnfbuf);
			unlink(Tidxbuf);
			ConfActive[RCONF_IDX] = 0 ;
		}
		else
	        ReplyExist = TRUE ; 
	}

}
/*
 * UPDATECONF:  Write only if necessary the last read pointer in the first
 * index structure of the file.
 */
void
UpdateConf(int mode)  /* valid modes are UPDATE and RESET */
{
	extern int      ActvCnt;
	struct MyIndex  Idx;
	long			here ;

	if (ActvCnt < 1 || !(ConfActive[CurConf])) {
		printf("No active conference to update.\n");
		if (FilesOpen){
			printf("ERROR: module UpdateConf() -- open files, inactive Conf\n");
			printf("ActvCnt = %d  CurAct = %d \n", ActvCnt , ConfActive[CurConf] ) ;
		} ;
		return;
	}
	if (!FilesOpen) {
		printf("ERROR: module UpdateConf() -- bad file pointer for fseek()\n");
		return;
	}
	if (fseek(fidx, 0L, SEEK_SET)) {
		printf("Seek error...\n"); /* debug ? */
		return;
	}
	fread((char *) &Idx.LastRead, 1, sizeof(struct MyIndex), fidx);
	if ( (Idx.LastRead >= Index.MsgNum) && mode == UPDATE ) 
		return; /* nothing to do */
			
	Idx.LastRead = Index.MsgNum;
	fseek(fidx, 0L, SEEK_SET);
	fwrite((char *) &Idx.LastRead, 1, sizeof(struct MyIndex), fidx);
	printf("%s.\n", txt[66]);	/* "Last read pointer updated" */

	if(mode == RESET){
		here = Idx.LastRead * sizeof(struct MyIndex);
		fseek(fidx, here, SEEK_SET);
	}
}

/*------------------------------------------------------------------------*/
/*
 * NPRINT: Print a string not null-terminated on stdout.
 */
void
nprint(const byte * str, int len)
{
	while (len--)
		putchar(*str++);
}

/*------------------------------------------------------------------------*/
/*
 * NFPRINT: Print a string not null-terminated in file fp
 */
void
nfprint(FILE * fp, const byte * str, int len)
{
	while (len--)
		fputc(*str++, fp);
}

/*----------------------------------------------------------------------*/
/*
 * STR2MEM : copies a string without the null character in order to fill
 * correctly the Qmail header.
 */
void
str2mem(char *mem, const char *str)
{

	while (*str) {
		*mem = *str;
		str++;
		mem++;
	}
}

/*----------------------------------------------------------------------*/
/*
 * scpy : copies len bytes to src, deleting left spaces.
 *
 */
void
scpy(char *dest, const char *src, const int len)
{
	int    i;

	for (i = 0; i < len; i++)
		*(dest + i) = *(src + i);

	*(dest + i) = 0;

	i--;
	while (*(dest + i) == 32) {
		*(dest + i) = 0;
		i--;
	}

}

#ifndef unix
/*---------------------------------------------------------------------*/
/*
 * FCOPY : fast copy of a file. return :  0 on success 1 on error
 */
int
fcopy(const char *source, const char *destin)
{
	int             src, dst;
	int             acc = ( O_RDWR | O_CREAT | O_TRUNC ) ;
    int             perms = ( S_IREAD | S_IWRITE ) ;
	int             nb;
	char           *buf;

	if ((buf = malloc(4096)) == NULL) {
		printf("%s", txt[1]);
		return (1);
	}
	if ((src = open(source, O_RDONLY)) == -1)
	{
		printf("%s %s\n", txt[49], source);	/* "unable to read file" */
		free(buf);
		return (1);
	}
	if ((dst = open(destin, acc, perms )) == -1) {
		printf("%s %s\n", txt[50], source);	/* "unable to create file" */
		free(buf);
		return (1);
	}
    while ( (nb = read(src, buf, 4096)) !=0 ) {
		write(dst, buf, nb);
	}
	close(src);
	close(dst);
	free(buf);
	return (0);
}
#endif

/*
 * VIEW :         View a file with more.
 */
void
view(const char *Path, const char *File)
{
	FILE           *fp;
	char		   *tmp ;
	unsigned char  *buf, *bptr, *ptr ;
	int             line = 0;

	if(( tmp = (char *) malloc(2000))==NULL) {
		printf("malloc 1 in view() failed\n\n");
		return;
	}
	sprintf( tmp, "%s%c%s", Path, SEP, File);
	if ((fp = fopen(tmp, "rb")) == NULL) {
		red();
		printf("%s %s\n", txt[67], tmp);	/* "No file" */
		free(tmp);
		return;
	}
	if (( buf = (byte *) malloc(600) )==NULL){
		printf("malloc 2 in view() failed\n\n");
		free(tmp);
		return;
	}
	clear();
	cyan();
	printf("\n\n");
	while (fget((char *) buf, 250, fp)) {
		line++;
		if (line > SCREENLINES - 7) {
			line = 0;
			if (!more(YES))
				break;
			cyan();
		}
		/* translate DOS character set to LINUX character set */
			if(charset != CHRDOS ){
				ptr = buf;
				bptr = (byte *) tmp ;
				while (*ptr != '\0') {
					if ( vtspecial(*ptr) && graphics ){ /* use vt100 graphics codes */
						*bptr = '\016';
						bptr++ ;
		   				*bptr =  codevt[ (unsigned) *ptr ]  ;
	       				bptr++ ;
	       				*bptr   =  '\017'; 
					} else
					*bptr = charset == ISOLAT1 ? codelu[(unsigned) (*ptr)]
				                           : code7bit[(unsigned) (*ptr)];
					ptr++ ; bptr++ ;
					*bptr = '\0' ;
				}
				bptr = (byte *) tmp ;
			} 
			else
			    bptr = buf ;
		puts((char *) bptr);
	}
	free(buf);
	free(tmp);
	fclose(fp);
}


/* view tagline list */

void
tagview(const char *Path, const char *File)
{

	FILE           *fp;
	char            tmp[MAXPATHS];
	unsigned char   buf[513];
	int             line = 0, taglin = 0 ;

	sprintf(tmp, "%s%c%s", Path, SEP, File);
	if ((fp = fopen(tmp, "rb")) == NULL) {
		red();
		printf("%s %s\n", txt[67], tmp);	/* "No file" */
		return;
	}
	clear();
	cyan();
	printf("\n\n");
	while (fget((char *) buf, 250, fp)) {
		line++;
		if (line > SCREENLINES - 7) {
			line = 0;
			if (!more(YES))
				break;
			cyan();
		}
		printf("%4d: %s\n", taglin, buf);
		taglin++ ;	
	}
	fclose(fp);
}


/*
 * Si un rigolo tape ce mot dans le shell...
 */
void
merde(void)
{
	magenta();
	high();
	printf("\n");
	printf("EspŠce de petit connard, on t'a pas appris la politesse  ???%c\n", 7);
	printf("Va te faire foutre !!!\n\n");
}

/*
 * NUMERIC : Returns 1 if the string is a number, 0 if not.
 */
int
Numeric(const char *str)
{
	if (*str == 0)
		return (0);
	while (*str) {
		if (*str < '0' || *str > '9')
			return (0);
		str++;
	}
	return (1);
}

/*
 * IsQuoted(str)   Returns 1 if the string contains c in the 6 left chars.
 */

int
IsQuoted(const unsigned char c, const unsigned char *str)
{
	int             i;

	for (i = 0; i < 6; i++) {
		if (*str == 0)
			return (0);
		if (*str++ == c)
			return (1);
	}
	return (0);
}

/*
 * DATE : prints the date :-)
 */

void
Date(void)
{
	time_t   t;
	blue();
	high();
	time(&t);
	printf(ctime(&t));
}

/*
 * EmptyMsg : Tell that there is no BBs loaded.
 */

void
EmptyMsg(void)
{
	red();
	high();
	printf("%s ", txt[68]);	/* "Hey !" */
	clear();
	magenta();
	printf("%s\n", txt[69]);/* There is no bbs loaded !" */
	printf("%s...\n", txt[70]);	/* use 'read' 'load' or 'help' command" */
	clear();
}

/* 
 * 	ShiftLeft shifts a string left at p1, if n==2 then string is shifted
 *	two spaces left
 */

void 
ShiftLeft(char *p1, const int n)
{
	char           *p2;

	p2 = p1;
	p2++;
	if (n == 2)
		p2++;
	while (*p2 != '\0') {
		*p1 = *p2;
		p2++;
		p1++;
	}
	*p1 = '\0';
	return ;
}


/* 
 *  Scans a line until there are no more backspace or 
 *  del or '\r' characters 
 */

void
StripDel(char *tp)
{
	char           *p;
	p = tp;
	while (*p != '\0') {
		if (p == tp)  /* first character on line */
			switch (*p) {

			case '\010':	/* backspace */
			case '\177':
				ShiftLeft(p, 1);	/* del       */
				break;
			case '\015':	/* carriage return */
				*p = '\0' ;
				return;
			case '\0':
				return;

			default:
				p++;
			}

		else
			switch (*p) {

			case '\010':

			case '\177':
				p--;
				ShiftLeft(p, 2);
				break;
			case '\015':	/* carriage return */
				*p = '\0' ;
				return;
			case '\0':
				return;

			default:
				p++;
			}

	}
}

/*
 * Qlist sets up shell command line for listing QWK packets in the mail
 * directory  sorted by creation time
 */

void
Qlist(const char *cmdlist)
{

	char            tmp1[MAXPATHS];
	char			tmp2[MAXPATHS];
	int 			l ;

	strcpy(tmp2, MailPath); l = strlen(tmp2) ;
	tmp2[--l] = (char) 0 ;
	getcwd(tmp1, MAXPATHS);	/* save current directory      */
  	chdir(tmp2);			/* change to Mail directory    */
	system(cmdlist);		/* execute command line        */
	chdir(tmp1);			/* restore current directory   */
}
/*------------------------------------------------------------------------*/
