/*   
@(SH) Funzioni per l'esecuzione di sort
@(VG$) PRIVATE
datisort        : copia struttura del sort attivo
nsortfield      : Numero campi definiti dal sort
sp			        :	struttura dei parametri sort
totrcd          : numero totale di record ordinati
no_seq				  :	contatore sequenze
no_seq1         : contatore sequenze
bspace          :	spazio disponibile nel buffer
nrcds           :	numero di records nel buffer di sort
nrcds1          : numero di records nel buffer di sort
bf, bf1         :	puntatori al buffer di sort
inbf            :	records variabili nel buffer di sort
sptr					  : puntatore al vettore dei puntatori ai buffer
init_sptr       :	puntatore al buffer appropriato
rcds_seq        :	sequenza record nel buffer di merge
fp1, fp2        :	puntatori a file di lavoro per il sort
fdname      : nome di lavoro del sort
f2name      : nome di lavoro del sort
-------------------------------------------------------------------------------
	 @(H) 2.3.00.112 23/11/91	Bonazzi		Corretta fine sort
	 @(H) 2.3.01.245 08/03/94 Soragna   Corretto sort_op per 386
*/

#include "csort.h"
#include <memory.h>

/*----------------------- STATIC PER PRIMA PARTE ----------------------------*/

static struct s_prm datisort;
static int nsortfield;

/*----------------------- STATIC PER SECONDA PARTE --------------------------*/

static struct s_prm *sp;
/* @(!) 2.3.00.112 */
static unsigned long totrcd;
static unsigned no_seq;
static unsigned no_seq1;
/* @(:) 2.3.00.112 */
static unsigned bspace;
/* @(!) 2.3.00.112 */
static unsigned nrcds;
static unsigned nrcds1;
/* @(:) 2.3.00.112 */
static char *bf, *bf1;
/* @(!) 2.3.00.112 */
static unsigned inbf;
/* @(:) 2.3.00.112 */
static char **sptr;
static char *init_sptr;
/* @(!) 2.3.00.112 */
static unsigned rcds_seq;
/* @(:) 2.3.00.112 */
static FILE *fp1, *fp2;
static char fdname [42];
static char f2name [42];

static int sortcomp(char **, char **);
static char *appr_mem(unsigned *);
/* @(!) 2.3.00.112 */
static FILE *wopen(char *);
/* @(:) 2.3.00.112 */
static void dumpbuff(void);
static void merge(void);
static void prep_merge(void);
/* @(!) 2.3.01.245 */
void  dummy(void);
/* @(:) 2.3.01.245 */

/*----------------------- PRIMA PARTE ---------------------------------------*/

/*
@(#) init_sort  SORT

@(ID)
Inizializza le variabili globali per il sort.
@(FD)
*/

int init_sort(prms)
struct s_prm *prms; /* struttura di descrizione del sort */
{
	sp = prms;
	if ((bf = appr_mem(&bspace)) != NULL)		{
		nrcds1 = nrcds = bspace / (sp->rc_len + sizeof(char *));
		init_sptr = bf;
		sptr = (char **) bf;
		bf += nrcds * sizeof(char *);
		fp1 = fp2 = NULL;
		totrcd = no_seq = inbf = 0;
		return 0;
	}
	else
		return -1;
}

/*
@(#) sort  SORT

@(ID)
Funzione per l'input dei record da ordinare.
@(FD)

@(IN)
La chiamata sort(char *) NULL) chiude la fase di input dei record.
@(FN)
*/

void sort(s_rcd)
char *s_rcd; /* buffer contenente il record da ordinare */

{
/* @(!) 2.3.00.112 */
		if (inbf == nrcds)	{	/* if the sort buffer is full */
/* @(:) 2.3.00.112 */
			qsort(init_sptr, inbf, sizeof (char *), sortcomp);
		if (s_rcd)	{		/* if there are more records to sort	*/
			dumpbuff(); /* dump the buffer to a sort work file*/
			no_seq++;	/* count the sorted sequences				*/
		}
	}
	if (s_rcd !=NULL)	{
								/* --- this is a record to sort --- */
		totrcd++;
								/* --- put the rcd addr in the pointer array --- */
		*sptr = bf + inbf * sp->rc_len;
		inbf++;
								/* --- move the rcd to the buffer --- */
		memcpy(*sptr, s_rcd, sp->rc_len);
		sptr++;				/* point to next array entry*/
	}
	else {										/* null pointer means no more rcds */
		if (inbf)		{						/* any records in the buffer?			*/
			qsort(init_sptr, inbf, sizeof (char *), sortcomp);
			if (no_seq)			/* if this isn't the only sequence*/
				dumpbuff();		/* dump the buffer to a work file */
			no_seq++;				/* count the sequence						*/
		}
		no_seq1 = no_seq;
		if (no_seq > 1)		/* if there is more than 1 sequence */
			prep_merge();		/* prepare for the merge						*/
	}
}
/*
@($) prep_merge  SORT

@(ID)
Funzione per la preparazione del Merge.
@(FD)

@(ISV)
i      = contatore.

rr     = puntatore ad una struttura di sequenza (di lavoro).

n_bfsz = ampiezza in byte del merge buffer.

@(FSV)
*/

static void prep_merge()
{
int i;
struct bp *rr;
unsigned n_bfsz;

memset(init_sptr,	'\0', bspace);
		/* -------- merge buffer size ------ */
n_bfsz = bspace - no_seq * sizeof(struct bp);
/* ------ # rcds/seq in merge buffer ------- */
rcds_seq = n_bfsz / no_seq / sp->rc_len;
if (rcds_seq < 2)	{
		/* ---- more sequence blocks than will fit in buffer,
						merge down ---- */
	while (rcds_seq < 2)		{
		FILE *hd;
/* @(!) 2.3.00.112 */
		char wname[42];					/* sort work name									*/

		fp2 = wopen(f2name);		/* open a sort work file */
/* @(:) 2.3.00.112 */
		merge();								/* binary merge */
		hd = fp1;								/* swap fds */
		fp1 = fp2;
		fp2 = hd;
/* @(!) 2.3.00.112 */
		strcpy(wname, fdname);
		strcpy(fdname, f2name);
		strcpy(f2name, wname);
		fclose(fp2);
		unlink(f2name);
		fp2 = NULL;
/* @(:) 2.3.00.112 */
		nrcds *= 2;
				/* ------ adjust number of sequence ------ */
		no_seq = (no_seq + 1) / 2;
		n_bfsz = bspace - no_seq * sizeof(struct bp);
		rcds_seq = n_bfsz / no_seq / sp->rc_len;
	}
}
bf1 = init_sptr;
rr = (struct bp *) init_sptr;
bf1 += no_seq * sizeof(struct bp);
bf = bf1;

/* fill the merge buffer with records from all sequences */

for (i = 0; i < no_seq; i++)			{
	fseek(fp1, (long) i * ((long) nrcds * sp->rc_len), 0);
		/* ------ read them all at once ------ */
/* @(!) 2.3.00.112 modificata fread(bf1, rcds_seq * sp->rc_len, 1, fp1); */
	fread(bf1, sp->rc_len, rcds_seq, fp1);
/* @(:) 2.3.00.112 */
	rr->rc = bf1;
		/* --- the last seq has fewer rcds than the rest --- */
	if (i == no_seq-1)	{
		if (totrcd % nrcds > rcds_seq)			{
			rr->rbuf = rcds_seq;
/* @(!) 2.3.00.112 */
			rr->rdsk = (unsigned) (totrcd % nrcds) - rcds_seq;
/* @(:) 2.3.00.112 */
		}
		else				{
/* @(!) 2.3.00.112 */
			rr->rbuf = (unsigned) (totrcd % nrcds);
/* @(:) 2.3.00.112 */
			rr->rdsk = 0;
		}
	}
	else			{
		rr->rbuf = rcds_seq;
		rr->rdsk = nrcds - rcds_seq;
	}
	rr++;
	bf1 += rcds_seq * sp->rc_len;
}
}

/* @(!) 2.3.01.245 */
/* Funzione  che non  fa assolutamente nulla,  ma  e'  estremamente 
	 utile all'interno di sort_op, quando si supera una certa mole di 
	 dati immessi  nel  buffer  di sort;  e' necessaria  solo  per la
	 compilazione di eseguibili per 386. Questa funzione inserita tra
	 le righe rtn= k+..... e memcpy(..) evita il  fastidioso   memory
	 fault che si ottiene utilizzando strutture molto ampie  o  molto
	 numerose. Probabilmente evita, in fase di compilazione, che vengano
	 creati indirizzi strani, oppure in fase di esecuzione, che vengano
	 reperite zone di memoria che non c'entrano per nulla.
*/
void dummy()
{
}
/* @(:) 2.3.01.245 */


/*
@($) merge  SORT

@(ID)
Effettua un merge. 
E' un merge binario di records dalla sequenza fp1 in fp2.
@(FD

@(ISV)
i   = contatore.

needy,needx = se true necessita un record da (x/y).

xcnt,ycnt   = numero di records lasciati in ogni sequenza.

x,y         = contatori per le sequenze.

adx,ady     = indirizzi sul disco delle sequenze di record.

ysptr       = stringa di lavoro.
@(FSV)
*/

static void merge()
{
	int i;
	int needy, needx;	/* true = need a rcd from (x/y)		*/
/* @(!) 2.3.00.112 */
	unsigned xcnt, ycnt;		/* # rcds left each sequence			*/
/* @(:) 2.3.00.112 */
	int x, y;					/* sequence counters							*/
	long adx, ady;			/* sequence record disk addresses */
/* @(!) 2.3.00.112 */
	char	 *ysptr = init_sptr + sp->rc_len;
/* @(:) 2.3.00.112 */

		/* --- the two sets of sequences are x and y ----- */
	fseek (fp2, 0L, 0);
	for (i = 0; i < no_seq; i += 2)		{
		x = y = i;
		y++;
		ycnt =
			y == no_seq ? 0 : y == no_seq - 1 ?
/* @(!) 2.3.00.112 */
			(unsigned) (totrcd % nrcds) : nrcds;
		xcnt = y == no_seq ? (unsigned) (totrcd % nrcds) : nrcds;
/* @(:) 2.3.00.112 */
		adx = (long) x * (long) nrcds * sp->rc_len;
		ady = adx + (long) nrcds * sp ->rc_len;
		needy = needx = 1;
		while (xcnt || ycnt)	{
			if (needx && xcnt)	{	/* need a rcd from x? */
				fseek(fp1, adx, 0);
				adx += (long) sp->rc_len;
				fread(init_sptr, sp->rc_len, 1, fp1);
				needx = 0;
			}
			if (needy && ycnt)	{	/* need a rcd from y? */
				fseek(fp1, ady, 0);
				ady += sp->rc_len;
/* @(!) 2.3.00.112 modificata fread(init_sptr+sp->rc_len, sp->rc_len, 1, fp1); */
				fread(ysptr, sp->rc_len, 1, fp1);
/* @(:) 2.3.00.112 */
				needy = 0;
			}
			if (xcnt || ycnt)	{	/* if anything is left */
								/* ---- compare the two sequences --- */
/* @(!) 2.3.00.112 modificata if (!ycnt || (xcnt && (sortcomp(&init_sptr, &init_sptr + sp->rc_len)) < 0)) { */
				if (!ycnt || (xcnt && (sortcomp(&init_sptr, &ysptr)) < 0)) {
/* @(:) 2.3.00.112 */
					/* ----- record from x is lower ---- */
					fwrite(init_sptr, sp->rc_len, 1, fp2);
					--xcnt;
					needx = 1;
				}
				else if (ycnt)	{	/* record from y is lower */
/* @(!) 2.3.00.112 */
					fwrite(ysptr, sp->rc_len, 1, fp2);
/* @(:) 2.3.00.112 */
					--ycnt;
					needy = 1;
				}
			}
		}
	}
}
/*
@($) dumpbuff  SORT

@(ID)
Copia il buffer di sort nel file di lavoro.
@(FD)

@(ISV)
i = contatore.
@(FSV)
*/

static void dumpbuff()
{
/* @(!) 2.3.00.112 */
	unsigned i;
/* @(:) 2.3.00.112 */

	if (fp1 == NULL)
/* @(!) 2.3.00.112 */
		fp1 = wopen(fdname);
/* @(:) 2.3.00.112 */
	sptr = (char **) init_sptr;
	for (i = 0; i < inbf; i++)	{
		fwrite(*(sptr + i), sp->rc_len, 1, fp1);
/* @(!) 2.3.00.112 */
	*(sptr + i) = 0;
/* @(:) 2.3.00.112 */
}
inbf = 0;
}

/*
@($) wopen  SORT

@(ID)
Apre un file di lavoro temporaneo (per il sort).
@(FD)

@(ISV)
fp     = puntatore al file temporaneo.

s = stringa di formato.

n      = numero del file.

@(FSV)
*/

/* @(!) 2.3.00.112 */
static FILE *wopen(name)
char *name; /* nome del file temporaneo */
/* @(:) 2.3.00.112 */
{
	FILE *fp;
	char	s[42];
/* @(!) 2.3.00.112 */
	static int	n = 0;
/* @(:) 2.3.00.112 */

	sprintf(s, "srt%03d", ++n);
	TMPFNAME(name,s);
#ifdef DOS
	if ((fp = fopen(name, "wb+")) == NULL)	{
#else
	if ((fp = fopen(name, "w+")) == NULL)	{
#endif

#ifndef XVT_OS
	printf("\nFile error");
	exit(1);
#else
	fatal_box("Can't open SORT file");
#endif
	}
	return fp;
}

/*
@(#) sort_op  SORT

@(ID)
Ritorna il puntatore al primo record non ancora elaborato nella sequenza ordinata.
@(FD)

@(ISV)
j         = e' true se una qualche sequenza contiene ancora dei records.

i         = contatore.

k,nrd,l   = variabili di lavoro.

rr        = variabile di lavoro.

r1        = variabile di lavoro.

rtn       = indirizzo del buffer da ritornare.

ad,tr     = variabili di lavoro.
@(FSV)

@(FN)
 Se la funzione sort_op ritorna NULL non ci sono piu' records nella sequenza ordinata.
@(FN)
*/


char *sort_op()
{
	int j = 0;
/* @(!) 2.3.00.112 */
	unsigned i, k, nrd, l;
/* @(:) 2.3.00.112 */
	struct bp *rr;
/* @(!) 2.3.00.112 */
	static unsigned r1 = 0;
/* @(:) 2.3.00.112 */
	char *rtn;
	long ad, tr;

	if (init_sptr == NULL) return NULL;

	sptr = (char **) init_sptr;
	if (no_seq < 2) {
				/* -- with only 1 sequence, mo merge has been done -- */
/* @(!) 2.3.00.112 */
		if (r1 == (unsigned) totrcd)	{
/* @(:) 2.3.00.112 */
			if (init_sptr != NULL) free(init_sptr);
/* @(!) 2.3.00.112 */
			init_sptr = NULL;
/* @(:) 2.3.00.112 */
			fp1 = fp2 = NULL;
			r1 = 0;
			return NULL;
		}
		return *(sptr + r1++);
	}


	rr = (struct bp *) init_sptr;
	for (i = 0; i < no_seq; i++)
/* @(!) 2.3.00.112 */
		j |= (int) (rr + i)->rbuf | (rr + i)->rdsk;
/* @(:) 2.3.00.112 */

		/* -- j will be true if any sequence still has records - */
	if (!j)
	{
		fclose(fp1);						/* none left */
		unlink(fdname);
/* @(!) 2.3.00.112  eliminata if (fp2) */
/* @(!) 2.3.00.112  eliminata { */
/* @(!) 2.3.00.112  eliminata fclose(fp2); */
/* @(!) 2.3.00.112  eliminata unlink(f2name); */
/* @(!) 2.3.00.112 eliminata } */
/* @(:) 2.3.00.112 */
		if (init_sptr != NULL) free(init_sptr);
/* @(!) 2.3.00.112 */
		init_sptr = NULL;
/* @(:) 2.3.00.112 */
		fp1 = fp2 = NULL;
		r1 = 0;
		return NULL;
	}
	k = 0;

		/* --- find the sequence in the merge buffer
												with the lowest record --- */
	for (i = 0; i < no_seq; i++)
		k = ((sortcomp( &(rr + k)->rc, &(rr + i)->rc) < 0) ? k : i);


		/* --- k is an integer sequence number that offsets to the
				sequence with the lowest record ---- */

	(rr + k)->rbuf--;					/* decrement the rcd counter */
	rtn = (rr + k)->rc;				/* set the return pointer */
	(rr + k)->rc += sp->rc_len;
	if ((rr + k)->rbuf == 0)		{
				/* ---- the sequence got empty ---- */
				/* --- so get some more if there are any --- */
		rtn = bf + k * rcds_seq * sp->rc_len;
/* @(!) 2.3.01.245 */
		dummy();
/* @(:) 2.3.01.245 */ 
		memcpy(rtn, (rr + k)->rc - sp->rc_len, sp->rc_len);
		(rr + k)->rc = rtn + sp->rc_len;
		if ((rr + k)->rdsk != 0)		{
			l = ((rcds_seq-1) < (rr+k)->rdsk) ? rcds_seq-1 : (rr+k)->rdsk;
/* @(!) 2.3.00.112 */
			nrd = k == no_seq - 1 ? (unsigned) (totrcd % nrcds) : nrcds;
/* @(:) 2.3.00.112 */
			tr = (long) ((k * nrcds + (nrd - (rr + k)->rdsk)));
			ad = tr * sp->rc_len;
			fseek(fp1, ad, 0);
/* @(!) 2.3.00.112  modificata fread(rtn + sp->rc_len, l * sp->rc_len, 1, fp1); */
			fread(rtn + sp->rc_len, sp->rc_len, l, fp1);
/* @(:) 2.3.00.112 */
			(rr + k)->rbuf = l;
			(rr + k)->rdsk -= l;
		}
		else
			memset((rr + k)->rc, 127, sp->rc_len);
	}
	return rtn;
}

/*
@(#) sort_stats  SORT

@(ID)
Visualizza la statistica sul sort.
@(FD)
*/

void sort_stats()
{
#ifndef XVT_OS
	printf("\n\r\n\r\n\rRecord Length = %d",sp->rc_len);
/* @(!) 2.3.00.112 */
	printf("\n\r%u records sorted",totrcd);
	printf("\n\r%u sequence",no_seq);
/* @(:) 2.3.00.112 */
	if (no_seq1 != 1) putchar('s');
	printf("\n\r%u characters of sort buffer", bspace);
/* @(!) 2.3.00.112 */
	printf("\n\r%u records per buffer\n\n",nrcds);
/* @(:) 2.3.00.112 */
#else
	message_box(
		"Record length = %d\n"
		"%u records sorted\n"
		"%u sequences\n"
		"%u bytes of sort buffer\n"
		"%u records per buffer",
		sp->rc_len, totrcd, no_seq, bspace, nrcds
	);
#endif
}

/*
@($) appr_mem  SORT

@(ID)
Alloca memoria per un buffer.
@(FD)

@(ISV)
buff = buffer di memoria allocato.
@(FSV)

@(IN)
Se puo' alloca MOSTMEM altrimenti ne alloca quanto puo', 
non scendendo mai sotto il minimo LEASTMEM
@(FN)
*/

static char *appr_mem(h)
unsigned *h; /* puntatore alla quantita' di byte allocati */
{
	char *buff = NULL;

	*h = (unsigned) MOSTMEM + 1024;
	while (buff == NULL && *h > LEASTMEM)		{
		*h -= 1024;
		buff = malloc(*h);
	}
	return buff;
}

/* 
@($) sortcomp  SORT

@(ID)
Esegue i confronti per il sort ed il merge.
@(FD)

@(ISV)
i = contatore.

k = risultato della comparazione.
@(FSV)

@(IN)
Ritorna un intero < 0  se  a < b
				           0   se  a = b
								  > 0  se  a > b
@(FN)
*/

static int sortcomp(a,b)
char **a; char **b;  /* puntatori ai puntatori ai record da confrontare */
{
	int i, k;

	if (**a == 127 || **b == 127)
		return (int) **a - (int) **b;
	for (i = 0; i < NOFLDS; i++)
	{
		if (sp->s_fld[i].f_pos == 0) break;
		if (sp->s_fld[i].f_len >= 0)                 /* string compare */
		{
			if  ( ( k=strncmp ( (*a)+sp->s_fld[i].f_pos-1, 
													(*b)+sp->s_fld[i].f_pos-1, 
													sp->s_fld[i].f_len
												)	) != 0) 
												return (sp->s_fld[i].ad == 'd')?-k:k;
		} 

		if (sp->s_fld[i].f_len == -1)          /* integer compare */
		{
			if ( (*( (int*) (*a)+sp->s_fld[i].f_pos-1 )) !=
					 (*( (int*) (*b)+sp->s_fld[i].f_pos-1 ))     )
			{
				k=(*( (int*) (*a)+sp->s_fld[i].f_pos-1 )) - (*( (int*) (*b)+sp->s_fld[i].f_pos-1 ));
				return (sp->s_fld[i].ad == 'd')?-k:k;
			}
		}

		if (sp->s_fld[i].f_len == -2)          /* BOOLEAN compare */
		{
			if ( (*( (BOOLEAN*) (*a)+sp->s_fld[i].f_pos-1 )) !=
					 (*( (BOOLEAN*) (*b)+sp->s_fld[i].f_pos-1 ))     )	
			{
				k=(int) ( (*( (BOOLEAN*) (*a)+sp->s_fld[i].f_pos-1 )) - (*( (BOOLEAN*) (*b)+sp->s_fld[i].f_pos-1 )) );
				return (sp->s_fld[i].ad == 'd')?-k:k;
			}
		}
	}
	return 0;
}

/*---------------------------- SECONDA PARTE --------------------------------*/

/*
@(#) initsortfield  SORT

@(ID)
Definizione di sort per un record ISAM.
@(FD)
*/

void initsortfield ()
{
   nsortfield = datisort.s_fld[0].f_pos = 0;   /* sort su nessun campo */
}

/*
@(#) addsortfield  SORT

@(ID)
Aggiunge campi alla descrizione.
@(FD)

@(ISV)
i = variablie di lavoro.

n = variablie di lavoro.
@(FSV)
*/

int addsortfield (isfd,ncampo,da,a,ad)
isfdptr isfd;     /* puntatore al descrittore del file ISAM                   */
FieldName ncampo; /* nome campo                                             */
int da;           /* carattere da cui considerare il campo per il sort        */
int a;            /* carattere fino al quale considerare il campo per il sort */
char ad;          /* ordine crescente o decrescente                           */

{
   int i,n;

   if (((n = findfld(isfd->r, ncampo)) != -1) && (nsortfield < NOFLDS))
	 {
	 /*  sostituito i con n in Fd[_].RecOff e Fd[_].len */
/* @(!) 2.3.01.temp */
      datisort.s_fld[nsortfield].f_pos = (da==0) ? (isfd->r->Fd[n].RecOff + 1) : (isfd->r->Fd[n].RecOff+da);
		 datisort.s_fld[nsortfield].f_len = (a==0) ? (isfd->r->Fd[n].Len) : (a-da+1);  /* carica FLDLEN */
      datisort.s_fld[nsortfield].ad = ad;        /* carica AD */
      nsortfield++;
/* @(:) 2.3.01.temp */
      return 0;
	 }
   else return -1;
}

/*
@(#) finesortfield  SORT

@(ID)
Chiude la descrizione del sort per i record ISAM.
@(FD)

@(IN)
Attenzione : questa funzione inizializza anche il sort.
@(FN)
*/

int finesortfield (isfd)
isfdptr isfd; /* puntatore al descrittore del file ISAM */
{
   datisort.rc_len=isfd->d->LenR;
/* @(!) 2.3.01.temp */
   if (nsortfield < NOFLDS)
      datisort.s_fld[nsortfield].f_pos = 0;    /* chiude l'elenco campi */
/* @(:) 2.3.01.temp */
   return (init_sort (&datisort));
}

/*---------------------------------------------------------------------------*/