//   CLOCK.C - set read and write locks in the cookie file

#include "precomp.h"
#pragma hdrstop
#include "messages.h"
EnableAssert

/*----------------------------------------------------------------------------
 * Name: cookie_free
 * Purpose: free any and ALL locks for current locker
 * Assumes:
 * Returns: OP_OK for success, or OP_SYSERR
 * NOTE: This may be called during aborting, so shouldn't call FatalError
 */
int cookie_free(F fAutotype)
{
    char *Cookiebuf;
    char *NewCookiebuf;
    int hfCookieFile, bufbytes;
    int Totread = 0;
    int TotLocks = 0;
    USHORT Rcount, Wcount, RBcount;
    char *tp;
    char *cp;
    char Lname[CMAXNAME];
    char Llock[CMAXLOCK];
    char Lcomm[CMAXDATE*2];
    int wMon, wDay, wHour, wMin;
    int cbLockName = strlen(szLockName);
    int cbWrite, cbWritten;
    char    *pbWrite;

    // set all lock counts to zero, free at most one of each lock type

    Rcount = Wcount = RBcount = 0;

    if ((hfCookieFile = open_cookie()) == -1)
        {
        Error(szCookieOpen, pszCookieFile, SzForEn(_doserrno));
        return (OP_SYSERR);
        }

    Cookiebuf = malloc(cbCookieMax);

    if ((char *)NULL == Cookiebuf)
        {
        _close(hfCookieFile);
        Error(szOutOfMem);
        return (OP_SYSERR);
        }

    NewCookiebuf = malloc(cbCookieMax);

    if ((char *)NULL == NewCookiebuf)
        {
        free(Cookiebuf);
        _close(hfCookieFile);
        Error(szOutOfMem);
        return (OP_SYSERR);
        }

    Cookiebuf[0] = '\0';
    NewCookiebuf[0] = '\0';

    while ((bufbytes=_read(hfCookieFile, NewCookiebuf, cbCookieMax)) > 0)
        {
        Totread += bufbytes;
        if (Totread >= cbCookieMax)
            {
            FatalError(szCookieTooBig, pszCookieFile);
            _close(hfCookieFile);
            free(Cookiebuf);
            free(NewCookiebuf);
            return (OP_SYSERR);
            }
        NewCookiebuf[bufbytes] = '\0';
        strcat(Cookiebuf, NewCookiebuf);
        }
    cp = Cookiebuf;
    tp = NewCookiebuf;

    while (*cp != '\0')
        {
        if ((strncmp(cp, szLockName, cbLockName) == 0) &&
                *(cp + cbLockName) == ' ')
            {
            if (sscanf(cp,"%s %s %d/%d @ %d:%d %s", Lname, Llock,
                       &wMon, &wDay, &wHour, &wMin, Lcomm) != 7)
                {
                Error(szCookieCorrupt, pszCookieFile);
                _close(hfCookieFile);
                free(Cookiebuf);
                free(NewCookiebuf);
                return (OP_SYSERR);
                }
            if (strcmp(Llock, "READ")==0)
                {
                if (fAutotype)
                    {
                    if (strncmp(Lcomm, "autolock", 8) != 0 ||
                            wLockMin != wMin || wLockHour != wHour ||
                            wLockDay != wDay || wLockMon != wMon)
                        goto passlock;
                    }
                if (++Rcount > 1)
                    goto passlock;
                }
            else if (strcmp(Llock, "WRITE")==0)
                {
                if (++Wcount > 1)
                    goto passlock;
                }
            else if (strcmp(Llock, "READ-BLOCK")==0)
                {
                if (fAutotype)
                    goto passlock;
                if (++RBcount > 1)
                    goto passlock;
                }

            while (*cp++ != '\n')
                ;  // increment past current line
            *(cp-1) = '\0';
            if (fVerbose)
                PrErr("lock released.\n");
            TotLocks++;
            }
        else
            {
passlock:   /* do not free this lock, just move into next buffer and loop */
            while ((*tp++ = *cp++) != '\n')
                ;
            }
        }

    *tp = '\0';

    free(Cookiebuf);    /* only the new buffer needed now */

    if (_chsize(hfCookieFile, 0) != 0)
        {
        Error(szCookieTrunc, pszCookieFile, SzForEn(errno));
        _close(hfCookieFile);
        free(NewCookiebuf);
        return (OP_SYSERR);
        }

    if (_lseek(hfCookieFile, 0, SEEK_SET) == -1)
        {
        Error(szCookieSeek, pszCookieFile, SzForEn(errno));
        _close(hfCookieFile);
        free(NewCookiebuf);
        return (OP_SYSERR);
        }

    cbWrite = strlen(NewCookiebuf);
    pbWrite = NewCookiebuf;
    while (cbWrite)
        {
        cbWritten = _write(hfCookieFile, pbWrite, cbWrite);
        if (-1 == cbWritten || 0 == cbWritten)
            {
            if (WRetryError(eoWrite, "writing", 0, pszCookieFile) != 0)
                continue;
            else
                {
                _close(hfCookieFile);
                free(NewCookiebuf);
                return (OP_SYSERR);
                }
            }
        ClearPreviousError();
        cbWrite -= cbWritten;
        pbWrite += cbWritten;
        }

    free(NewCookiebuf);
    _close(hfCookieFile);
    if (TotLocks == 0)
        {
        if (fAutotype)
            Error("not found; lock file may have been corrupted\n");
        else
            PrErr("no locks.\n");
        }
    return (OP_OK);
}


//============================================================================
//
//                       cookie_lock_RB
//
//  obtain a read-block lock for given workstation name
//
//============================================================================
int cookie_lock_RB(AD *pad, char *szComment)
{
    char    *TargBuf;
    int     lock_res;

    TargBuf = malloc(LINE_LEN);
    if ((char *)NULL == TargBuf)
        FatalError(szOutOfMem);

    LockFill(pad, TargBuf, RB_LOCK);

    strcat(TargBuf, szComment);
    strcat(TargBuf, "\r\n");
    lock_res = add_cookie_lock(pad, TargBuf, RB_LOCK, fFalse);
    free(TargBuf);
    if (lock_res != OP_OK)
        {
        Error("Read lock DENIED\n");
        return (lock_res);
        }

    if (fVerbose)
        PrErr(szCookieGrant, "Read", szLockName);
    return (OP_OK);
}


//============================================================================
//
//                       cookie_lock_read
//
//  obtain a read lock for given workstation name
//
//============================================================================
int cookie_lock_read(AD *pad, char *szComment, int fAutotype)
{
    char    *TargBuf;
    int     lock_res;

    TargBuf = malloc(LINE_LEN);
    if ((char *)NULL == TargBuf)
        FatalError(szOutOfMem);
    LockFill(pad, TargBuf, READ_LOCK);

    strcat(TargBuf, szComment);
    strcat(TargBuf, "\r\n");
    lock_res = add_cookie_lock(pad, TargBuf, READ_LOCK, fAutotype);
    free(TargBuf);
    if (lock_res != OP_OK)
        {
        Error("Read lock DENIED\n");
        return (lock_res);
        }

    if (fVerbose)
        PrErr(szCookieGrant, "Read", szLockName);
    return (OP_OK);
}


//============================================================================
//
//                       cookie_lock_write
//
//  obtain a write lock for current project
//
//============================================================================
int cookie_lock_write(AD *pad, char *szComment, F fAutotype)
{
    char    *TargBuf;
    int     lock_res;

    TargBuf = malloc(LINE_LEN);
    if ((char *)NULL == TargBuf)
        FatalError(szOutOfMem);

    LockFill(pad, TargBuf, WRITE_LOCK);

    strcat(TargBuf, szComment);
    strcat(TargBuf, "\r\n");
    lock_res = add_cookie_lock(pad, TargBuf, WRITE_LOCK, fAutotype);
    free(TargBuf);
    if (lock_res != OP_OK)
        {
        Error("Write lock DENIED\n");
        return (lock_res);
        }

    if (fVerbose)
        PrErr(szCookieGrant, "Write", szLockName);
    return (OP_OK);
}