/*static char *SCCSID = "@(#)dosdel.c   6.8 92/04/30";*/
#define INCL_DOSDATETIME
#define INCL_DOSMISC

/* OS/2 header files */

#include <os2def16.h>
#include <bsedos16.h>
#include <error.h>
#include <sysbits.h>

/* C run-time header files */

#include <dos.h>
#include <limits.h>
#include <stdio.h>

/* Local header files */
#include "dosdel.h"
#include "cutilc.h"

unsigned short modify_fSharing (unsigned short SrvAttrib);           //d25363

unsigned int GetVars (
  char far *    pszEnvironment,
  char far *    pszFName,
  char far      szSourcePath[],
  char far      szTargetPath[],
  char far      szDeleteDir[],
  struct _FILESTATUS *pFileStatus,
  unsigned long far *pulMaxDelDirSize);

unsigned int GetEnvVars (
  char     far * pszEnvironment,
  char     far   szDelDirName [],
  unsigned long far *pulMaxDelDirSize);

unsigned int StoreFile (
  char far *pszFName,
  char far *szDeleteDir,
  char far *szTargetPath);

unsigned int CreateDeletePath (
  char far szDeleteDir[]);

char far * GetFileFromPath (
  char far * Path);

unsigned int CreateDeleteRecord (
  char      far szSourcePath[],
  char      far szTargetPath[],
  char      far szDeleteDir[],
  struct _FILESTATUS *pFileStatus,
  unsigned  long ulMaxDelDirSize);

void MakeRoom (
  unsigned int  hControlFile,
  unsigned long ulAllocatedSize,
  unsigned long ulMaxDelDirSize,
  char      far szDeleteDir []);

void EraseOverflow (
  unsigned int  hControlFile,
  char     far  szDeleteDir [],
  unsigned long ulDelDirSize,
  unsigned long ulMaxDelDirSize);

char far * strstrf (
 char far * pszString1,
 char far * pszString2);

unsigned long atol (char far * pszString);

void ltoa (char far * pszString, unsigned long ulVar, unsigned int usField);

unsigned long power10 (int pos);

#define FPF_SEG(fp) (*((unsigned far *)&(fp) + 1))

unsigned char szTempBuf [MAXPATHLEN] = {0};
struct _DELFILEINFO TempDelFileInfo = {0};


unsigned far pascal W_DOSDELETE (
  char far *    pszFName,
  char far *    pszEnvironment)
{
  struct _MEMVARS
  {
    char szSourcePath [MAXPATHLEN];
    char szTargetPath [MAXPATHLEN];
    char szDeleteDir  [MAXPATHLEN];
    unsigned long ulMaxDelDirSize;
    unsigned short FsaDataBuffer [256];
    struct _FILESTATUS FileStatus;
  };



/* Local variables */

  struct _MEMVARS *MemVars;
  unsigned rc;
  unsigned FsaDataBufferLen = 256 * 2;


/* Code begins */


  /* Allocate memory for variables */
  MemVars = NULL;
  if (DOSALLOCSEG (
        sizeof (struct _MEMVARS),
        & FPF_SEG (MemVars),
        0))
  {
     return (DOSFORCEDELETE (pszFName, 0L));
  }

  /* No UNC names allowed */
  if (strstrf (pszFName, (char far *) "\\\\"))
  {
    DOSFREESEG (FPF_SEG (MemVars));
    return (DOSFORCEDELETE (pszFName, 0L));
  }

  /* Get full path name of file to delete */
  if (DOSQPATHINFO (
        (char far *) pszFName,
        5,
        (char far *) MemVars->szSourcePath,
        MAXPATHLEN,
        0L))
  {
    DOSFREESEG (FPF_SEG (MemVars));
    return (DOSFORCEDELETE (pszFName, 0L));
  }

  /* Delete directory will be on same disk as file */
  MemVars->szDeleteDir [0] = MemVars->szSourcePath [0];
  MemVars->szDeleteDir [1] = ':';
  MemVars->szDeleteDir [2] = '\0';

  /* Make sure that this is a local drive */
  if (P_DOSQFSATTACH (
       (char far *) MemVars->szDeleteDir,
       0,
       1,
       (char far *) &(MemVars->FsaDataBuffer),
       &FsaDataBufferLen,
       0))
  {
    DOSFREESEG (FPF_SEG (MemVars));
    return (DOSFORCEDELETE (pszFName, 0L));
  }

  MemVars->szDeleteDir [2] = '\\';
  MemVars->szDeleteDir [3] = '\0';

  if (MemVars->FsaDataBuffer [0] != 3)      /* Only works for local drives */
  {
    DOSFREESEG (FPF_SEG (MemVars));
    return (DOSFORCEDELETE (pszFName, 0L));
  }

  SEM_REQUEST();

  /* Get the full path of the source file (the file which the app   */
  /* has asked to delete), the target file (the file where we're    */
  /* going to store the source file) and the storage dir (directory */
  /* that will hold the discardable file).                          */
  /* Also returns delete dir size.                                  */

  if (!GetVars (
         pszEnvironment,
         pszFName,
         (char far *) MemVars->szSourcePath,
         (char far *) MemVars->szTargetPath,
         (char far *) MemVars->szDeleteDir,
         (void far *) &(MemVars->FileStatus),
         (unsigned long far *) &(MemVars->ulMaxDelDirSize))
     )
  {
    /* Unable to get vars from environment, do a normal delete */
    DOSFREESEG (FPF_SEG (MemVars));
    SEM_RELEASE();
    return (DOSFORCEDELETE (pszFName, 0L));
  }
  else
  {

    /* Put file in storage directory */
    if (StoreFile (
          (char far *) pszFName,
          (char far *) MemVars->szDeleteDir,
          (char far *) MemVars->szTargetPath))
    {
      /* Can't put file in storage dir, do a normal delete */
      DOSFREESEG (FPF_SEG (MemVars));
      SEM_RELEASE();
      return (DOSFORCEDELETE (pszFName, 0L));
    }

    /* Write information to the delete directory control file */
    if (!CreateDeleteRecord (
           (char far *) MemVars->szSourcePath,
           (char far *) MemVars->szTargetPath,
           (char far *) MemVars->szDeleteDir,
           (void far *) &(MemVars->FileStatus),
           MemVars->ulMaxDelDirSize))
    {
      /* Unable to update control file, delete temp file from storage dir */
      rc = DOSFORCEDELETE (MemVars->szTargetPath, 0L);  /* 57983 */
      DOSFREESEG (FPF_SEG (MemVars));
      SEM_RELEASE();
      return (rc);                                      /* 57983 */
/*      return (DOSFORCEDELETE (MemVars->szTargetPath, 0L));  57983 */
    }
  }
  DOSFREESEG (FPF_SEG (MemVars));
  SEM_RELEASE();
  return (0);
}


/***************************************************************************/
/*                                                                         */
/* GetVars                                                                 */
/*                                                                         */
/* Given the path of the file to be made discardable, construct a source   */
/* path, a target path, and a delete directory path.  These are full paths */
/* that begin at the root directory, but do not include a drive letter.    */
/* The source path is the full current path name of the file specified for */
/* deletion.  The delete directory is the directory which contains all     */
/* deletable files on the disk;  it also contains a file with control      */
/* information for restoring or permanently deleting these files.  The     */
/* target path is the path within the delete directory that will contain   */
/* the discardable file.                                                   */
/*                                                                         */
/***************************************************************************/

unsigned int GetVars (
  char far *     pszEnvironment,
  char far *     pszFName,
  char far       szSourcePath[],
  char far       szTargetPath[],
  char far       szDeleteDir[],
  struct _FILESTATUS *pFileStatus,
  unsigned long far *pulMaxDelDirSize)

{
  unsigned int usDirPathLen;
  unsigned int usCurDisk;
  unsigned long  ulDriveMap;
  char far *pszCurrentPos;
  struct _DATETIME *dtTempMem;

  /* Get file attributes */
  if (DOSQPATHINFO (
        (char far *) pszFName,
        1,
        pFileStatus,
        sizeof (struct _FILESTATUS),
        0L))
  {
    return FALSE;
  }

  /* Return error if file is hidden */
  if ((pFileStatus->attrFile & (1+(1<<1)+(1<<2)+(1<<3)+(1<<4))))
  {
    return FALSE;
  }

  if (!GetEnvVars (
         (char far *) pszEnvironment,
         (char far *) szDeleteDir,
         (unsigned long far *) pulMaxDelDirSize))
  {
    return (FALSE);
  }

  /* Discardable file will be stored in delete directory under     */
  /* unique name.                                                  */
  strcpyfc ((char far *) szTargetPath, (char far *) szDeleteDir);
  strcatfc ((char far *) szTargetPath, "\\");


  /* Allocate memory for variables */
  dtTempMem = (struct _DATETIME *) szTempBuf;

  DOSGETDATETIME ((void far *) dtTempMem);

  ltoa (szTargetPath+strlenfc(szTargetPath), (unsigned long) dtTempMem->day, 2);
  ltoa (szTargetPath+strlenfc(szTargetPath), (unsigned long) dtTempMem->hours, 2);
  ltoa (szTargetPath+strlenfc(szTargetPath), (unsigned long) dtTempMem->minutes, 2);
  ltoa (szTargetPath+strlenfc(szTargetPath), (unsigned long) dtTempMem->seconds, 2);
  strcatfc (szTargetPath, ".");
  ltoa (szTargetPath+strlenfc(szTargetPath), (unsigned long) dtTempMem->hundredths, 2);

  return (TRUE);
}


/****************************************************************************/
/*                                                                          */
/*  StoreFile                                                               */
/*                                                                          */
/*  Place the file into the storage directory, creating the directory if    */
/*  necessary.                                                              */
/*                                                                          */
/****************************************************************************/
unsigned int StoreFile (
  char far *pszFName,
  char far *szDeleteDir,
  char far *szTargetPath)
{
  unsigned int i, rc;

  /* Create storage directory */
  if (CreateDeletePath ((char far *) szDeleteDir))
  {
    return TRUE;
  }

  rc = 0;
  i = 0;
  do
  {
    /* Move the discardable file to the delete directory */
    rc = DOSMOVE (pszFName, (char far *) szTargetPath, 0L);
    if (rc)
    {
      if ((rc == 5) && (i <= 26)) /* File already exists and letter <= Z */
      {
        /* Change last char in name, try to make unique */
        *(GetFileFromPath (szTargetPath) + 11) = (char) (i + 'A');
        *(GetFileFromPath (szTargetPath) + 12) = '\0';
        i++;
      }
      else
      {
        return TRUE;
      }
    }
  } while (rc);
  return FALSE;
}

/****************************************************************************/
/*                                                                          */
/*  CreateDeletePath                                                        */
/*                                                                          */
/*  Create the storage directory.                                           */
/*                                                                          */
/****************************************************************************/
unsigned int CreateDeletePath (char far szDeleteDir[])
{
  char far * pszPos;
  int  rc;
  char HoldChar;

  /* Create storage path */
  if (strlenfc ((char far *) szDeleteDir) > 3)   /* if not root dir */
  {
    rc = 0;
    pszPos = szDeleteDir;
    while (*(pszPos++) != '\\');         /* Point to first delimeter */
    while ((*pszPos != '\0') && (!rc))   /* While not end of line */
    {
      pszPos++;
      if ((*pszPos == '\\') || (*pszPos == '\0'))  /*If this char is delim */
      {
        HoldChar = *pszPos;               /* Save delimiter char */
        *pszPos = '\0';                   /* Make this end of line */
        rc = DOSMKDIR (szDeleteDir, 0L);  /* Create next dir */
        if (rc == 5)
          rc = 0;                         /* Ignore "already exists" error */
        *pszPos = HoldChar;               /* Restore delimiter char */
      }
    }

    if (rc)
      return (TRUE);
    else
      return (FALSE);
  }
}



/***************************************************************************/
/*                                                                         */
/* GetEnvVars                                                              */
/*                                                                         */
/* Get name and maximum size of delete directory from environment.         */
/*                                                                         */
/***************************************************************************/

unsigned int GetEnvVars (
  char          far *pszEnvironment,
  char          far szDelDirName [],
  unsigned long far *pulMaxDelDirSize)
{
  char far *pszDelDirParm;
  unsigned int i;
  unsigned char szLowDelDirName [4];

  /* Exit if not called by someone who's passing the environment */
  if (!pszEnvironment) return (FALSE);

  szLowDelDirName [0] = szDelDirName [0] + ('a' - 'A');
  szLowDelDirName [1] = ':';
  szLowDelDirName [2] = '\\';
  szLowDelDirName [3] = '\0';

  /* Find our environment variables (format DELDIR=drive:dirname,size;) */
  pszDelDirParm = NULL;
  while ((*pszEnvironment != '\0') && (!pszDelDirParm))
  {
    pszDelDirParm = strstrf (pszEnvironment, "DELDIR=");
    if (pszDelDirParm)
    {
      if (!(pszDelDirParm = strstrf (pszEnvironment, szDelDirName)))
        pszDelDirParm = strstrf (pszEnvironment, szLowDelDirName);
    }
    pszEnvironment += (strlenfc (pszEnvironment) + 1);
  }

  /* Get directory name and size */

  if (pszDelDirParm)
  {
    /* Get copy of dir path */
    for (i = 0;
        (*pszDelDirParm != ' ') && (*pszDelDirParm != ',') &&
          (*pszDelDirParm != ';') && (*pszDelDirParm != '\0');
        i++)
    {
      szDelDirName [i] = *pszDelDirParm++;
    }
    szDelDirName [i] = '\0';

    /* Scan to dir size parm */
    while (!(*pszDelDirParm >= '0' && *pszDelDirParm <= '9') &&
           (*pszDelDirParm != ';' && *pszDelDirParm != '\0'))
      pszDelDirParm++;

    /* If dir size parm not found then return unsuccessful */
    if (*pszDelDirParm == ';' || *pszDelDirParm == '\0')
      return (FALSE);

    /* Get max dir size */
    *pulMaxDelDirSize = atol ((char far *) pszDelDirParm) << 10;

    /* return environment variables found */
    return TRUE;
  }
  else
  {
    /* return variables not found */
    return FALSE;
  }
}


/**************************************************************************/
/*                                                                        */
/* GetFileFromPath                                                        */
/*                                                                        */
/* Return a pointer to the last element in a path.                        */
/*                                                                        */
/**************************************************************************/

char far * GetFileFromPath (
  char far * Path)
{
  char far * Pos;

  /* Back up through string until we find path delimiter or hit */
  /* the start of the string.                                   */
  Pos = Path + strlenfc (Path);
  while ((*Pos != '\\') && (Pos >= Path))
  {
    Pos--;
  }

  /* Point at first char in file name */
  Pos++;

  return (Pos);
}


/**************************************************************************/
/*                                                                        */
/* CreateDeleteRecord                                                     */
/*                                                                        */
/* Append a record to the control file.  This record contains information */
/* necessary to restore or permanently delete the current file.           */
/*                                                                        */
/**************************************************************************/

unsigned int CreateDeleteRecord (
  char      far szSourcePath[],
  char      far szTargetPath[],
  char      far szDeleteDir[],
  struct _FILESTATUS *pFileStatus,
  unsigned long ulMaxDelDirSize)

{
  unsigned int  hControlFile;
  unsigned int  usAction;
  unsigned long ulFilePointer;
  unsigned int  usBytesWritten;
  unsigned short SrvAttrib;                                          //d25363
  unsigned int  rc;

  /* Set control file path */
  strcpyfc ((char far *) szTempBuf, (char far *) szDeleteDir);
  strcatfc ((char far *) szTempBuf, "\\CONTROL.DEL");

  SrvAttrib = modify_fSharing (0);                                   //d25363

  /* Open the control file */
  rc = P_DOSOPEN (
         szTempBuf,
         &hControlFile,
         &usAction,
         0L,
         (1 << 1) | (1 << 2),      /* hidden, system */
         (1 << 4) | 1,
         (1 << 13) | (1<<12) | (1 << 4) | 2, /* no hard error, no cache, no sharing, read/write */
         0L);

  modify_fSharing (SrvAttrib);                                       //d25363

  if (rc)
  {
    return (FALSE);
  }


  /* Make room in control file for current record */
  MakeRoom (
    hControlFile,
    pFileStatus->cbFileAlloc,
    ulMaxDelDirSize,
    (char far *) szDeleteDir);

  /* Place file pointer at start of file */
  if (DOSCHGFILEPTR (
        hControlFile,
        0L,
        0,
        &ulFilePointer))
  {
    DOSCLOSE (hControlFile);
    return (FALSE);
  }

  /* Create delete file record, first the allocated size */
  TempDelFileInfo.ulFileSize = pFileStatus->cbFileAlloc;

  /* Place the original file path into the record (skip drive letter) */
  strcpyfc ((char far *) (TempDelFileInfo.szFileName),
           (char far *) &(szSourcePath [2]));

  /* Place the stored file path into the record */
  strcpyfc (
    (char far *) (TempDelFileInfo.szStoredName),
    (char far *) GetFileFromPath ((char far *) szTargetPath));

  /* Insert delete record into control file */
  if (DOSPWRITE (
        hControlFile,
        (PVOID) &(TempDelFileInfo),
        sizeof (struct _DELFILEINFO),
        &usBytesWritten))
  {
    DOSCLOSE (hControlFile);
    return (FALSE);
  }

  /* Hide file in storage directory */
  DOSSETFILEMODE (
    (char far *) szTargetPath,
    (1 << 1) | (1 << 2),  /* hidden, system */
    0L);

  DOSCLOSE (hControlFile);

  /* Hide control file */
  DOSSETFILEMODE (
    (char far *) szTempBuf,
    (1 << 1) | (1 << 2),  /* hidden, system */
    0L);

  return TRUE;
}



/**************************************************************************/
/*                                                                        */
/* MakeRoom                                                               */
/*                                                                        */
/* if control file contains other records, make room for the current      */
/* record, which will be placed at the beginning of the file.             */
/*                                                                        */
/**************************************************************************/

void MakeRoom (
  unsigned int  hControlFile,
  unsigned long ulAllocatedSize,
  unsigned long ulMaxDelDirSize,
  char     far  szDeleteDir [])

{
  unsigned long  ulFilePointer;
  unsigned long  ulDelDirSize;
  unsigned long  ulTotalRecords;
  unsigned long  ulIndex;
  unsigned int usBytesWritten;

  /* Get size of control file */
  DOSCHGFILEPTR (
    hControlFile,
    0L,
    2,
    &ulFilePointer);

  /* if control file contains other records, make room for the current */
  /* record, which will be placed at the beginning of the file.        */
  if (ulFilePointer > 0)
  {
    ulDelDirSize = (unsigned long) sizeof (struct _DELFILEINFO)
                   + ulFilePointer
                   + ulAllocatedSize;

    /* Loop through all records, moving the to the next higher position */
    /* This will make room for one record at the beginning of the file. */

    ulTotalRecords = udivc (ulFilePointer,
                           (unsigned) sizeof (struct _DELFILEINFO));

    for (ulIndex = ulTotalRecords; ulIndex > 0L; ulIndex--)
    {
      /* Point to record to be moved to next position */
      DOSCHGFILEPTR (
        hControlFile,
        ulongmulc ((unsigned long) (ulIndex - 1),
                  (unsigned long) sizeof (struct _DELFILEINFO)),
        0,
        &ulFilePointer);

      /* Read record */
      DOSREAD (
        hControlFile,
        (PVOID) &TempDelFileInfo,
        sizeof (struct _DELFILEINFO),
        &usBytesWritten);

      ulDelDirSize += TempDelFileInfo.ulFileSize;

      /* Point to next position */
      DOSCHGFILEPTR (
        hControlFile,
        ulongmulc (ulIndex, (unsigned long) sizeof (struct _DELFILEINFO)),
        0,
        &ulFilePointer);

      /* Copy record to next position */
      DOSPWRITE (
        hControlFile,
        (PVOID) &TempDelFileInfo,
        sizeof (struct _DELFILEINFO),
        &usBytesWritten);
    }
    EraseOverflow (hControlFile, szDeleteDir, ulDelDirSize, ulMaxDelDirSize);
  }
}


/***************************************************************************/
/*                                                                         */
/* EraseOverflow                                                           */
/*                                                                         */
/* If the total size of the files in the delete directory (including the   */
/* one that we're about to put in) is greater than the maximum specified   */
/* in the DELDIR environment variable, we delete files until the directory */
/* size is OK.  Files are deleted on a first in, first out basis.  This    */
/* is done by first deleting the file whose record appears at the end      */
/* of the control file, and then deleting the record.  This is continued   */
/* until there is enough room on the disk or there is only one record      */
/* left (which will be the record of the file we're putting in).           */
/*                                                                         */
/***************************************************************************/

void EraseOverflow (
  unsigned int  hControlFile,
  char     far  szDeleteDir [],
  unsigned long ulDelDirSize,
  unsigned long ulMaxDelDirSize)
{

  unsigned long  ulFilePointer;
  unsigned int   usBytesRead;
  unsigned long  ulBytesDeleted;
  unsigned int   rc;
  unsigned long  ulBytesNeeded;

  if (ulDelDirSize > ulMaxDelDirSize)
  {
    ulBytesNeeded = ulDelDirSize - ulMaxDelDirSize;

    /* Delete files until enough space */
    ulBytesDeleted = 0L;
    do
    {
      /* Place file pointer at last record */
      rc = DOSCHGFILEPTR (
             hControlFile,
             0,
             2,                      /* move from end of file */
             &ulFilePointer);

      if (!rc)
        rc = DOSCHGFILEPTR (
               hControlFile,
               ulFilePointer - (unsigned long) (sizeof (struct _DELFILEINFO)),
               0,                     /* move from beginning of file */
               &ulFilePointer);

      /* Read in last record */
      if (!rc)
        rc = DOSREAD (
               hControlFile,
               (PVOID) &(TempDelFileInfo),
               sizeof (struct _DELFILEINFO),
               &usBytesRead);

      /* Truncate off last record */
      if (!rc)
        rc = DOSNEWSIZE (hControlFile, ulFilePointer);

      /* Delete this file */
      if (!rc)
      {
        strcpyfc ((char far *) szTempBuf, (char far *) szDeleteDir);
        strcatfc ((char far *) szTempBuf, "\\");
        strcatfc ((char far *) szTempBuf,
                 (char far *) (TempDelFileInfo.szStoredName));
        _asm push ds;
        rc = DOSFORCEDELETE ((char far *) szTempBuf, 0L);
        _asm pop ds;
      }

      /* Update count of bytes successfully deleted */
      if (!rc)
        ulBytesDeleted += TempDelFileInfo.ulFileSize;

    /* Continue until enough files deleted */
    } while (
        (ulFilePointer >= sizeof (struct _DELFILEINFO)) &&
        (ulBytesDeleted < ulBytesNeeded) &&
        !rc) ;
  }
}


char far * strstrf (
 char far * pszString1,
 char far * pszString2)
{
  int        Str1Len, Str2Len;
  char far * pszCurPos;

  Str1Len = strlenfc (pszString1);
  Str2Len = strlenfc (pszString2);

  if (Str1Len >= Str2Len)
  {
    for (pszCurPos = pszString1;
         pszCurPos <= (pszString1 + Str1Len);
         pszCurPos++)
    {
      if (strncmpfc (pszCurPos, pszString2, Str2Len) == 0)
        return (pszCurPos);
    }
  }
  return (NULL);
}

unsigned long atol (char far * pszString)
{
  char far *    pszCurrentPos;
  unsigned long Total;
  int           pos, Found;

  Found = FALSE;
  while ((*pszString != '\0') && (!Found))
  {
    if ((*pszString >= '0') && (*pszString <= '9'))
      Found = TRUE;
    else
      pszString++;
  }

  if (Found)
  {
    pszCurrentPos = pszString;

    while ((*pszCurrentPos >= '0') && (*pszCurrentPos <= '9'))
      pszCurrentPos++;

    pos = 0;
    Total = 0L;

    do
    {
      pszCurrentPos--;
      Total = Total + ulongmulc ((unsigned long) (*pszCurrentPos - '0'),
                                power10 (pos));
      pos++;
    } while (pszCurrentPos > pszString);

    return (Total);
  }
  else
  {
    return (0L);
  }
}


void ltoa (char far * pszString, unsigned long ulVar, unsigned int usWidth)
{
  int i;
  unsigned long ulPower;
  unsigned      usDiv;

  for (i = usWidth - 1; i >= 0; i --)
  {
    ulPower = power10 (i);
    usDiv = udivc (ulVar, (unsigned int) ulPower);
    *pszString++ = (unsigned char) usDiv + '0';
    if (usDiv)
    {
      ulVar-= ulongmulc ((unsigned long) usDiv, ulPower);
    }
  }
  *pszString = '\0';
}

unsigned long power10 (int pos)
{
  unsigned long total;
  int i;

  if (pos == 0)
    return (1L);

  total = 10L;
  for (i = 1; i < pos; i++)
    total = ulongmulc (total, 10L);
  return total;
}


