/*
** Declarations needed for handling CAF (Crunched Article Files)
** Written by Richard Todd (rmtodd@mailhost.ecn.uoknor.edu) 3/24/96
**
** Various bug fixes, code and documentation improvements since then
** in 1998-2000, 2002, 2004, 2009, 2021, 2022, 2024, 2026.
*/


/*
** Format of a crunched article file:
** Header:
*/

typedef struct _CAFHEADER {
    char Magic[4];   /* Magic Number "CRMT" */
    ARTNUM Low;      /* lowest article in the file */
    ARTNUM NumSlots; /* number of articles there are room for in the TOC */
    ARTNUM High;     /* last article actually present in the file */
    size_t
        Free; /* amount of space currently unused (freed by cancels/expires) */
    off_t StartDataBlock;   /* offset of first article data block. */
    unsigned int BlockSize; /* unit of allocation for CAF files. */
    size_t
        FreeZoneTabSize; /* amount of space taken up by the free zone table. */
    size_t FreeZoneIndexSize; /* size taken up by the "index" part of the free
                                 zone table. */
    time_t LastCleaned;       /* note time of last cleaning. */
    int spare[3];
} CAFHEADER;

#define CAF_MAGIC             "CRMT"
#define CAF_MAGIC_LEN         4
#define CAF_DEFAULT_BLOCKSIZE 512

/*
** Then the table of free blocks.  The table is FreeZoneTabSize bytes
** long.  First comes a "first-level" or "index" bitmap, taking up the
** space from the end of the CAFHEADER to the end of the first
** block, i.e. FreeZoneIndexSize.  The rest of the table is a big bitmap
** listing free blocks in the 'data' portion of the CAF file.
**
** In the "index" bitmap: LSB of bitmap byte 0 is 1 if there are any 1s
** (free blocks) listed in the first block of the big bitmap, and 0 if there
** are no 1s in that block.  The remaining bits of the index bitmap
** correspond to the remaining blocks of the big bitmap accordingly.
** The idea is that from the index bitmap one can tell which part of the
** main bitmap is likely to have free blocks w/o having to read the entire
** main bitmap.
**
** As for the main bitmap, each bit is 1 if the corresponding data
** block (BlockSize bytes) is free.  LSB of bitmap byte 0 corresponds
** to the block @ offset StartDataBlock, and all the rest follow on
** accordingly.
**
** Note that the main part of the bitmap is *always* FreeZoneIndexSize*8
** blocks long, no matter how big the CAF file is.  The table of free blocks
** is almost always sparse.  Also note that blocks past EOF in the CAF file
** are *not* considered free.  If the CAF article write routines fail to
** find free space in the free block bitmaps, they will always attempt to
** extend the CAF file instead.
*/

#define CAF_DEFAULT_FZSIZE    (CAF_DEFAULT_BLOCKSIZE - sizeof(CAFHEADER))
#define CAF_MIN_FZSIZE        128

/*
** (Note: the CAFBITMAP structure isn't what's actually stored on disk
** in the free bitmap region, this is just a convenient structure to
** keep the bitmap size and StartBlockOffset together with the bitmap
** w/o having keep passing the original CAFHEADER to every routine
** that wants it.  The bitmap structure contains the first (index) bitmap,
** as well as pointers to structures for each block of the main bitmap that
** has been read into memory.
*/

typedef struct _CAFBITMAP {
    off_t StartDataBlock;
    off_t MaxDataBlock; /* can only handle offsets < this with this bitmap. */
    size_t FreeZoneTabSize;
    size_t FreeZoneIndexSize;
    size_t BytesPerBMB; /* size of chunk, in bytes, that any given BMBLK can
                           map. */
    unsigned int BlockSize;
    unsigned int NumBMB; /* size of Blocks array. */
    struct _CAFBMB **Blocks;
    char *Bits;
} CAFBITMAP;

typedef struct _CAFBMB {
    off_t StartDataBlock;
    off_t MaxDataBlock;
    int Dirty; /* 1 if this BMB has had any bits changed. */
    char *BMBBits;
} CAFBMB;

/*
** Next in the file are the TOC (Table of Contents) entries.  Each TOC
** entry describes an article.
*/

typedef struct _CAFTOCENT {
    off_t Offset;
    size_t Size;
    time_t ModTime;
} CAFTOCENT;

/*
** And then after the NumSlots TOC Entries, the actual articles, one after
** another, always starting at offsets == 0 mod BlockSize.
*/

/*
** Number of slots to put in TOC by default.  Can be raised if we ever get
** more than 256*1024=262144 articles in a file (frightening thought).
*/

#define CAF_DEFAULT_TOC_SIZE (256 * 1024)

/*
** Default extension name for CAF file in the news spool dir.
*/
#define CAF_NAME             "CF"

extern int CAFOpenArtRead(const char *cfpath, ARTNUM art, size_t *len);
extern int CAFOpenArtWrite(char *cfpath, ARTNUM *art, int WaitLock,
                           size_t size);
extern int CAFStartWriteFd(int fd, ARTNUM *art, size_t size);
extern int CAFFinishWriteFd(int fd);
extern int CAFFinishArtWrite(int fd);
extern int CAFCreateCAFFile(char *cfpath, ARTNUM lowart, ARTNUM tocsize,
                            size_t cfsize, int nolink, char *temppath,
                            size_t pathlen);
extern const char *CAFErrorStr(void);
extern CAFTOCENT *CAFReadTOC(char *cfpath, CAFHEADER *ch);
extern int CAFRemoveMultArts(char *cfpath, unsigned int narts, ARTNUM *arts);
extern int CAFStatArticle(char *path, ARTNUM art, struct stat *st);

#ifdef CAF_INNARDS
/*
** Functions used internally by caf.c, and by the cleaner program, and cafls
** but probably aren't useful/desirable to be used by others.
*/
extern int CAFOpenReadTOC(char *cfpath, CAFHEADER *ch, CAFTOCENT **tocpp);
extern int CAFReadHeader(int fd, CAFHEADER *h);
extern off_t CAFRoundOffsetUp(off_t offt, unsigned long int bsize);
extern CAFBITMAP *CAFReadFreeBM(int fd, CAFHEADER *h);
extern void CAFDisposeBitmap(CAFBITMAP *cbm);

/*
** Note: CAFIsBlockFree needs the fd, since blocks of the free bitmap may
** need to be fetched from disk.
*/
extern int CAFIsBlockFree(CAFBITMAP *bm, int fd, off_t block);
#endif

extern int caf_error; /* last error encountered by library. */
extern int caf_errno; /* saved value of errno here if I/O error hit by lib. */

#define CAF_ERR_IO             1 /* generic I/O error, see caf_errno */
#define CAF_ERR_BADFILE        2 /* corrupt file */
#define CAF_ERR_ARTNOTHERE     3 /* article not in the database */
#define CAF_ERR_CANTCREATECAF  4 /* can't create the CAF file, see errno */
#define CAF_ERR_FILEBUSY       5 /* file locked by someone else */
#define CAF_ERR_ARTWONTFIT     6 /* outside the range in the TOC */
#define CAF_ERR_ARTALREADYHERE 7 /* try to create an article already here */
#define CAF_ERR_BOGUSPATH      8 /* pathname not parseable */
