/*static char *SCCSID = "@(#)addcp.c    6.2 92/01/31";*/
#define BSWAP1 BSWAP1
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>
#include <cdib.h>

#include "chain.h"
#include "const.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "codepage.h"
#include "superb.h"
#include "pbmacros.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "heap.h"
#include "storage.h"
#include "tables.h"
#include "log.h"                                                 // @76193

#include "pr_cache.h"
#include "pr_fsini.h"
#include "pr_heap.h"

// @76193   #ifdef DEBUG
#include "pr_debug.h"
// @76193   #endif


/*
 ***    AddCodePages - Add volume's code pages to system code pages
 *
 *      ENTRY   pVol - pointer to VOLTAB being mounted
 *
 *      EXIT    Returns an error code
 *
 *      HISTORY
 *  @76193  23-Nov-Aug-1993 by D.Azzarito - Change holds for LOG support
 *          11-Aug-1989 by JR (John Rogers, JohnRo@Microsoft)
 *              - PTR B705990: Decrease stack usage.
 */

USHORT
AddCodePages (PVOLTAB pVol, PFRAME pfrm)
{
    USHORT usRet;
    UINT iCPVol;                        /* index of volume's code pages */
    LSN lsnCPInfo;
    PCPINFOSECTOR pCPInfoSec;
    PBUFNODE pbufCPInfo;
#if 0
    /* BUGBUG - this is never used - see note at end of routine. */
    UINT cCPSysSave = cCodePage;        /* save original count - for cleanup */
#endif

#ifdef DEBUG_CODEPAGE
    dprintf (__FILE__": volume has %d code pages\n", pVol->cCodePage);
#endif

    if (pVol->cCodePage == 0) {
        /* no code pages on the volume (yet) - nothing to do */
        return NO_ERROR;
    }

    if (pVol->cCodePage > CMAXCODEPAGES) {
#ifdef DEBUG
      dprintf ("%s (%d): More than %d code pages\n",
               __FILE__, __LINE__, CMAXCODEPAGES);

#endif
      return ERROR_INVALID_DATA;
    }

    if ((pVol->paiCodePage = GHS(pVol->cCodePage *
                                 sizeof (BYTE),pfrm)) == NULL) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    /* from this point on, we have to do some common clean up before exiting */

    /* fill in pVol->paiCodePage with 0xFFs so we know we haven't found them */
    memset (pVol->paiCodePage, 0xFF, pVol->cCodePage * sizeof (BYTE));

    /* read in the spares block to get the code page info sector lsn */
    {
        PSPAREB psb;
        PBUFNODE pbufsb;
        if ((psb = RdBuf(SEC_SPAREB,BP_TOSS,&pbufsb,pfrm)) == NULL) {
#ifdef DEBUG
            dprintf (__FILE__": RdBuf (SEC_SPAREB) returned NULL\n");
            Int3 ();
#endif
            usRet = GetHardErr (ERROR_READ_FAULT);
            goto acp900;
        }

//#114986
#ifndef MINIFSD
        if (pbufsb->fFlag & BFL_LZREAD) {
          if (LazyRead (pbufsb, pfrm)) {
            usRet = GetHardErr (ERROR_READ_FAULT);
            goto acp900;
          }
        }
#endif

        lsnCPInfo = psb->lsnCPInfo;

    } /* scope of psb, pbufsb */

    iCPVol = 0;

    do {
        /* read in the next code page info sector */

        UINT iCP;                           /* index in this cp info sec */

        iCP = iCPVol % CPPERINFOSEC;

        /* if lsnCPInfo exceeds disk size, quit - let chkdsk figure it out */
        if (lsnCPInfo >= pVol->cSectsOnVol) {
            usRet = ERROR_INVALID_DATA;
            goto acp900;
        }

        if ((pCPInfoSec = RdBuf(lsnCPInfo,BP_TOSS,&pbufCPInfo,pfrm)) == NULL) {
#ifdef DEBUG
            dprintf (__FILE__": RdBuf (lsnCPInfo) returned NULL\n");
            Int3 ();
#endif
            usRet = ERROR_READ_FAULT;
            goto acp900;
        }

//#114986
#ifndef MINIFSD
        if (pbufCPInfo->fFlag & BFL_LZREAD) {
          if (LazyRead (pbufCPInfo, pfrm)) {
            usRet = GetHardErr (ERROR_READ_FAULT);
            goto acp900;
          }
        }
#endif

        if (pCPInfoSec->sig != CPINFOVAL) {
#ifdef DEBUG
            dprintf (__FILE__": pCPInfoSec (%x) - Sig invalid\n", lsnCPInfo);
            Int3 ();
#endif
            usRet = ERROR_INVALID_HANDLE;
            goto acp900;
        }

        do {
            UINT iCPSys;

#ifdef DEBUG_CODEPAGE
                    dprintf (__FILE__": looking for vol CP %d (%d, %d)\n",
                             iCPVol,
                             pCPInfoSec->CPInfoEnt [iCP].CountryCode,
                             pCPInfoSec->CPInfoEnt [iCP].CodePageID);

#endif
            /* search the system code pages - looking for matches */
            for (iCPSys = 0; iCPSys < cCodePage; iCPSys++) {
                if ((paCodePageEnt [iCPSys].CodePageID ==
                                 pCPInfoSec->CPInfoEnt [iCP].CodePageID) &&
                    (paCodePageEnt [iCPSys].CountryCode ==
                                  pCPInfoSec->CPInfoEnt [iCP].CountryCode)) {

#ifdef DEBUG_CODEPAGE
                    dprintf (__FILE__": found vol CP in sys slot %d\n", iCPSys);
#endif
                    pVol->paiCodePage [iCPVol] = (BYTE) iCPSys;
                    goto acp_Next;
                }
            }

            /* add code page iCPVol to the system code pages */
#ifdef DEBUG_CODEPAGE
            dprintf (__FILE__": adding vol CP (%d) to sys slot %d\n",
                     iCPVol, cCodePage);
#endif
            {
                PCPENTRY paTemp;
                PCPINFOENTRY pCPInfoEnt;
                PCPDATAENTRY pCPDataEnt;
                PBUFNODE pbufCPData;
                USHORT cbSave;

                pCPInfoEnt = &pCPInfoSec->CPInfoEnt [iCP];

                IncrHoldsBUF (pbufCPInfo);  // @76193

                {
                    PCPDATASECTOR pCPDataSec;

                    /* If pCPInfoEnt->lsnCPData exceeds disk size, quit -
                     * Let chkdsk figure it out
                     */
                    if (pCPInfoEnt->lsnCPData >= pVol->cSectsOnVol) {
                        usRet = ERROR_INVALID_DATA;
                        DecrHoldsBUF (pbufCPInfo);  // @76193
                        goto acp900;
                    }

                    if ((pCPDataSec = RdBuf(pCPInfoEnt->lsnCPData, BP_TOSS,
                                            &pbufCPData, pfrm)) == NULL) {
#ifdef DEBUG
                        dprintf (__FILE__": RdBuf (lsnCPData) returned NULL\n");
                        Int3 ();
#endif
                        usRet = ERROR_READ_FAULT;
                        goto acp800;
                    }

//#114986
#ifndef MINIFSD
                    if (pbufCPData->fFlag & BFL_LZREAD) {
                      if (LazyRead (pbufCPData, pfrm)) {
                        usRet = GetHardErr (ERROR_READ_FAULT);
                        goto acp800;
                      }
                    }
#endif

                    if (pCPDataSec->sig != CPDATAVAL) {
#ifdef DEBUG
                        dprintf (__FILE__": pCPDataSec (%x) - Sig invalid\n",
                                 pCPInfoEnt->lsnCPData);
                        Int3 ();
#endif
                        usRet = ERROR_INVALID_HANDLE;
                        DecrHoldsBUF (pbufCPInfo);  // @76193
                        goto acp900;
                    }

                    pCPDataEnt = (PCPDATAENTRY) ((PBYTE) pCPDataSec +
                                                 pCPDataSec->offCPData
                                                 [iCPVol % CPPERDATASEC]);
                }

                IncrHoldsBUF (pbufCPData);  // @76193

                {
                    UINT cNeeded = 1;

                    /* resize xlat table segment */
                    if (pCPInfoEnt->cDBCSrange > 0) {
                        cNeeded++;
                    }

                    /* save old size, just in case of error */
                    cbSave = OFFSETOF (pCodePageSeg);

                    MAYBLOCK (usRet = FSH_SEGREALLOC (SELECTOROF(pCodePageSeg),
                                                      (ULONG)
                                                      OFFSETOF (pCodePageSeg) +
                                                      (256 * cNeeded)));

                    if (usRet != NO_ERROR) {
#ifdef DEBUG
                        dprintf (__FILE__": FSH_SEGREALLOC error: %d\n", usRet);
                        Int3 ();
#endif
                        goto acp790;
                    }
                }

                /* resize heap for code page information */
#ifdef DEBUG
                if (CMAXCODEPAGES * sizeof (CPENTRY) >= 0x8000) {
                    dprintf (__FILE__" (%d): CPENTRY too big (%d) for GHS\n",
                             __LINE__, sizeof (CPENTRY));
                    Int3 ();
                }
#endif
                if ((paTemp = GHS((cCodePage + 1) *
                                   sizeof (CPENTRY),pfrm)) == NULL) {
#ifdef DEBUG
                    dprintf (__FILE__": GHS returned NULL\n");
                    Int3 ();
#endif
                    usRet = ERROR_NOT_ENOUGH_MEMORY;
                    goto acp790;
                }


                /* we got everything we need and won't block */

                /* unhold the buffers */

                DecrHoldsBUF (pbufCPData);  // @76193
                DecrHoldsBUF (pbufCPInfo);  // @76193

                /* copy old code page info into new heap area and release old */
                memcpy (paTemp, paCodePageEnt, cCodePage * sizeof (CPENTRY));
                RetHeap (paCodePageEnt);
                paCodePageEnt = paTemp;

                /* point to the new code page entry */
                paTemp = &paCodePageEnt [cCodePage];

                /* build xlat table */
                {
                    UINT iByte;

                    for (iByte = 0; iByte < 128; iByte++)
                        pCodePageSeg [iByte] = (BYTE) iByte;

                    for (iByte = 'a'; iByte <= 'z'; iByte++)
                        pCodePageSeg [iByte] -= ('a' - 'A');

                    memcpy (&pCodePageSeg [128], pCPDataEnt->bCaseMapTable, 128);
                }

                paTemp->pCaseTable = pCodePageSeg;
                pCodePageSeg += 256;

                paTemp->CountryCode = pCPInfoEnt->CountryCode;
                paTemp->CodePageID = pCPInfoEnt->CodePageID;

                if (pCPInfoEnt->cDBCSrange == 0) {
                    paTemp->bFlags = 0;
                } else {
                    struct CDIB_DBCS_range_section far * pDBCSRange;
                    USHORT iRange;
                    USHORT iByte;

                    paTemp->bFlags = CPF_DBCS;

                    memset (pCodePageSeg, 0, 256);

                    pDBCSRange = pCPDataEnt->DBCS_Range;

                    for (iRange = 0; iRange < pCPInfoEnt->cDBCSrange;
                         iRange++, pDBCSRange++) {
                        for (iByte = pDBCSRange->CDIB_DBCS_start;
                             iByte <= pDBCSRange->CDIB_DBCS_end;
                             iByte++) {
                            pCodePageSeg [iByte] = DBCSLead;
                        }
                    }

                    paTemp->oDBCSTable = OFFSETOF (pCodePageSeg);
                    pCodePageSeg += 256;
                }

                pVol->paiCodePage [iCPVol] = (BYTE) cCodePage;

                cCodePage++;

                goto acp_Next;

                /* Error cleanup code (for this scope) */

acp790:
                DecrHoldsBUF (pbufCPData);  // @76193

acp800:
                DecrHoldsBUF (pbufCPInfo);  // @76193

                if (cbSave != cCodePage) {
                    MAYBLOCK (FSH_SEGREALLOC (SELECTOROF(pCodePageSeg),
                                              (ULONG) cbSave));
                    cCodePage = cbSave;
                }

                if (pVol->paiCodePage != NULL) {
                    RetHeap (pVol->paiCodePage);
                }

                goto acp900;

            }   /* end of scope for paTemp */

acp_Next:
            iCPVol++;
        } while (++iCP < (UINT) pCPInfoSec->cCodePage);

        lsnCPInfo = pCPInfoSec->lsnNext;

    } while (iCPVol < pVol->cCodePage);

    return NO_ERROR;

acp900:
    /*
     * BUGBUG: after discussion with Mark Hitch, perhaps this should
     * (1) reset the number of code pages, and (2) resize the segment.
     * --JR (John Rogers, 03-Aug-89)
     */
    RetHeap (pVol->paiCodePage);
    return (usRet);
}
