/*static char *SCCSID = "@(#)chkcps.c   6.3 92/02/19";*/
/*static char *SCCSID = "@(#)chkcps.c   6.3 92/02/19";*/
/***    CHKCPS.C - Routines to check and read codepage information
 *
 *      CHKDSK
 *      Peter A. Williams
 *      Copyright 1988 Microsoft Corporation
 *
 *      Modification history:
 *      P.A. Williams   08/10/89         Original for UHPFS 1.0
 *      69658  06/15/93  rjp  Ported OS/2 1.3 fix from defect 5466:
 *                            Memory reduction. Reduce chk_disk_map and
 *                            chk_db_usemap from 8 bits per sector to 2 bits
 *                            per sector.
 */

#include "ldefs.h"
#include "ltypes.h"
#include "globals.h"
#include <pr_chk.h>

#ifdef CPTRACE
#define TRACE
#endif

#define CCS_ROT 7

/*
 ***    CCS - Compute Checksum
 *
 *      CCS computes a simple 32-bit checksum of a byte string.
 *      The string isn't known to start on a quad byte boundary.
 *
 *      WARNING - CCS will produce a different checksum then CAS!
 *
 *      ENTRY   pbString = start address of string
 *              cb = byte count
 *
 *      EXIT    Returns checksum
 *
 */

ULONG chk_cp_chksum (PUCHAR pbString, USHORT cb)
{
register USHORT ul; /* loop counter */
ULONG cks = 0;      /* accumulator */

   for (ul = 0; ul < cb; ul++) {

          /* add in the byte, then rotate left by CCS_ROT */
          cks += (ULONG) (*pbString++);
          cks = (cks << CCS_ROT) + (cks >> (32-CCS_ROT));
   }

   return (cks);
}


/***    chk_create_cpinfolist () - create a cpinfolist node
 *
 *      This function allocates memory for a cpinfolist node (the
 *      structure that is used to hold information about a read
 *      in cp sector). It also allocates memory for the codepage
 *      sector that will be read off disk and pointed to by the node.
 *
 *      chk_create_cpinfolist (lptr)
 *
 *      ENTRY           lptr - an indirect pointer to the cpinfolist.
 *
 *      EXIT            Returns TRUE for success, FALSE for out of memory.
 *
 *      CALLS           malloc
 *                              memset
 *
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Allocates memory
 *
 */

USHORT chk_create_cpinfolist (PCPINFOLIST *lptr)
{
        PBYTE p;

        if (((*lptr = malloc (sizeof (CPINFOLIST))) == NULL) ||
            ((p = malloc (BYTES_PER_CODEPAGE))      == NULL))

                return (FALSE);
        else {
                memset (*lptr, '\0', sizeof (CPINFOLIST)); /* fill in mem */
                memset (p,      '\0', BYTES_PER_CODEPAGE);
                (*lptr)->p.p = p;               /* set cp ptr saved above */
                return (TRUE);
        }

} /* chk_create_cpinfolist */


/***    chk_insert_cp () - insert a cpinfolist node into the memory list
 *
 *      This routine inserts a cpinfolist node into the memory list. The
 *      list contains codepage sectors that were found on disk during disk
 *      scanning (during recovery) but that were not ready to be processed yet.
 *      The sectors will be removed from the list when they are ready to be
 *      processed.
 *
 *      chk_insert_cp (cpl, lptr)
 *
 *      ENTRY           cpl - indirect pointer to the list head.
 *                              lptr - indirect ptr to the node to insert
 *
 *      EXIT            None
 *
 *      CALLS           chk_create_cpinfolist
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 *
 */

void chk_insert_cp (PCPINFOLIST *cpl, PCPINFOLIST *lptr)
{
#ifdef CPTRACE
        printf ("chk_insert_cp (): insert %s from lsn=%08lx\n",
                                cp_types[(*lptr)->cp_type], (*lptr)->lsn);
        fflush (stdout);
#endif

        if (*cpl == NULL) {         /* insert into NULL list */
            *cpl = *lptr;
            chk_create_cpinfolist (lptr);
        }
        else  {

            /* insert into non-NULL list */

             chk_insert_cp (&(*cpl)->next, lptr);
        }

} /* chk_insert_cp */


/***    chk_remove_cp () - remove a cpinfolist node from memory list
 *
 *      This function removes a target cpinfolist node from the internal memory
 *      list (if the target is found) and returns it.
 *
 *      chk_remove_cp (cpl, cp_type)
 *
 *      ENTRY           cpl - indirect ptr to the head of the memory list.
 *                              cp_type - type of node to remove (CPINFOSEC or CPDATASECTOR node)
 *
 *      EXIT            Returns NULL if node not found or a ptr to the node if
 *                              it is found.
 *
 *      CALLS           None
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 *
 */

PCPINFOLIST chk_remove_cp (PCPINFOLIST *cpl, USHORT cp_type)
{
PCPINFOLIST ptr;

#ifdef CPTRACE
   printf ("chk_remove_cp (): want %s\n", cp_types[cp_type]);
   fflush (stdout);
#endif

    if (*cpl == NULL)      /* the list has nothing in it, */
        return (NULL);     /* so we didn't find the target. */
    else
        /* recurse to next element - this isn't it */
        if (((*cpl)->cp_type != cp_type) || ((*cpl)->index != chk_cp_index))
            return (chk_remove_cp (&(*cpl)->next, cp_type));

        else {  /* found it! Remove node and return it. */
                ptr = *cpl;
                *cpl = (*cpl)->next;
                return (ptr);
        }

} /* chk_remove_cp */


/* chk_bad_cpdatasec - do sanity checks on cpdatasec to see if it is valid
 *
 *      This routine decides whether the given cpdatasec is valid or not.
 *      The cpdatasec may be corrupted but fixable, in which case the
 *      cpdatasec is fixed and deemed valid. If the cpdatasec is unrecognizeable
 *      or unable to be fixed, it is returned as bad. Most of this sanity
 *      checking is performed on the header information although some of
 *      the fields of the first entry are checked (but not fixed, that will
 *      be done later).
 *
 *      chk_bad_cpdatasec (lptr)
 *
 *      ENTRY           lptr - a ptr to the cpinfolist node that points to the
 *                                         cpdatasec.
 *
 *      EXIT            Returns TRUE if the cpdatasec is bad, FALSE otherwise.
 *
 *      CALLS           None
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Corrupted but valid cpdatasec may be fixed up.
 */

USHORT chk_bad_cpdatasec (PCPINFOLIST lptr)
{
        USHORT cpsno = CP_NO(CPPERDATASEC);
 PCPDATASECTOR cps = lptr->p.cps;

#ifdef CPTRACE
        printf ("chk_bad_cpdatasec (), chk_cp_index=%02x\n", chk_cp_index);
        fflush (stdout);
#endif

   /* See if we can recognize the cpdatasec. Some specific fields are
    * checked and then the signature. If either is completely correct
    * we assume the other is corrupted and fix it.
    */

   if (cps->iFirstCP != chk_cp_index) {
     cps->iFirstCP = chk_cp_index;
     lptr->dirty = TRUE;
   }

   if (cps->cCodePage != CP_LEFT(CPPERDATASEC)) {
    cps->cCodePage = CP_LEFT(CPPERDATASEC);
    lptr->dirty = TRUE;
   }

   if (cps->sig != CPDATAVAL) { // now use field info in conjunction
     if (!lptr->dirty) {        // with sig to determine if we recognize
            cps->sig = CPDATAVAL;    // a CPDATASECTOR.
            lptr->dirty = TRUE;
          }
          else
            return (TRUE);   /* both fields and sig are bad! */
   }

   return (FALSE);

} /* chk_bad_cpdatasec */


/***    chk_bad_cpinfosec - do sanity checks on cpinfosec to see if it is valid
 *
 *      This routine decides whether the given cpinfosec is valid or not.
 *      The cpinfosec may be corrupted but fixable, in which case the
 *      cpinfosec is fixed and deemed valid. If the cpinfosec is unrecognizeable
 *      or unable to be fixed, it is returned as bad. Most of this sanity
 *      checking is performed on the header information although some of
 *      the fields of the first entry are checked (but not fixed, that will
 *      be done later).
 *
 *      chk_bad_cpinfosec (lptr)
 *
 *      ENTRY           lptr - a ptr to the cpinfolist node that points to the
 *                                         cpinfosec.
 *
 *      EXIT            Returns TRUE if the name bad, FALSE otherwise.
 *
 *      CALLS           None
 *
 *      WARNINGS        None
 *
 *      EFFECTS         May fix a corrupted cpinfosec.
 *
 */

USHORT chk_bad_cpinfosec (PCPINFOLIST lptr)
{
PCPINFOSECTOR cp = lptr->p.cp;

#ifdef CPTRACE
   printf ("chk_bad_cpinfosec (), chk_cp_index=%02x\n", chk_cp_index);
   fflush (stdout);
#endif


       /* see if we can recognize the cpinfosec. Some specific fields are
        * checked and then the signature. If either is completely correct
        * we assume the other is corrupted and fix it.
        */

        if (cp->iFirstCP != chk_cp_index) {
                cp->iFirstCP = chk_cp_index;
                lptr->dirty = TRUE;
        }

        if (cp->cCodePage != CP_LEFT(CPPERINFOSEC)) {
                cp->cCodePage = CP_LEFT(CPPERINFOSEC);
                lptr->dirty = TRUE;
        }

        if (cp->CPInfoEnt[0].iCPVol != chk_cp_index)
                lptr->dirty = TRUE;


        if (cp->sig != CPINFOVAL) {

                 /* now use field info in conjunction
                  * with sig to determine if we recognize
                  * a cpinfosec.
                  */

                if (!lptr->dirty) {
                        cp->sig = CPINFOVAL;
                        lptr->dirty = TRUE;
                }
                else
                        return (TRUE);   /* both fields and sig are bad! */
        }

        return (FALSE);

} /* chk_bad_cpinfosec */


/***    chk_get_cp - locate the requested cpinfosec or cpdatasec
 *
 *      This routine locates the requested cp sector. If we have done
 *      previous disk scanning, then the memory list of codepage sectors
 *      will first be searched for the sector. If it is not there, then
 *      we will try to read the sector at the given lsn. If the sector
 *      cannot be read or the read sector is determined to be invalid,
 *      then we start scanning the disk, looking for the requested sector.
 *      During scanning, if we find other codepage sectors that we will need
 *      to process later, we just insert them into the memory list where
 *      we will find them later.
 *
 *      chk_get_cp (cpl, lptr, lsn, recover_lsn, cp_type)
 *
 *      ENTRY           cpl - indirect ptr to the memory list we may have to insert
 *                                        into.
 *                              lptr - a memory node where we should put the requested
 *                                         sector if we find it.
 *                              lsn - lsn where we should try to read the sector.
 *                              recover_lsn - lsn where we should continue any disk scanning.
 *                              cp_type - the type of codepage sector we are looking for
 *                                                (cpinfosec or cpdatasec).
 *
 *      EXIT            Returns TRUE if the requested sector is located,
 *                              FALSE otherwise.
 *
 *      CALLS           strchr
 *                      memcmp
 *                      chk_check_dbcs
 *
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 *
 */

USHORT chk_get_cp ( PCPINFOLIST *cpl,
                                        PCPINFOLIST *lptr,
                                        ULONG lsn,
                                        PULONG recover_lsn,
                                        USHORT cp_type)
{
        USHORT disk_scan = TRUE;
        USHORT unfound = TRUE;
        USHORT check_cp = TRUE;
        USHORT found_type;
        ULONG sec;

#ifdef CPTRACE
        printf ("chk_get_cp: %s, lsn = %lx\n", cp_types[cp_type], lsn);
        fflush (stdout);
#endif

        /* In the case where we go to the next cpinfosec but don't need
         * a new cpdatasec, we just return and keep processing the same
         * cpdatasec.
         */

        if ((cp_type == CP_FIND_CPDATASEC) && CP_NO(CPPERDATASEC)) {
#ifdef CPTRACE
                printf ("chk_get_cp: Did not require getting new CPDATASECTOR\n");
                fflush (stdout);
#endif
                return (TRUE);
        }

        /*
         * If we recovered any codepage information, we must check if we have
         * already found the requested cp sector. We do so by comparing
         * cp types and indices.
         */

        if (chk_cp_recovery &&
                ((*lptr  = chk_remove_cp (cpl, cp_type)) != NULL))
                unfound = FALSE;


        /* see if we have to read in the structure. If we are looking for
         * a cpdatasec, we should have already processed the cpinfosec.
         */

        if (unfound) {             /* do we need to read the cp sector ? */

                if (!chk_create_cpinfolist (lptr))
                        return (FALSE);
                else {
                        (*lptr)->lsn = lsn;
                        (*lptr)->cp_type = cp_type;
                }


                /* See if we get a valid CPINFOSECTOR. If we don't, we will scan
                 * the disk for it.
                 */

                if (lsn) {

                        if ((sec=chk_hotfix (lsn, SECTORS_PER_CODEPAGE,
                                                                        (*lptr)->p.p)) != lsn) {
                                if (sec) {
                                        (*lptr)->lsn = sec;
                                        (*lptr)->dirty = TRUE;
#ifdef CPTRACE
                                        printf ("chk_get_cp: lsn=%08lx was hotfixed\n", lsn);
                                        printf ("and reallocated to sector %08lx\n", sec);
                                        fflush (stdout);
#endif
                                }
                                else {
                                        check_cp = FALSE;
#ifdef CPTRACE
                                        printf ("chk_get_cp: could not read CP at lsn=%lx\n", lsn);
                                        fflush (stdout);
#endif
                                }
                        }
                }
        } /* if unfound */


        /* Disk scan is always TRUE at this point. We turn off disk scanning
         * if we determine the read sector to be valid.
         */

        while (disk_scan) {

                if (check_cp) {
                        if (cp_type == CP_FIND_CPINFOSEC)
                                disk_scan = !check_cp || chk_bad_cpinfosec (*lptr);
                        else
                                disk_scan = !check_cp || chk_bad_cpdatasec (*lptr);

#ifdef CPTRACE
                        if (disk_scan) {
                                printf ("chk_get_cp: the %s was INVALID.\n", cp_types[cp_type]);
                                fflush (stdout);
                        }
#endif
                }
                else
                        check_cp = TRUE;


                /* use up the sectors in the bitmap. We do this as early
                * as possible so that if we have to scan the disk, we don't
                * pick up this structure again. If we need to scan the
                * disk, remove the current lsn from the disk bitmap. The
                * sig will be bad so we won't pick it up.
                */

                if (disk_scan) {
                        chk_cp_recovery = TRUE;

                        /* We didn't find a valid CPINFOSECTOR, thus we starting scanning
                        * the disk for it. Other codepage structures that are found
                        * are tacked onto the memory. We either stop when we find
                        * the CPINFOSECTOR that we are looking for or if we reach the
                        * end of the disk.
                        */

                        if ((found_type=chk_find_cp (lptr, recover_lsn)) == CP_FIND_NONE) {

#ifdef CPTRACE
                                printf ("\nchk_get_cp: we did not find the sought %s\n",
                                                cp_types[cp_type]);
                                fflush (stdout);
#endif
                                return (FALSE);
                        }
                        else
                        if (((*lptr)->index == chk_cp_index) &&
                                (found_type == cp_type)) {       /* we found it! */
#ifdef CPTRACE
                                        printf ("\nchk_get_cp: the %s was found, lsn=%08lx\n",
                                                        cp_types[cp_type], (*lptr)->lsn);
                                        fflush (stdout);
#endif
                        }
                        else {                                  /* we found some other CP,   */
                                chk_insert_cp (cpl, lptr);      /* put it in the memory tree */
#ifdef CPTRACE
                                printf ("\nchk_get_cp: another %s was found at lsn=%lx\n",
                                        cp_types[found_type], *recover_lsn-1);
                                fflush (stdout);
#endif
                                check_cp = FALSE;
                        }

                }

        } /* while */


        return (TRUE);       /* we stopped disk scanning and fell out of  */
                             /* the loop so we found the requested sector */

} /* chk_get_cp */


/***    chk_process_cpdataent - check a cpdataent and store its info in memory
 *
 *      This function checks a cpdataent and fixes it up if possible. It
 *      uses the cpdataent info in conjunction with the cpinfoent info to
 *      try to fix up the cpdataent. The cpinfoent may also be updated should
 *      it be found to be corrupted. If a cpdataent is not fixable then
 *      the CPID and CNTRY code for the cpinfoent and cpdataent will be set
 *      to invalid.
 *
 *      chk_process_cpdataent (lptr, lptr2, pcs, cpi, cpd)
 *
 *      ENTRY           lptr - memory node pointing to cpinfosec.
 *                              lptr2 - memory node pointing to cpdatasec.
 *                              cps - ptr to the cpdatasec containing the checked cpdataent
 *                              cpi - ptr to the cpinfoent corresponding to the cpdataent
 *                              cpd - ptr to the cpdataent
 *
 *      EXIT            Returns TRUE if the cpdataent is valid or corupted but
 *                              fixable, FALSE otherwise.
 *
 *      CALLS           chk_cp_chksum
 *                              chk_allocate_cp_tables
 *
 *      WARNINGS        None
 *
 *      EFFECTS         May fix up a corrupted cpdataent
 */

USHORT chk_process_cpdataent (  PCPINFOLIST lptr,
                                                                PCPINFOLIST lptr2,
 PCPDATASECTOR cps,
 PCPINFOENTRY cpi,
 PCPDATAENTRY cpd)
{
PCPINFOSECTOR cp = lptr->p.cp;
USHORT invalid_cpds=0;
ULONG chksum;
USHORT cpsno = CP_NO(CPPERDATASEC);
USHORT maxvalue;
USHORT i, k;
USHORT start, end;        /* Defect 64236 */
DBCS_Range *range;
USHORT invalid = FALSE;
USHORT OldCountry, OldRangeCnt, OldCPID;

#ifdef CPTRACE
   printf ("chk_process_cpdataent (): chk_cp_index=%x\n", chk_cp_index);
   fflush (stdout);
#endif

        /*
         *  Right off the bat, we need to know if the cpd is reasonably
         *  close to the cps.
         */
        if( (PBYTE)cpd < (PBYTE)cps ||
            (PBYTE)cpd - (PBYTE)cps >= SECSIZE ) {

            /*  This is not a valid cps/cpd pair.  */
            return(FALSE);
        }

        /* We first check that the RNGECNT field is correct. Since our chksum
        * needs to use this value, we make sure it is correct first.
        */

        maxvalue = (BYTES_PER_CODEPAGE - cps->offCPData[cpsno] - sizeof(CPDATAENTRY))
                                / sizeof (DBCS_Range) - 1;    /* -1 for 0,0 terminator */

        if (cpd->cDBCSrange != cpi->cDBCSrange) {

                if (cpd->cDBCSrange <= maxvalue) {

                    /*
                     *  Since the CPD_RANGECNT from the CPDATAENT
                     *  doesn't exceed the possible range, check
                     *  to see if using it gives the correct chksum.
                     *  If it does, take that value for CPI_RANGECNT.
                     */

                    chksum = chk_cp_chksum((PBYTE)cpd,
                                      sizeof (CPDATAENTRY)
                                    + cpd->cDBCSrange * sizeof (DBCS_Range));

                    if ((chksum == cps->cksCP[cpsno]) ||
                            (chksum == cpi->cksCP)) {

                        cpi->cDBCSrange = cpd->cDBCSrange;
                        lptr->dirty = TRUE;
                    }
                } /* try fix infoent */


                if ((!lptr->dirty) && (cpi->cDBCSrange <= maxvalue)) {

                    /*
                     *  We haven't fixed it, so we'll try the other
                     *  way around:  if substituting the CPINFOSEC's
                     *  CPI_RANGECNT value for the CPDATAENT's
                     *  CPD_RANGECNT generates the correct sum, then
                     *  correct the data entry from the info sector.
                     *
                     *  Note that if this doesn't work, we have to
                     *  restore the CPDDATASEC to its former state,
                     *  since we don't know what memory we might be
                     *  messing with.
                     */
                    OldRangeCnt = cpd->cDBCSrange;

                    cpd->cDBCSrange = cpi->cDBCSrange;
                    chksum = chk_cp_chksum((PBYTE)cpd,
                                   sizeof (CPDATAENTRY) +
                                   cpd->cDBCSrange * sizeof (DBCS_Range));

                    if ((chksum == cps->cksCP[cpsno]) ||
                            (chksum == cpi->cksCP)) {
                            cpd->cDBCSrange = cpi->cDBCSrange;
                            lptr2->dirty = TRUE;

                    } else {

                        /*  Didn't work--back out the change.  */
                        cpd->cDBCSrange = OldRangeCnt;
                        invalid = TRUE;
                    }

                } else
                    invalid = TRUE;
        } /* check RNGECNT */

        /*
         * If we couldn't fix the rngecnt or both rngecnts are equal and invalid
         * we mark the cpdataent as invalid.
         */

        if (invalid || (cpd->cDBCSrange > maxvalue)) {

#ifdef CPTRACE
                printf ("We couldnt fix the RNGECNT field of a datasec\n");
                fflush (stdout);
#endif
                cpi->cDBCSrange = 0;
                return (FALSE);
        }


        /* We now check that the chksum is correct.
        */

        chksum = chk_cp_chksum((PBYTE)cpd,
                                sizeof (CPDATAENTRY) +
                                cpd->cDBCSrange * sizeof(DBCS_Range));

        if ((chksum != cps->cksCP[cpsno]) || (chksum != cpi->cksCP)) {

            if ((chksum != cps->cksCP[cpsno]) && (chksum != cpi->cksCP)) {

                /* The chksum isn't good. The only fields we can fix are
                 * cntry and cpid so we try setting them from the cpinfoent.
                 *
                 * Note that if changing them doesn't work, we have to
                 * change them back.
                 */
                OldCountry = cpd->CountryCode;
                OldCPID = cpd->CodePageID;
                cpd->CountryCode = cpi->CountryCode;
                cpd->CodePageID  = cpi->CodePageID;
                chksum = chk_cp_chksum((PBYTE)cpd,
                                                        sizeof(CPDATAENTRY) +
                                                        cpd->cDBCSrange * sizeof (DBCS_Range));

                lptr2->dirty = TRUE;           /* we dirtied the cpdataent */

                if (chksum != cps->cksCP[cpsno]) {

                    /* now is the chksum OK ? */
#ifdef CPTRACE
                    printf ("chksum invalid for CPDATAENTRY\n");
                    fflush (stdout);
#endif
                    /*
                     *  Changing these fields didn't fix it--
                     *  back the change out.
                     */
                    cpd->CountryCode = OldCountry;
                    cpd->CodePageID = OldCPID;

                    return (FALSE);
                }

            } else if (cps->cksCP[cpsno] != chksum) {

                /*  The checksum in the info sector was right--
                 *  use it to correct the data sector.
                 */
                cps->cksCP[cpsno] = chksum;
                lptr2->dirty = TRUE;

#ifdef CPTRACE
                printf ("chksum fixed for for CPDATASECTOR\n");
                fflush (stdout);
#endif
            } else if (cpi->cksCP != chksum) {

                /*
                 *  The checksum in the data sector was right--use
                 *  it to correct the info sector.
                 */
                cpi->cksCP = chksum;
                lptr->dirty = TRUE;

#ifdef CPTRACE
                printf ("chksum fixed for for CPINFOENTRY\n");
                fflush (stdout);
#endif
            }

        } /* check CHKSUM */

        /* Update the cpi_datasec field in the cpinfosec if necessary */

        if (cpi->lsnCPData != lptr2->lsn) {
                cpi->lsnCPData = lptr2->lsn;
                lptr->dirty = TRUE;
        }



        /* The cpdataent is valid so we check if we need to update the cpinfoent
        */

        if ((cpi->CountryCode != cpd->CountryCode) ||
                (cpi->CodePageID        != cpd->CodePageID)) {
                cpi->CountryCode = cpd->CountryCode;
                cpi->CodePageID  = cpd->CodePageID;
                lptr->dirty = TRUE;

#ifdef CPTRACE
                printf ("CNTRY or CPID fields fixed for CPINFOENTRY\n");
                fflush (stdout);
#endif
        }

        /* Everthing seems OK so process the data entry. First we allocate
         * memory for the tables.
         */

        chk_allocate_cp_tables (cpd->bCaseMapTable);

/* Fill in the corresponding dbcs flags table.
 */

/* Defect 64236  Virginia Romero OS/2 CHANGE TEAM */
/* Compiler causes problems when code is optimized, created temp variables */
/* Table below (chk_dbcs_flag) was not being set correctly... */

        for (k = 0; k < cpd->cDBCSrange; k++) {
                /* set lead-byte ranges */
                range = (DBCS_Range *)&cpd->DBCS_Range[k];
                start = range->CDIB_DBCS_start & 0x7f;
                end = range->CDIB_DBCS_end & 0x7f;
                for (i = start;
                         i <= end; i++)
                        chk_dbcs_flag[chk_cp_index][i] = TRUE;
        }

        return (TRUE);

} /* chk_process_cpdataent */


/***    chk_allocate_cp_tables - allocate memory cp tables
 *
 *      This function allocates the cp tables that will reside in memory
 *      during the chkdsk run. There are two types of tables that need
 *      to be initialized: 1) the casemap table specifying how to map
 *      characters from lowercase to uppercase, and 2) the dbcs lead-byte
 *      table specifying which dbcs characters are lead-bytes.
 *      We are passed a ptr to the cp table retrieved
 *      from the cpdataent which we will use to fill in the casemap table.
 *      The first 128 bytes of the 256-byte casemap table is constant for all
 *      tables, the last 128 bytes are taken from the 128-byte table in the
 *      cpdataent.
 *
 *      chk_allocate_cp_tables (cpd_table)
 *
 *      ENTRY           cpd_table - a ptr to the cp table taken from the cpdataent.
 *
 *      EXIT            None
 *
 *      CALLS           malloc
 *                              memset
 *                              _toupper
 *                              memcpy
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Allocates tables in memory
 *
 */

void chk_allocate_cp_tables (PUCHAR cpd_table)
{
        USHORT c;
        PUCHAR q;


        if (((chk_casemap[chk_cp_index]   = malloc (CASEMAP_SIZE)) == NULL) ||
                ((chk_dbcs_flag[chk_cp_index] = malloc (DBCSFLAG_SIZE)) == NULL)) {
#ifdef CPTRACE
                printf ("Not enough memory for casemap. indx = %d\n", chk_cp_index);
                fflush (stdout);
#endif
        }
        else                                    /* zero flags table */
                memset(chk_dbcs_flag[chk_cp_index], '\0', DBCSFLAG_SIZE);

        /* Set up the casemap table. We generate the first 128 entries, each
         * of which map to itself except for 'a'-'z' which
         * map to 'A'-'Z'. We then copy in the next 128 entries from
         * the table we get from the cpdataent.
         */

        for (c = 0, q=chk_casemap[chk_cp_index]; c < 128; c++, q++)
                *q = (UCHAR)c;

        for (c=(USHORT)'a', q=chk_casemap[chk_cp_index] + 'a'; c <= 'z'; c++, q++)
                *q = _toupper((UCHAR)c);

        memcpy (&chk_casemap[chk_cp_index][128], cpd_table, 128);

} /* chk_allocate_cp_tables */


/***    chk_zero_cpinfolist - zero a cpinfolist node preserving the data ptr
 *
 *      This function zeroes out a cpinfolist node but saves the ptr to
 *      the memory available for the next cp data sector.
 *
 *      chk_zero_cpinfolist (lptr)
 *
 *      ENTRY           lptr - ptr to the cpinfolist node.
 *
 *      EXIT            None
 *
 *      CALLS           memset
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */

void chk_zero_cpinfolist (PCPINFOLIST lptr)
{
        PUCHAR p = lptr->p.p;                 /* save data sector ptr */

        memset (lptr, '\0', sizeof (CPINFOLIST));
        lptr->p.p = p;                        /* restor the ptr */

} /* chk_zero_cpinfolist */


/***    chk_mark_cpdataent_invalid - mark a corrupted cpdataent as invalid
 *
 *      This function will mark a cpdataent and corresponding cpinfoent
 *      invalid because it was corrupted beyond recovery. The cpid and cntry
 *      field of both entries are set to 0xffffffff which indicates they
 *      are invalid. The filesystem may re-use invalid entries.
 *
 *      chk_mark_cpdataent_invalid (lptr, lptr2, cpi, cps)
 *
 *      ENTRY           lptr - ptr to the mem node containing the cpinfosec.
 *                              lptr2 - ptr to the mem node containing the cpdatasec.
 *                              cpi - ptr to the cpinfoent to be marked invalid.
 *                              cps - ptr to the cpdatasec containing the invalid cpdataent.
 *
 *      EXIT            None
 *
 *      CALLS           chk_cp_chksum
 *                              chk_cllocate_cp_tables
 *
 *      WARNINGS        Since the end and .. dirents use cp index 0, we cannot mark
 *                              it as invalid. Thus, if we are told to make the cpdataent for
 *                              index 0 invalid, we actually generate the index 0 memory
 *                              table that will allow us to process the end and .. dirents.
 *
 *      EFFECTS         None
 */

void chk_mark_cpdataent_invalid (PCPINFOLIST lptr, PCPINFOLIST lptr2,
 PCPINFOENTRY cpi, PCPDATASECTOR cps, USHORT offset)
{
        USHORT cps_no = CP_NO(CPPERDATASEC);
   PCPDATAENTRY cpd;
        USHORT c;
        PUCHAR q;
   USHORT maxvalue;             //PCR TRAP

        /* If the offset is 0 then there is no cpdataent present and we
         * must set the offset. Otherwise, there was a cpdataent there and
         * we are just marking it invalid.
         */

        if (!cps->offCPData[cps_no])
                cps->offCPData[cps_no] = sizeof(CPDATASECTOR) + cps_no*sizeof(CPDATAENTRY);

        cpd = (PCPDATAENTRY)((PBYTE)cps + cps->offCPData[cps_no]);

        /* We first check that the RNGECNT field is correct. Since our chksum
        * needs to use this value, we make sure it is correct first.
        */
        maxvalue = (BYTES_PER_CODEPAGE - cps->offCPData[cps_no] - sizeof(CPDATAENTRY)) //PCR TRAP
                                / sizeof (DBCS_Range) - 1;    /* -1 for 0,0 terminator */             //PCR TRAP

   if (cpd->cDBCSrange <= maxvalue)                                //PCR TRAP
   {                                                               //PCR TRAP
     cpi->cksCP = cps->cksCP[cps_no] =
                      chk_cp_chksum ((PBYTE)cpd,
                                      sizeof (CPDATAENTRY)
                                      + cpd->cDBCSrange*sizeof (DBCS_Range));
   }                                                               //PCR TRAP

        cpi->CountryCode = MARK_CP_CNTRY_INVALID;
        cpi->CodePageID  = MARK_CP_CPID_INVALID;


        /*
         *  If the estimated offset agrees with the data sector's
         *  offset entry, we'll go ahead and spoil the fields of
         *  the data entry, too.  (If it doesn't, we'll leave the
         *  data entry alone, because we aren't sure where it is).
         */
        if( offset == cps->offCPData[cps_no] ){
            cpd->CountryCode = MARK_CP_CNTRY_INVALID;
            cpd->CodePageID  = MARK_CP_CPID_INVALID;
        }

        lptr->dirty = lptr2->dirty = TRUE;

        chk_valid_cps[chk_cp_index] = FALSE;    /* mark cpdataent as invalid */


        /* At this point we see if we are invalidating the 0 cp index.
         * We must make a table for this because the '..', end dirent, and
         * recovered files with bad cp indices will have a cp index of 0.
         */

        if (!chk_cp_index) {

                for (c = 128, q=cpd->bCaseMapTable; c < 256; c++, q++)
                        *q = (UCHAR)c;

                chk_allocate_cp_tables (cpd->bCaseMapTable);
        }

} /* chk_mark_cpdataent_invalid */


/***    chk_create_cp - start re-building a lost cpinfosec or cpdatasec.
 *
 *      This function will create as much of a lost cpinfosec or cpdatasec as
 *      possible. We require doing this when such a structure is lost.
 *
 *      chk_create_cp (lptr, lptr2, cp_type)
 *
 *      ENTRY           lptr - ptr to mem node containing structure to be re-build
 *                              lptr2 - ptr to cpinfosec that will help us rebuild the
 *                                              cpdatasec from.
 *                              cp_type - indicates whether a cpinfosec or cpdatasec needs
 *                                                to be rebuilt.
 *
 *      EXIT            None
 *
 *      CALLS           memset
 *                              chk_zero_cpinfolist
 *                              chk_mark_cpdataent_invalid
 *                              allocate_early_sectors
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */

void chk_create_cp (PCPINFOLIST lptr, PCPINFOLIST lptr2, USHORT cp_type)
{
 PCPINFOSECTOR cp;
 PCPDATASECTOR cps;
        USHORT j;

#ifdef CPTRACE
        printf ("chk_create_cp: create a %s.\n", cp_types[cp_type]);
        fflush (stdout);
#endif

        /* fill in CPINFOLIST info */

        chk_zero_cpinfolist (lptr);

        lptr->lsn = allocate_early_sectors (SECTORS_PER_CODEPAGE,
                                                                                SECTORS_PER_CODEPAGE);
        lptr->index = chk_cp_index;
        lptr->dirty = TRUE;
        lptr->cp_type = cp_type;

        memset (lptr->p.p, '\0', BYTES_PER_CODEPAGE);


        if (cp_type == CP_FIND_CPINFOSEC) {   /* rebuild cpinfosec */

                cp = lptr->p.cp;

                /* Fill in CPINFOSECTOR header information */

                cp->sig         = CPINFOVAL;
                cp->cCodePage   = CP_LEFT(CPPERINFOSEC);
                cp->iFirstCP    = chk_cp_index;
        }
        else {  /* rebuild cpdatasec */

            cp = lptr2->p.cp;
            cps = lptr->p.cps;

            /* Fill in CPDATASECTOR header information */

            cps->sig        = CPDATAVAL;
            cps->cCodePage= CP_LEFT(CPPERDATASEC);
            cps->iFirstCP   = chk_cp_index;

            /* Mark the data entries as bad. */

            for (j = 0; j < CP_LEFT(CPPERDATASEC); j++, chk_cp_index++)  {

                chk_mark_cpdataent_invalid (lptr, lptr2,
                                &cp->CPInfoEnt[CP_NO(CPPERINFOSEC)], cps, 0);
                lptr2->dirty = TRUE;
            }
        }

} /* chk_create_cp */


/***    chk_write_cp - write a cp data sector to disk
 *
 *      This function writes a dirty cpinfosec or cpdatasec to disk. If
 *      the structure is a cpinfosec, then we update the sector ptr of
 *      the previous cpinfosec if necessary.
 *
 *      chk_write_cp (lptr, lastsec, cp_sec, cp_type)
 *
 *      ENTRY           lptr - ptr to mem node containing structure to write
 *                              lastsec - sector of last cpinfosec (or spare block) written
 *                              cp_sec - sector where we thought this data was located
 *                              cp_type - indicates whether writing cpinfosec or cpdatasec
 *
 *      EXIT            None
 *
 *      CALLS           chk_write_data
 *                              read_scratch
 *                              write_scratch
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */

void chk_write_cp (PCPINFOLIST lptr,
                                        ULONG lastsec,
                                        ULONG cp_sec,
                                        USHORT cp_type)
{
        ULONG sec;

        /* write data and check whether the write was hotfixed. */

        if (lptr->dirty &&
                ((sec=chk_write_data (lptr->lsn,
                                        lptr->p.p, SECTORS_PER_CODEPAGE, FALSE)) != lptr->lsn)) {

                if (!sec) {     /* sec == 0L so no disk space is left */
#ifdef TRACE
                        printf ("chk_write_cp: There is no disk space left to \n");
                        printf ("allocate the sector to.\n");
                        fflush (stdout);
#endif
                }
                else {  /* sec != 0L so the sector was reallocated */
                        lptr->lsn = sec;
#ifdef TRACE
                        printf ("chk_write_cp: There is no disk space left to \n");
                        printf ("allocate the sector to.\n");
                        fflush (stdout);
#endif
                }
        }

        /* check if the sector pointer to this cpinfosec needs to be updated */

        if ((cp_type == CP_FIND_CPINFOSEC) && (lptr->lsn != cp_sec)) {

                read_scratch (lastsec, lptr->p.p, SECTORS_PER_CODEPAGE);

                if (lastsec == SEC_SPAREB)
                    /* update Spare block pointer */
                    spb.lsnCPInfo = lptr->lsn;
                else
                    /* update previous link pointer */
                    lptr->p.cp->lsnNext = lptr->lsn;

                write_scratch (lastsec, lptr->p.p, SECTORS_PER_CODEPAGE);
        }

} /* chk_write_cp */


/***    chk_check_cpcnt - check count fields in cpinfosec and cpdatasec
 *
 *      This function checks that the count fields in the header info of
 *      a cpinfosec or cpdatasec are correct. This routine is only called
 *      after a cpinfosec or cpdatasec has been processed. If we find that
 *      the there are more cp entries than we previously thought, then we
 *      allocate more table space in memory to store the related cp info.
 *
 *      chk_check_cpcnt (lptr, lptr2, cp_type)
 *
 *      ENTRY           lptr -  ptr to mem node for cpinfosec
 *                              lptr2 - ptr to mem node for cpdatasec
 *                              cp_type - indicates whether to check cpinfosec or cpdatasec.
 *
 *      EXIT            None
 *
 *      CALLS           malloc
 *                              memcopy
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */

void chk_check_cpcnt (PCPINFOLIST lptr, PCPINFOLIST lptr2, USHORT cp_type)
{
        PUCHAR p;

#ifdef CPTRACE
        printf ("chk_check_cpcnt(): %s with chk_cp_index=%x\n",
                        cp_types[cp_type], chk_cp_index);
        fflush (stdout);
#endif

        chk_cp_index--;    /* the for loop incremeted this value */
                           /* before this routine was invoked so */
                           /* we have to go one back.            */

        if (cp_type == CP_FIND_CPINFOSEC) { /* check cpinfosec */

                if (lptr->p.cp->cCodePage != CP_NO(CPPERINFOSEC) + 1) {
#ifdef CPTRACE
                        printf ("chk_check_cpcnt: cCodePage fixed from %x to %x\n",
                                        lptr->p.cp->cCodePage, CP_NO(CPPERINFOSEC) + 1);
                        fflush (stdout);
#endif
                        lptr->p.cp->cCodePage = CP_NO(CPPERINFOSEC) + 1;
                        lptr->dirty = TRUE;
                }

                /* see if we need more memory for tables */

                if (chk_cp_count <= chk_cp_index + 1) {
                        chk_cp_count = CPCNT(4);
                        p = malloc (chk_cp_count * sizeof (UCHAR));
                        memcpy (p, chk_valid_cps, chk_cp_count * sizeof (UCHAR));
                        chk_valid_cps = p;
                        free (p);

                        p = malloc (chk_cp_count * sizeof (PUCHAR));
                        memcpy (p, *chk_casemap, chk_cp_count * sizeof (PUCHAR));
                        *chk_casemap = p;
                        free (p);

                        p = malloc (chk_cp_count * sizeof (PUCHAR));
                        memcpy (p, *chk_dbcs_flag, chk_cp_count * sizeof (PUCHAR));
                        *chk_dbcs_flag = p;
                        free (p);
                }

        }
        else
        if (cp_type == CP_FIND_CPDATASEC) { /* check cpdatasec */

                if (lptr2->p.cps->cCodePage != CP_NO(CPPERDATASEC) + 1) {
#ifdef CPTRACE
                        printf ("chk_check_cpcnt: cCodePage fixed from %x to %x\n",
                                        lptr2->p.cps->cCodePage, CP_NO(CPPERDATASEC) + 1);
                        fflush (stdout);
#endif
                        lptr2->p.cps->cCodePage = CP_NO(CPPERDATASEC) + 1;
                        lptr2->dirty = TRUE;
                }
        }
        else
        if (spb.culCP != chk_cp_index + 1) {
#ifdef CPTRACE
                        printf ("chk_check_cpcnt: culCP fixed from %x to %x\n",
                                        spb.culCP, chk_cp_index + 1);
                        fflush (stdout);
#endif
                spb.culCP = chk_cp_index + 1;
        }

        chk_cp_index++;       /* set value back to its original */

} /* chk_check_cpcnt */


/***    chk_process_cpinfosecs - process cpinfosecs and related cpdatasecs
 *
 *      This function reads a cpinfosec and processes it and its related
 *      cpdatasecs. Anything that is corrupted is fixed if possible. If fixing
 *      is not possible then the cp entries that are bad are marked invalid.
 *      If a cpinfosec or cpdatasec is invalid or lost, the disk will be
 *      scanned for it. If found, we link the structure back into the list.
 *      If we lose a cpinfosec, we rebuild it from the cpdatasec info. If
 *      we lose a cpdatasec, we can't rebuilt it so the related cp indices
 *      are marked invalid.
 *
 *      chk_process_cpinfosecs (void)
 *
 *      ENTRY           None
 *
 *      EXIT            None
 *
 *      CALLS           memset
 *                              memcmp
 *                              chk_create_cp
 *                              chk_check_cpcnt
 *                              chk_write_cp
 *                              chk_process_cpdataent
 *                              chk_mark_cpdataent_invalid
 *                              chk_get_cp
 *                              use_sectors
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */

void chk_process_cpinfosecs (void)
{
    PCPINFOLIST cpl, lptr, lptr2;
 PCPINFOSECTOR cp;
 PCPDATASECTOR cps;
 PCPDATAENTRY cpd;
 PCPINFOENTRY cpi;
    ULONG recover_lsn;
    USHORT j, k;
    USHORT cp_found, offset;
    USHORT new_cpdatasec = TRUE;
    ULONG cp_sec, cps_sec, last_cp_sec;
    /* BUGBUG - is nextsec set. */
    USHORT done, offsetOld;

#ifdef TRACE
   printf ("process_cpinfosecs ()\n");
   fflush (stdout);
#endif


    /*
     * Allocate character conversion tables and DBCS lead-byte tables.
     * Also allocate the valid code page map. This keeps track of the
     * codepages that we have been able to successfully process.
     */

    recover_lsn = SEC_START_DISK_SEARCH;
    cp_sec = spb.lsnCPInfo;
    chk_cp_index = 0;
    last_cp_sec = SEC_SPAREB;
    cpl = NULL;
    lptr = NULL;
    lptr2 = NULL;
    memset (scratchbuf, '\0', sizeof (CPINFOENTRY));


    do {    /* until finished processing cpinfosecs */

        /* We get the CPINFOSECTOR. If we can't find it, we have scanned the whole
         * disk. So we have in memory any CPDATASECTORs that might belong to this
         * CPINFOSECTOR. We MUST trust them. We fill in the CPINFOSECTOR entries
         * from the found CPDATASECTORS as we process them.
         *
         * If we have to create a new CPINFOSECTOR, for each CPDATASECTOR we found
         * the corresponding iCPVol in the infosec is set non-zero, so that
         * the (for j) loop below will not terminate immediately.
         */

        if (!(cp_found = chk_get_cp (&cpl, &lptr, cp_sec, &recover_lsn,
                                                     CP_FIND_CPINFOSEC))) {
            chk_create_cp (lptr, NULL, CP_FIND_CPINFOSEC);
            for (lptr2=cpl, j=0; lptr2; lptr2=lptr2->next, j++)
                lptr->p.cp->CPInfoEnt[j].iCPVol = 0xfe;
            lptr2=NULL;
        }


        use_sectors (lptr->lsn, (ULONG)SECTORS_PER_CODEPAGE, DISK_CONTROL);

        cp = lptr->p.cp;

        /* Loop through cpinfoents in the cpinfosec
         */

        for (j = 0; j < CPPERINFOSEC; ) {

            /* check if we are done processing (ie next cpinfoent is all 0's */

            if (done=!memcmp (&cp->CPInfoEnt[j], scratchbuf,
                                      sizeof (CPINFOENTRY))) {

                if (lptr->p.cp->lsnNext) {  /* ensure nextsec is 0 */
                    lptr->p.cp->lsnNext = 0L;
                    lptr->dirty = TRUE;
                }
                break;
            }

            cps_sec = cp->CPInfoEnt[chk_cp_index].lsnCPData;

            if (!chk_get_cp (&cpl, &lptr2, cps_sec, &recover_lsn,
                                                    CP_FIND_CPDATASEC))
                chk_create_cp (lptr2, lptr, CP_FIND_CPDATASEC);

            else {    /* we found the CPDATASECTOR */

                /* Process each cpdataent in the cpdatasec */

                cps = lptr2->p.cps;

                /* we set offset based on whether we went to a new cpdatasec or not */

                if (!CP_NO(CPPERDATASEC))
                    offset = sizeof (CPDATASECTOR);    /* offset to first CPDATAENTRY */
                else
                    offset = cps->offCPData[CP_NO(CPPERDATASEC)];


                /* process each cpdataent in the cpdatasec */

                for (k = CP_NO(CPPERDATASEC);
                     (k < CPPERDATASEC) && (j < CPPERINFOSEC) &&
                     memcmp (&cp->CPInfoEnt[j], scratchbuf, sizeof (CPINFOENTRY));
                     k++, j++, chk_cp_index++) {

                    cpi = &cp->CPInfoEnt[j];                          /* pt to current cpinfoent */

                    /* check cpinfoent information */

                    if (cpi->iCPVol != chk_cp_index) {
                        cpi->iCPVol = chk_cp_index;
                        lptr->dirty  = TRUE;
                    }

                    /*
                     *  NOTE:  it might be nice to check
                     *  the offset field--we might be able
                     *  to recover codepage data if the
                     *  offset is wrong.
                     */
                    cpd = (PCPDATAENTRY)((PBYTE)cps + cps->offCPData[k]);

                    if (!cp_found) {

                        /* CPINFOSECTOR not found so fill in the
                         * information entry from the CPDATASECTOR.
                         */

                        cpi->CountryCode  = cpd->CountryCode;
                        cpi->CodePageID   = cpd->CodePageID;
                        cpi->cksCP = cps->cksCP[k];
                        cpi->lsnCPData= lptr2->lsn; /* BUGBUG-make sure filled */
                        cpi->cDBCSrange= cpd->cDBCSrange;
                        /* cpinfosec set dirty above */
                    }

                    /* We process the cpdataent. If it is bad, we mark it invalid
                     * in the cpinfosec.
                     */

                    if (!chk_process_cpdataent (lptr, lptr2, cps, cpi, cpd)) {

            offsetOld = cps->offCPData[k];
            cps->offCPData[k] = offset;
                           cpd = (PCPDATAENTRY)((PBYTE)cps + cps->offCPData[k]);
                           lptr2->dirty = TRUE;

                           if (!chk_process_cpdataent (lptr, lptr2, cps, cpi, cpd)) {
                                        cps->offCPData[k] = offset;
                              chk_mark_cpdataent_invalid (lptr, lptr2, cpi, cps, offset);
                           }
                    }

                    offset += sizeof(CPDATAENTRY) +
                              cpd->cDBCSrange * sizeof(DBCS_Range);

                } /* for k */
            }

            chk_check_cpcnt (lptr, lptr2, CP_FIND_CPDATASEC);

            use_sectors (lptr2->lsn, (ULONG)SECTORS_PER_CODEPAGE, DISK_CONTROL);

            if (chk_delete_inconsistency)
                chk_write_cp (lptr2, last_cp_sec, cp_sec, CP_FIND_CPDATASEC);

        } /* for j */

        chk_check_cpcnt (lptr, lptr2, CP_FIND_CPINFOSEC);


        /* write out the cpinfosec if it is dirty */

        if (chk_delete_inconsistency)
            chk_write_cp (lptr, last_cp_sec, cp_sec, CP_FIND_CPINFOSEC);


        last_cp_sec = cp_sec;
        cp_sec = lptr->p.cp->lsnNext;

    } while (!done);

    chk_check_cpcnt (lptr, lptr2, CP_FIND_SPAREB);

} /* chk_process_cpinfosecs */


/***    chk_find_cp - scan the disk for a lost cp sector
 *
 *      This function will scan the disk trying to locate lost cp sectors.
 *      It starts searching at the place where it left off last. Once
 *      A cpinfosec or cpdatasec is found it copies it into the mem node
 *      and returns. Only the unused portions of the memory disk map is
 *      searched because we have already checked everything in the
 *      memory map so we know that cp sectors can't be residing there.
 *
 *      chk_find_cp (lptr, recover_lsn)
 *
 *      ENTRY           lptr - ptr to the mem node to put the recovered sector in.
 *                              recover_lsn - lsn to continue the search at.
 *
 *      EXIT            Return values:
 *                              CP_FIND_CPINFOSEC - if cpinfosec is found
 *                              CP_FIND_CPDATASEC - if cpdatasec if found
 *                              CP_FIND_NONE - if complete disk scanned without success
 *
 *      CALLS           strchr
 *                      memcmp
 *                      chk_check_dbcs
 *
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */

USHORT chk_find_cp (PCPINFOLIST *lptr,
                                        PULONG recover_lsn)
{
        register ULONG l;
        register ULONG diskmap;                                /* 69658 -rjp */
        UCHAR huge *bmp;

#ifdef CPTRACE
        printf ("chk_find_cp: recover_lsn = %08lx\n", *recover_lsn);fflush (stdout);
#endif

        (*lptr)->p.p = ALLOCATE_BLOCK (SECTORS_PER_CODEPAGE);   /* allocate memory */

        if (!(*lptr)->p.p)
                return (CP_FIND_NONE);         /* allocation failed */

        /* search through disk */

        for (l=*recover_lsn, bmp=bitmap+(*recover_lsn>>3),
                diskmap=*recover_lsn; l<number_of_sectors; l++,diskmap++) {  /* 69658 -rjp */

#ifdef CPTRACE
                printf ("lsn=%lx\r", l);
#endif

                if ((QueryHugeUseMapBits ( chk_disk_map, diskmap ) == DISK_UNKNOWN) &&  /* haven't seen this one */ /* 69658 -rjp */
                        !(*bmp & (1 << (l & 7)))) { /* and not free */

                        if (read_scratch (l, (*lptr)->p.p, SECTORS_PER_CODEPAGE)) {
#ifdef TRACE
                                printf ("chk_find_cp: read_scratch failed.\n"); fflush (stdout);
#endif
                        }

                        /* did we find a CPINFOSECTOR or a CPDATASECTOR ? */

                        else
                        if (((*lptr)->p.cp->sig == CPINFOVAL) ||
                                ((*lptr)->p.cps->sig == CPDATAVAL)) {

                                (*lptr)->lsn = l;    /* store where we found the sector */
                                *recover_lsn = l+1;  /* update where to resume search */

                                if ((*lptr)->p.cp->sig == CPINFOVAL) {

                                        (*lptr)->index = (USHORT)(*lptr)->p.cp->iFirstCP;
                                        (*lptr)->cp_type = CP_FIND_CPINFOSEC;
                                }
                                else {
                                        (*lptr)->index = (*lptr)->p.cps->iFirstCP;
                                        (*lptr)->cp_type = CP_FIND_CPDATASEC;
                                }

                                return ((*lptr)->cp_type); /* return pointer to memory */
                        }
                } /* saw unseen sector in bitmap */

                if (((USHORT)l & 7) == 7)       /* end of this bitmap byte? */
                        bmp++;                  /* yes, advance bitmap pointer */

        } /* for */

#ifdef CPTRACE
        printf ("\n");
#endif

        return (CP_FIND_NONE);  /* scanned the entire disk without success */

} /* chk_find_cpinfosecs */


/***    chk_set_cp_tables - set up the conversion tables to casemap characters
 *
 *      This function is the entry point to codepage checking. It allocates
 *      necessary memory to do the checking and does some intialization of
 *      global variables. It then calls chk_process_cpinfosecs to read in the
 *      codepage information, check it (possibly fix it), and store the
 *      required information in memory.
 *
 *      chk_set_cp_tables (void)
 *
 *      ENTRY           None
 *
 *      EXIT            None
 *
 *      CALLS           strchr
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */
void chk_set_cp_tables (void)
{
    USHORT c;
    PUCHAR q;

#ifdef TRACE
        printf ("chk_set_cp_tables () culCP=%lx\n", spb.culCP);
        fflush (stdout);
#endif

        if (!spb.culCP) {
                chk_casemap   = malloc (sizeof (PUCHAR));
                *chk_casemap = malloc(256);
                chk_dbcs_flag = malloc (sizeof (PBYTE));
                *chk_dbcs_flag = malloc(256);
                memset (chk_dbcs_flag[0], '\0', 256);
                chk_valid_cps = malloc (sizeof (BYTE));
                chk_valid_cps[0] = TRUE;
                for (c = 0, q=chk_casemap[0]; c <= 255; c++, q++)
                        *q = (UCHAR)c;
                chk_cp_count = 1;
                return;
        }

  /* Allocate character conversion tables and DBCS lead-byte tables.
   * Also allocate the valid code page map. This keeps track of the
   * codepages that we have been able to successfully process.
   * The memory allocated for these tables will be increased should
   * we find that there is more cp entries than culCP.
   */

        chk_cp_count = CPCNT(2);

#ifdef CPTRACE
        printf ("Cpcnt = %x\n", chk_cp_count);
        fflush (stdout);
#endif

        chk_casemap   = malloc ((chk_cp_count+1) * sizeof (PUCHAR));
        chk_dbcs_flag = malloc ((chk_cp_count+1) * sizeof (PUCHAR));
        chk_valid_cps = malloc ((chk_cp_count+1) * sizeof (BYTE));
        memset (chk_valid_cps, '\1', chk_cp_count+1); /* initially mark all cpds */
                                                    /* as valid. We mark a */
                                                    /* cpd invalid during checking. */

        chk_cp_recovery = FALSE;      /* Flag indicating whether we had to */
                                      /* do any disk scanning */
        chk_cp_index = 0;             /* The index of the current cp entry */
                                      /* being processed */
        chk_process_cpinfosecs ();    /* Process the codepage information. */


        /*
         *  Set up a valid codepage after the
         *  last codepage, in case all the codepages
         *  found were bad.
         */
        if ( chk_cp_index < chk_cp_count ) {

            chk_casemap [chk_cp_index] = malloc (256);
            chk_dbcs_flag [chk_cp_index] = malloc (256);
            memset (chk_dbcs_flag [chk_cp_index], '\0', 256);
            for (c = 0, q=chk_casemap[chk_cp_index]; c <= 255; c++, q++)
                    *q = (UCHAR)c;
            chk_cp_index++;

            /*
             *  Mark the rest invalid
             */
            for( c = chk_cp_index; c < chk_cp_count; c++)
                chk_valid_cps[c] = FALSE;

        } else {

            for (c=0; c<chk_cp_count; c++)
                    if (chk_valid_cps [c])
                            break;

            if (c == chk_cp_count) {
                    chk_casemap [chk_cp_count] = malloc (256);
                    chk_dbcs_flag [chk_cp_count] = malloc (256);
                    memset (chk_dbcs_flag [chk_cp_count], '\0', 256);
                    for (c = 0, q=chk_casemap[chk_cp_count]; c <= 255; c++, q++)
                            *q = (UCHAR)c;
                    chk_cp_count++;
            }
        }

#ifdef TRACE
        {
        USHORT temp;
        printf ("chk_valid_cps[] table:\n");
        for (temp = 0; temp < (USHORT)spb.culCP; temp++)
                if (temp == (temp / 10))
                        printf ("%1u", temp / 10);
        printf ("\n");

        for (temp = 0; temp < (USHORT)spb.culCP; temp++)
                printf ("%1u", temp % 10);
        printf ("\n");

        for (temp = 0; temp < (USHORT)spb.culCP; temp++)
                printf ("%1u", chk_valid_cps[temp]);
        printf ("\n");
        }
#endif

} /* chk_set_cp_tables */
