Ignore:
Timestamp:
Jun 1, 2001, 3:21:13 AM (24 years ago)
Author:
phaller
Message:

Revert to old but optimized loader

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kernel32/winimagepeldr.cpp

    r5837 r5848  
    1 /* $Id: winimagepeldr.cpp,v 1.77 2001-05-30 08:23:21 sandervl Exp $ */
     1/* $Id: winimagepeldr.cpp,v 1.78 2001-06-01 01:21:12 phaller Exp $ */
    22
    33/*
     
    5555#include <wprocess.h>
    5656
     57
    5758//Define COMMIT_ALL to let the pe loader commit all sections of the image
    5859//This is very useful during debugging as you'll get lots of exceptions
     
    110111    nrsections(0), imageSize(0), dwFlags(0), section(NULL),
    111112    imageVirtBase(-1), realBaseAddress(0), imageVirtEnd(0),
    112     nrNameExports(0), nrOrdExports(0), nameexports(NULL), ordexports(NULL),
    113     memmap(NULL), pFixups(NULL), dwFixupSize(0), curnameexport(NULL), curordexport(NULL)
     113    nrNameExports(0), nrOrdExports(0),
     114    nameexports(NULL), ordexports(NULL),
     115    curnameexport(NULL), curordexport(NULL),
     116    memmap(NULL), pFixups(NULL), dwFixupSize(0)
    114117{
    115118 HFILE  dllfile;
     
    168171BOOL Win32PeLdrImage::init(ULONG reservedMem)
    169172{
     173    PERF_START(init)
     174
    170175 LPVOID win32file = NULL;
    171176 ULONG  filesize, ulRead, ulNewPos;
     
    217222    //Allocate memory to hold the entire image
    218223    if(allocSections(reservedMem) == FALSE) {
    219         dprintf((LOG, "Failed to allocate image memory for %s at %x, rc %d", szFileName, oh.ImageBase, errorState));;
     224        dprintf((LOG, "Failed to allocate image memory for %s at %x, rc %d", szFileName, oh.ImageBase, errorState));
    220225        goto failure;
    221226    }
     
    650655        }
    651656    }
     657
     658    PERF_ELAPSED(init, "Win32PeLdrImage::init", szFileName)
     659
     660
    652661    return(TRUE);
    653662
     
    688697//
    689698//******************************************************************************
     699
     700#define DOSREAD_IDEAL_SIZE 61440
     701static inline APIRET _Optlink fastDosRead(HFILE hFile,
     702                                          PVOID pAddress,
     703                                          ULONG ulSize,
     704                                          PULONG pulBytesRead)
     705{
     706    /* we better break the DosRead into multiple calls */
     707    PBYTE  p = (PBYTE)pAddress;
     708    ULONG  ulReadBytes;
     709    APIRET rc;
     710
     711    *pulBytesRead = ulSize;
     712
     713    do
     714    {
     715        rc = DosRead(hFile,
     716                     p,
     717                     min(DOSREAD_IDEAL_SIZE, ulSize),
     718                     &ulReadBytes);
     719        if (rc != NO_ERROR)
     720        {
     721            /* in case of errors bail out */
     722            *pulBytesRead = 0;
     723            return rc;
     724        }
     725
     726        ulSize -= ulReadBytes;
     727        p += ulReadBytes;
     728    }
     729    while (ulSize > 0);
     730
     731    return NO_ERROR;
     732}
     733
     734
    690735BOOL Win32PeLdrImage::commitPage(ULONG virtAddress, BOOL fWriteAccess, int fPageCmd)
    691736{
     
    783828            goto fail;
    784829        }
    785         rc = DosRead(hFile, (PVOID)virtAddress, size, &ulRead);
     830
     831        // 2001-05-31 PH
     832        // ensure DosRead() does not have to read more
     833        // than 65535 bytes, otherwise split into two requests!
     834        rc = fastDosRead(hFile, (PVOID)virtAddress, size, &ulRead);
    786835        if(rc) {
    787836            DosExitCritSec();
     
    12771326BOOL Win32PeLdrImage::processExports(char *win32file)
    12781327{
    1279  IMAGE_SECTION_HEADER    sh;
    1280  PIMAGE_EXPORT_DIRECTORY ped;
    1281  ULONG *ptrNames, *ptrAddress;
    1282  USHORT *ptrOrd;
    1283  BOOL fForwarder;
    1284  int i;
     1328  PERF_START(processexports)
     1329
     1330  IMAGE_SECTION_HEADER    sh;
     1331  PIMAGE_EXPORT_DIRECTORY ped;
     1332  ULONG *ptrNames, *ptrAddress;
     1333  USHORT *ptrOrd;
     1334  BOOL fForwarder;
     1335  int i;
    12851336
    12861337  /* get section header and pointer to data directory for .edata section */
    12871338  if((ped = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryOffset
    1288      (win32file, IMAGE_DIRECTORY_ENTRY_EXPORT)) != NULL &&
    1289      GetSectionHdrByImageDir(win32file, IMAGE_DIRECTORY_ENTRY_EXPORT, &sh) ) {
    1290 
    1291         dprintf((LOG, "Exported Functions: " ));
     1339      (win32file, IMAGE_DIRECTORY_ENTRY_EXPORT)) != NULL &&
     1340     GetSectionHdrByImageDir(win32file, IMAGE_DIRECTORY_ENTRY_EXPORT, &sh) )
     1341  {
     1342
     1343    dprintf((LOG, "Exported Functions: " ));
    12921344    ptrOrd     = (USHORT *)((ULONG)ped->AddressOfNameOrdinals +
    12931345                            (ULONG)win32file);
    12941346    ptrNames   = (ULONG *)((ULONG)ped->AddressOfNames +
    1295                             (ULONG)win32file);
     1347                           (ULONG)win32file);
    12961348    ptrAddress = (ULONG *)((ULONG)ped->AddressOfFunctions +
    1297                             (ULONG)win32file);
     1349                           (ULONG)win32file);
    12981350    nrOrdExports  = ped->NumberOfFunctions;
    12991351    nrNameExports = ped->NumberOfNames;
    1300 
     1352   
    13011353    int   ord, RVAExport;
    13021354    char *name;
    13031355    for(i=0;i<ped->NumberOfNames;i++)
    13041356    {
    1305     fForwarder = FALSE;
    1306         ord        = ptrOrd[i] + ped->Base;
    1307         name       = (char *)((ULONG)ptrNames[i] + (ULONG)win32file);
    1308         RVAExport  = ptrAddress[ptrOrd[i]];
    1309 
    1310         /* forwarder? ulRVA within export directory. */
    1311         if(RVAExport > oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress &&
    1312            RVAExport < oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
    1313                        + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
    1314         {
    1315             fForwarder = AddForwarder(oh.ImageBase + RVAExport, name, ord);
    1316         }
    1317     if(!fForwarder) {
    1318             //points to code (virtual address relative to oh.ImageBase
    1319             AddNameExport(oh.ImageBase + RVAExport, name, ord);
    1320             dprintf((LOG, "address 0x%x %s @%d (0x%08x)", RVAExport, name, ord, realBaseAddress + RVAExport));
    1321         }
    1322     }
     1357      fForwarder = FALSE;
     1358      ord        = ptrOrd[i] + ped->Base;
     1359      name       = (char *)((ULONG)ptrNames[i] + (ULONG)win32file);
     1360      RVAExport  = ptrAddress[ptrOrd[i]];
     1361
     1362      /* forwarder? ulRVA within export directory. */
     1363      if(RVAExport > oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress &&
     1364         RVAExport < oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
     1365         + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
     1366      {
     1367        fForwarder = AddForwarder(oh.ImageBase + RVAExport, name, ord);
     1368      }
     1369     
     1370      if(!fForwarder)
     1371      {
     1372        //points to code (virtual address relative to oh.ImageBase
     1373        AddNameExport(oh.ImageBase + RVAExport, name, ord);
     1374        dprintf((LOG, "address 0x%x %s @%d (0x%08x)", RVAExport, name, ord, realBaseAddress + RVAExport));
     1375      }
     1376    }
     1377   
    13231378    for(i=0;i<max(ped->NumberOfNames,ped->NumberOfFunctions);i++)
    13241379    {
    1325     fForwarder = FALSE;
    1326         ord = ped->Base + i;  //Correct??
    1327         RVAExport = ptrAddress[i];
    1328         /* forwarder? ulRVA within export directory. */
    1329         if(RVAExport > oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress &&
    1330            RVAExport < oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
    1331                        + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
    1332         {
    1333             fForwarder = AddForwarder(oh.ImageBase + RVAExport, NULL, ord);
    1334         }
    1335     if(!fForwarder && RVAExport) {
    1336             //points to code (virtual address relative to oh.ImageBase
    1337             dprintf((LOG, "ord %d at 0x%08x (0x%08x)", ord, RVAExport, realBaseAddress + RVAExport));
    1338             AddOrdExport(oh.ImageBase + RVAExport, ord);
    1339         }
    1340     }
    1341   }
     1380      fForwarder = FALSE;
     1381      ord = ped->Base + i;  //Correct??
     1382      RVAExport = ptrAddress[i];
     1383      /* forwarder? ulRVA within export directory. */
     1384      if(RVAExport > oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress &&
     1385         RVAExport < oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
     1386         + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
     1387      {
     1388        fForwarder = AddForwarder(oh.ImageBase + RVAExport, NULL, ord);
     1389      }
     1390     
     1391      if(!fForwarder && RVAExport)
     1392      {
     1393        //points to code (virtual address relative to oh.ImageBase
     1394        dprintf((LOG, "ord %d at 0x%08x (0x%08x)", ord, RVAExport, realBaseAddress + RVAExport));
     1395        AddOrdExport(oh.ImageBase + RVAExport, ord);
     1396      }
     1397    }
     1398  }
     1399
     1400  PERF_ELAPSED(processexports, "Win32PeLdrImage::processExports", getModuleName())
     1401
    13421402  return(TRUE);
    13431403}
     
    13461406void Win32PeLdrImage::AddNameExport(ULONG virtaddr, char *apiname, ULONG ordinal, BOOL fAbsoluteAddress)
    13471407{
    1348  ULONG nsize;
    1349 
    1350     if(nameexports == NULL) {
    1351         nameExportSize= 4096;
    1352         nameexports   = (NameExport *)malloc(nameExportSize);
    1353         curnameexport = nameexports;
    1354     }
    1355     nsize = (ULONG)curnameexport - (ULONG)nameexports;
    1356     if(nsize + sizeof(NameExport) + strlen(apiname) > nameExportSize) {
    1357         nameExportSize += 4096;
    1358         char *tmp = (char *)nameexports;
    1359         nameexports = (NameExport *)malloc(nameExportSize);
    1360         memcpy(nameexports, tmp, nsize);
    1361         curnameexport = (NameExport *)((ULONG)nameexports + nsize);
    1362         free(tmp);
    1363     }
    1364     if(fAbsoluteAddress) {//forwarders use absolute address
    1365         curnameexport->virtaddr = virtaddr;
    1366     }
    1367     else curnameexport->virtaddr = realBaseAddress + (virtaddr - oh.ImageBase);
    1368     curnameexport->ordinal  = ordinal;
    1369     *(ULONG *)curnameexport->name = 0;
    1370     strcpy(curnameexport->name, apiname);
    1371 
    1372     curnameexport->nlength = strlen(apiname) + 1;
    1373     if(curnameexport->nlength < sizeof(curnameexport->name))
    1374         curnameexport->nlength = sizeof(curnameexport->name);
    1375 
    1376     curnameexport = (NameExport *)((ULONG)curnameexport->name + curnameexport->nlength);
    1377 }
    1378 //******************************************************************************
    1379 //******************************************************************************
    1380 void Win32PeLdrImage::AddOrdExport(ULONG virtaddr, ULONG ordinal, BOOL fAbsoluteAddress)
    1381 {
    1382     if(ordexports == NULL) {
    1383         ordexports   = (OrdExport *)malloc(nrOrdExports * sizeof(OrdExport));
    1384         curordexport = ordexports;
    1385     }
    1386     if(fAbsoluteAddress) {//forwarders use absolute address
    1387         curordexport->virtaddr = virtaddr;
    1388     }
    1389     else curordexport->virtaddr = realBaseAddress + (virtaddr - oh.ImageBase);
    1390 
    1391     curordexport->ordinal  = ordinal;
    1392     curordexport++;
    1393 }
    1394 //******************************************************************************
    1395 //******************************************************************************
    1396 BOOL Win32PeLdrImage::AddForwarder(ULONG virtaddr, char *apiname, ULONG ordinal)
    1397 {
    1398  char         *forward = (char *)(realBaseAddress + (virtaddr - oh.ImageBase));
    1399  char         *forwarddll, *forwardapi;
    1400  Win32DllBase *WinDll;
    1401  DWORD         exportaddr;
    1402  int           forwardord;
    1403 
    1404     forwarddll = strdup(forward);
    1405     if(forwarddll == NULL) {
    1406         return FALSE;
    1407     }
    1408     forwardapi = strchr(forwarddll, '.');
    1409     if(forwardapi == NULL) {
    1410         goto fail;
    1411     }
    1412     *forwardapi++ = 0;
    1413     if(strlen(forwarddll) == 0 || strlen(forwardapi) == 0) {
    1414         goto fail;
    1415     }
    1416     WinDll = Win32DllBase::findModule(forwarddll);
    1417     if(WinDll == NULL) {
    1418         WinDll = loadDll(forwarddll);
    1419         if(WinDll == NULL) {
    1420             dprintf((LOG, "ERROR: couldn't find forwarder %s.%s", forwarddll, forwardapi));
    1421             goto fail;
    1422         }
    1423     }
    1424     //check if name or ordinal forwarder
     1408  ULONG nsize;
     1409  int iApiNameLength = strlen(apiname);
     1410
     1411  if(nameexports == NULL)
     1412  {
     1413    nameExportSize= 4096;
     1414    nameexports   = (NameExport *)malloc(nameExportSize);
     1415    curnameexport = nameexports;
     1416  }
     1417 
     1418  nsize = (ULONG)curnameexport - (ULONG)nameexports;
     1419  if(nsize + sizeof(NameExport) + iApiNameLength > nameExportSize)
     1420  {
     1421    nameExportSize += 4096;
     1422    char *tmp = (char *)nameexports;
     1423    nameexports = (NameExport *)malloc(nameExportSize);
     1424    memcpy(nameexports, tmp, nsize);
     1425    curnameexport = (NameExport *)((ULONG)nameexports + nsize);
     1426    free(tmp);
     1427  }
     1428 
     1429  if(fAbsoluteAddress) //forwarders use absolute address
     1430    curnameexport->virtaddr = virtaddr;
     1431  else
     1432    curnameexport->virtaddr = realBaseAddress + (virtaddr - oh.ImageBase);
     1433 
     1434  curnameexport->ordinal  = ordinal;
     1435  *(ULONG *)curnameexport->name = 0;
     1436  strcpy(curnameexport->name, apiname);
     1437
     1438  curnameexport->nlength = iApiNameLength + 1;
     1439  if(curnameexport->nlength < sizeof(curnameexport->name))
     1440    curnameexport->nlength = sizeof(curnameexport->name);
     1441
     1442  curnameexport = (NameExport *)((ULONG)curnameexport->name + curnameexport->nlength);
     1443}
     1444//******************************************************************************
     1445//******************************************************************************
     1446void Win32PeLdrImage::AddOrdExport(ULONG virtaddr,
     1447                                   ULONG ordinal,
     1448                                   BOOL fAbsoluteAddress)
     1449{
     1450  if(ordexports == NULL)
     1451  {
     1452    ordexports   = (OrdExport *)malloc(nrOrdExports * sizeof(OrdExport));
     1453    curordexport = ordexports;
     1454  }
     1455 
     1456  if(fAbsoluteAddress) //forwarders use absolute address
     1457    curordexport->virtaddr = virtaddr;
     1458  else
     1459    curordexport->virtaddr = realBaseAddress + (virtaddr - oh.ImageBase);
     1460
     1461  curordexport->ordinal  = ordinal;
     1462  curordexport++;
     1463}
     1464//******************************************************************************
     1465//******************************************************************************
     1466BOOL Win32PeLdrImage::AddForwarder(ULONG virtaddr,
     1467                                   char *apiname,
     1468                                   ULONG ordinal)
     1469{
     1470  char         *forward = (char *)(realBaseAddress + (virtaddr - oh.ImageBase));
     1471  char         *forwarddll, *forwardapi, *forwardapi0;
     1472  Win32DllBase *WinDll;
     1473  DWORD         exportaddr;
     1474  int           forwardord;
     1475  int           iForwardApiLength; /* save strlen result */
     1476   
     1477// 2001-06-01 PH
     1478// we do exactly know which character we replace: the "." is zeroed
     1479// so we can cheaply restore the name at the end of the method.
     1480//  forwarddll = strdup(forward);
     1481//  if(forwarddll == NULL)
     1482//  {
     1483//    return FALSE;
     1484//  }
     1485  forwarddll = forward;
     1486   
     1487  forwardapi = strchr(forwarddll, '.');
     1488  if(forwardapi == NULL)
     1489  {
     1490    goto fail;
     1491  }
     1492  *forwardapi0 = 0;
     1493  forwardapi=forwardapi0++;
     1494  iForwardApiLength = strlen(forwardapi);
     1495 
     1496  if(strlen(forwarddll) == 0 || iForwardApiLength == 0)
     1497  {
     1498    goto fail;
     1499  }
     1500 
     1501  WinDll = Win32DllBase::findModule(forwarddll);
     1502  if(WinDll == NULL)
     1503  {
     1504    WinDll = loadDll(forwarddll);
     1505    if(WinDll == NULL)
     1506    {
     1507      dprintf((LOG, "ERROR: couldn't find forwarder %s.%s", forwarddll, forwardapi));
     1508      goto fail;
     1509    }
     1510  }
     1511 
     1512  //check if name or ordinal forwarder
     1513  if(*forwardapi >= '0' && *forwardapi <= '9')
     1514  {
     1515    forwardord = atoi(forwardapi);
     1516  }
     1517  else
    14251518    forwardord = 0;
    1426     if(*forwardapi >= '0' && *forwardapi <= '9') {
    1427         forwardord = atoi(forwardapi);
    1428     }
    1429     if(forwardord != 0 || (strlen(forwardapi) == 1 && *forwardapi == '0')) {
    1430         exportaddr = WinDll->getApi(forwardord);
    1431     }
    1432     else  exportaddr = WinDll->getApi(forwardapi);
    1433 
    1434     if(apiname) {
    1435         dprintf((LOG, "address 0x%x %s @%d (0x%08x) forwarder %s.%s", virtaddr - oh.ImageBase, apiname, ordinal, virtaddr, forwarddll, forwardapi));
    1436         AddNameExport(exportaddr, apiname, ordinal, TRUE);
    1437     }
    1438     else {
    1439         dprintf((LOG, "address 0x%x @%d (0x%08x) forwarder %s.%s", virtaddr - oh.ImageBase, ordinal, virtaddr, forwarddll, forwardapi));
    1440          AddOrdExport(exportaddr, ordinal, TRUE);
    1441     }
    1442     free(forwarddll);
    1443     return TRUE;
    1444 
    1445 fail:
    1446   free(forwarddll);
     1519 
     1520  if(forwardord != 0 || (iForwardApiLength == 1 && *forwardapi == '0'))
     1521    exportaddr = WinDll->getApi(forwardord);
     1522  else 
     1523    exportaddr = WinDll->getApi(forwardapi);
     1524
     1525  if(apiname)
     1526  {
     1527    dprintf((LOG, "address 0x%x %s @%d (0x%08x) forwarder %s.%s", virtaddr - oh.ImageBase, apiname, ordinal, virtaddr, forwarddll, forwardapi));
     1528    AddNameExport(exportaddr, apiname, ordinal, TRUE);
     1529  }
     1530  else
     1531  {
     1532    dprintf((LOG, "address 0x%x @%d (0x%08x) forwarder %s.%s", virtaddr - oh.ImageBase, ordinal, virtaddr, forwarddll, forwardapi));
     1533    AddOrdExport(exportaddr, ordinal, TRUE);
     1534  }
     1535
     1536//  free(forwarddll);
     1537  *forwardapi0 = '.';
     1538  return TRUE;
     1539
     1540  fail:
     1541//  free(forwarddll);
     1542  *forwardapi0 = '.';
    14471543  return FALSE;
    14481544}
     
    14511547Win32DllBase *Win32PeLdrImage::loadDll(char *pszCurModule)
    14521548{
     1549    PERF_START(loaddll)
     1550
    14531551 Win32DllBase *WinDll = NULL;
    14541552 char modname[CCHMAXPATH];
     
    15381636    dprintf((LOG, "**********************************************************************" ));
    15391637
     1638
     1639    PERF_ELAPSED(loaddll, "Win32PeLdrImage::loadDll", pszCurModule)
     1640
    15401641    return WinDll;
    15411642}
     
    15501651BOOL Win32PeLdrImage::processImports(char *win32file)
    15511652{
     1653    PERF_START(processimports)
     1654
    15521655 PIMAGE_IMPORT_DESCRIPTOR pID;
    15531656 IMAGE_SECTION_HEADER     shID;
     
    17731876
    17741877  free(pszModules);
     1878
     1879  PERF_ELAPSED(processimports, "Win32PeLdrImage::processImports", getModuleName())
     1880
    17751881  return TRUE;
    17761882}
     
    18111917  NameExport *curexport;
    18121918  ULONG       ulAPIOrdinal;                      /* api requested by ordinal */
    1813 
     1919 
    18141920    apilen = strlen(name) + 1;
    18151921    if(apilen < 4)
     
    18281934           *(ULONG *)curexport->name == *(ULONG *)apiname)
    18291935        {
    1830             if(strcmp(curexport->name, apiname) == 0)
    1831                 return(curexport->virtaddr);
     1936          if(strcmp(curexport->name, apiname) == 0)
     1937          {
     1938            return(curexport->virtaddr);
     1939          }
    18321940        }
    18331941        curexport = (NameExport *)((ULONG)curexport->name + curexport->nlength);
     
    18391947ULONG Win32PeLdrImage::getApi(int ordinal)
    18401948{
    1841  ULONG       apiaddr, i;
    1842  OrdExport  *curexport;
    1843  NameExport *nexport;
    1844 
    1845     curexport = ordexports;
    1846     for(i=0;i<nrOrdExports;i++) {
    1847         if(curexport->ordinal == ordinal)
    1848             return(curexport->virtaddr);
    1849         curexport++;
    1850     }
    1851     //Name exports also contain an ordinal, so check this
    1852     nexport = nameexports;
    1853     for(i=0;i<nrNameExports;i++) {
    1854         if(nexport->ordinal == ordinal)
    1855             return(nexport->virtaddr);
    1856 
    1857         nexport = (NameExport *)((ULONG)nexport->name + nexport->nlength);
    1858     }
    1859     return(0);
     1949  ULONG       apiaddr, i;
     1950  OrdExport  *curexport;
     1951  NameExport *nexport;
     1952
     1953  curexport = ordexports;
     1954  for(i=0;i<nrOrdExports;i++)
     1955  {
     1956    if(curexport->ordinal == ordinal)
     1957      return(curexport->virtaddr);
     1958    curexport++;
     1959  }
     1960 
     1961  //Name exports also contain an ordinal, so check this
     1962  nexport = nameexports;
     1963  for(i=0;i<nrNameExports;i++)
     1964  {
     1965    if(nexport->ordinal == ordinal)
     1966      return(nexport->virtaddr);
     1967
     1968    nexport = (NameExport *)((ULONG)nexport->name + nexport->nlength);
     1969  }
     1970  return(0);
    18601971}
    18611972//******************************************************************************
Note: See TracChangeset for help on using the changeset viewer.