#include "csort.h" #ifdef __WATCOMC__ #include #include #else #include #endif /*----------------------- 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 int sortcomp(const void*, const void*); 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() { unsigned 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() { unsigned 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 */ unsigned 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 */ /* @(!) 2.3.00.316 */ sprintf(s, "srt%02d", ++n); /* @(:) 2.3.00.316 */ 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 */ static int sortcomp(const void* pa, const void* pb) { int i, k; const char** a = (const char**)pa; const char** b = (const char**)pb; 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 */ } /*---------------------------------------------------------------------------*/