diff --git a/Utils/Libs/GLib/BITS.CPP b/Utils/Libs/GLib/BITS.CPP new file mode 100644 index 000000000..e0a689381 --- /dev/null +++ b/Utils/Libs/GLib/BITS.CPP @@ -0,0 +1,138 @@ +//=========================================================================== +// +// BITS.CPP +// +// Author: Graeme Webb +// Created: 3rd August 1996 +// Project: General C++ Library +// Purpose: Bit stream object +// +// Copyright (c) 1996 Graeme Webb +// +//=========================================================================== +// Revision history: +//=========================================================================== + +#include "gtypes.h" +#include "bits.hpp" + +//=========================================================================== + +BitStream::BitStream(byte *ptr) +{ + baseptr = (byte *) ptr; + currptr = (byte *) ptr; + bitpos = 0; + mask = 0x01; +} + +BitStream::~BitStream() +{ +} + +void BitStream::IncCurPtr(void) +{ + currptr++; +} + + +void BitStream::PutBit(uint bit) +{ + if (mask == 0x01) + { + *currptr = (bit ? mask : 0); + bitpos++; + mask <<= 1; + } + else + { + *currptr |= (bit ? mask : 0); + bitpos++; + if (mask == 0x80) + { + IncCurPtr(); + mask = 0x01; + } + else + { + mask <<= 1; + } + } +} + +void BitStream::PutBits(ulong bits, uint len) +{ + uint i; + + for (i = 0; i < len; i++) + { + PutBit(bits & 1); + bits >>= 1; + } +} + +uint BitStream::GetBit() +{ + uint bit; + + bit = (*currptr & mask) ? 1 : 0; + bitpos++; + + if (mask == 0x80) + { + IncCurPtr(); + mask = 0x01; + } + else + { + mask <<= 1; + } + + return bit; +} + +ulong BitStream::GetBits(uint len) +{ + uint i; + ulong bits, mbit; + + bits = 0; + mbit = 1; + + for (i = 0; i < len; i++) + { + if (GetBit()) + bits |= mbit; + mbit <<= 1; + } + + return bits; +} + +static inline ulong RoundUp(ulong value, uint align) +{ + value += align - 1; + return (value - value % align); +} + +void BitStream::Align(uint bytes) +{ + ulong bytepos, nextpos; + + bytepos = ByteLength(); + nextpos = RoundUp(bytepos,bytes); + + currptr = baseptr + bytepos; + + while (bytepos < nextpos) + { + *currptr=0; + IncCurPtr(); + bytepos++; + } + + bitpos = bytepos * 8; + mask = 0x01; +} + +//=========================================================================== diff --git a/Utils/Libs/GLib/BITS.HPP b/Utils/Libs/GLib/BITS.HPP new file mode 100644 index 000000000..6b976b1e0 --- /dev/null +++ b/Utils/Libs/GLib/BITS.HPP @@ -0,0 +1,49 @@ +//=========================================================================== +// +// BITS.HPP +// +// Author: Graeme Webb +// Created: 3rd August 1996 +// Project: General C++ Library +// Purpose: Bit stream declarations +// +// Copyright (c) 1996 Graeme Webb +// +//=========================================================================== +// Revision history: +//=========================================================================== + +#ifndef __BITS_HPP__ +#define __BITS_HPP__ + +//=========================================================================== +#include + +//=========================================================================== + +class BitStream +{ + byte * baseptr; + byte * currptr; + ulong bitpos; + byte mask; + void IncCurPtr(void); + +public: + + BitStream(byte *ptr); + ~BitStream(); + + void PutBit(uint bit); + void PutBits(ulong bits, uint len); + uint GetBit(); + ulong GetBits(uint len); + void Align(uint bytes=1); + + byte *CurrPtr() { return currptr; } + ulong BitLength() { return bitpos; } + ulong ByteLength() { return (bitpos+7)/8; } +}; + +//=========================================================================== +#endif diff --git a/Utils/Libs/GLib/Dpanim.cpp b/Utils/Libs/GLib/Dpanim.cpp new file mode 100644 index 000000000..0db9030d8 --- /dev/null +++ b/Utils/Libs/GLib/Dpanim.cpp @@ -0,0 +1,1049 @@ +/*========================================================================= + + DPANM.HPP + + Author: Gary Liddon @ Fareham + Created: + Project: + Purpose: + + Copyright (c) 1997 Gary Liddon + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +//#include +#include +#include +#include + +/* Glib + ---- */ + +/* Local + ----- */ +#include "dpanim.hpp" +//#include "ilbm.hpp" + +/* Name space + ---------- */ +using namespace std; + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ +typedef UWORD LP_TABLE_ELEMENT; + +#define MAX_NUMBER_CYCLES 16 + +#define BMBODY_RUNSKIPDUMP 1 +#define MAX_COLORS 256 +#define PALETTE_SIZE (MAX_COLORS * sizeof(long)) +#define MAX_RECORDS 0xffffu +#define MAX_LARGE_PAGE 256 + +#define LARGE_PAGE_SIZE 0x10000L + +#define LPF_HEADER_HEAD_SIZE_IN_FILE 256 +#define LPF_HEADER_SIZE_IN_FILE ((sizeof(AnimHdr)) + (8 * 16) + (4 * 256) + (sizeof(LpTable[MAX_LARGE_PAGE]))) +#define LP_FILE_OFFSET(nLp) ((nLp) * LARGE_PAGE_SIZE + LPF_HEADER_SIZE_IN_FILE) + +#define MakeID(d,c,b,a) (((ULONG)(a)<<24l) | ((ULONG)(b)<<16l) | ((ULONG)(c)<<8l) | ((ULONG)(d))) + +#define LARGE_PAGE_FILE_ID MakeID('L','P','F',' ') +#define ANIM_CONTENTS_ID MakeID('A','N','I','M') + +#define FIRST_FRAME_N 1 +#define FIRST_RECORD_N 0 // Records #d from 0. + + + +/*---------------------------------------------------------------------- + Typedefs + -------- */ +class LpObj; + + +/*---------------------------------------------------------------------- + Class Defintions + ---------------- */ + +/* Compressed Frame + ---------------- */ +class CompFrame : public GObject +{ +public: + CompFrame(void); + CompFrame(CompFrame const & C); + + ~CompFrame(void); + + void operator=(CompFrame const &); + + /* Make Vectorable */ + + bool operator==(CompFrame const &) const; + bool operator<(CompFrame const &) const; + + void WriteByte(u8 Byte); + void WriteWord(u16 Word); + void WriteData(u8 const * Src,int Amount); + void Write(ofstream & Out) const; + + friend ostream & operator<<(ostream & str,CompFrame & Fr); + + u8 const * SeeData(void) const {return(BinData);} + int GetSize() const {return(Len);} + +protected: + void Dump(); + void Init(); + void Resize(int NewLen); + void Increase(int NewLen) {Resize(ActualLen+NewLen);} + + u8 * BinData; + int Len; + + int ActualLen; + + enum + { + RESIZE_CHUNKS=1000, + }; +}; + + +typedef std::vector CompFrameVec; + +/* Large Frame Object + ------------------ */ +class LpObj : public GObject +{ +public: + LpObj(void); + + bool AddFrame(CompFrame const & C ); + void SetBaseRecord(int f){BaseRecord=f;} + int NumOfFrames(void) { return(MyFrames.size());} + + void Write(std::ofstream & Out) const; + void WriteHdr(std::ofstream & Out) const; + + /* Make Vectorable */ + + void operator=(LpObj const &); + bool operator==(LpObj const &) const; + bool operator<(LpObj const &) const; + + +protected: + int GetSizeInAll(void) const; + int BaseRecord; + CompFrameVec MyFrames; +}; + + + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ +static void GenerateLpTable(LpObjVec & LpTable,CompFrameVec & CompFrames); +static int GetMaxRecsPerLp(LpObjVec & LpTable); +static void WriteWord(std::ofstream & Out,u16 Word); + + + +/*---------------------------------------------------------------------- + Vars + ---- */ +//static FilterDetails MyDetails(DpAnimFilter(),".anm"); + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Function: Main + ---------------------------------------------------------------------- */ +DpAnimFilter::DpAnimFilter() +{ + InitVars(); +} + +DpAnimFilter::DpAnimFilter(char const * FName) : GAnimFilter(FName) +{ + InitVars(); + + loaded_LP=0xffff; +} + +DpAnimFilter::~DpAnimFilter(void) +{ + DeleteVars(); +} + +bool DpAnimFilter::Load(GAnim & Anm) +{ + int Frames; + Palette MyPal; + + MakeVars(); + + anm_init2(FileName); + + GetPal(MyPal); + + anm_read_first_frame(); + Frames=0; + + do + { + Frame * NewFr; + + NewFr=&Anm.GetNewFrame(); + + NewFr->SetFrame(dst_buffer,320,200,MyPal); + + Frames++; + } + while(anm_read_next_frame()); + + fclose(inANM); + + DeleteVars(); + + return(true); +} + +void DpAnimFilter::GetPal(Palette & MyPal) +{ + fseek(inANM,sizeof(AnimHdr)+sizeof(ColCycles[MAX_NUMBER_CYCLES]),SEEK_SET); + + for (int f=0;f<256;f++) + { + MyPal[f].SetB(fgetc(inANM)); + MyPal[f].SetG(fgetc(inANM)); + MyPal[f].SetR(fgetc(inANM)); + fgetc(inANM); + } +} + +void DpAnimFilter::InitVars(void) +{ + lpfTable=NULL; + current_LP=NULL; + dst_buffer=NULL; + +} + +void DpAnimFilter::MakeVars(void) +{ + DeleteVars(); + + if (!(lpfTable = new LpTable[MAX_LARGE_PAGE])) + Error(ERM_OUTOFMEM); + + if (!(current_LP = new LpTableMem)) + Error(ERM_OUTOFMEM); + + if (!(dst_buffer=new u8[128960])) + Error(ERM_OUTOFMEM); + +} + +void DpAnimFilter::DeleteVars(void) +{ + if (lpfTable) + delete[] lpfTable; + + if (current_LP) + delete current_LP; + + if (dst_buffer) + delete dst_buffer; + + InitVars(); +} + + + +bool DpAnimFilter::Save(GAnim & Anm) +{ + + MakeVars(); + + lpfHdr.InitHdr(); + + ofstream Out; + + Out.open(FileName,ios::trunc|ios::out|ios::binary); + + if (!Out) + Error(ERR_FATAL,"Can't open %s for output",(char const *)FileName); + else + { + int NumOfFrames; + CompFrameVec CompFrames; + LpObjVec MyLpTable; + + NumOfFrames=Anm.NumOfFrames(); + + if (NumOfFrames) + { + CompFrames.reserve(NumOfFrames); + CompFrames.resize(NumOfFrames); + + for (int f=0;f= (lpfTable[load_lp].baseRecord + lpfTable[load_lp].nRecords)) || (current_Frame < lpfTable[load_lp].baseRecord)) + { + load_lp++; + } + + + read_LP(load_lp); + base = 0; /* Set start to beginning of buffer */ + + /* Find start of Frame by adding number of bytes of previous frames. */ + + for (loop = 0;loop <= (current_Frame - current_LP->baseRecord);loop++) + base += current_LP->size[loop]; + + + if (lp_buffer[base] != 'B') + Error(ERR_FATAL,"Corrupt Anim File or Non Bitmapped image. base=%d,lp=%d in %s\n",base,load_lp,GetName()); + + if ((UWORD) lp_buffer[base + 2] != BMBODY_RUNSKIPDUMP) + Error(ERR_FATAL,"Compression scheme for frames is unkown."); + + PlayRunSkipDump(&lp_buffer[base+4],dst_buffer); + + return(1); +} + + +/*---------------------------------------------------------------------- + Read first frame of anm file. + ---------------------------------------------------------------------- */ +void DpAnimFilter::anm_read_first_frame(void) +{ + lp_buffer = &dst_buffer[320u * 200u]; + + current_Frame = FIRST_FRAME_N; + + read_LP(loaded_LP); + + current_Frame = 0xffff; + + anm_read_next_frame(); +} + + +/*---------------------------------------------------------------------- + Encode a frame with the DP anim codec and bung it into + a comp frame + ---------------------------------------------------------------------- */ +void DpAnimFilter::Encode(Frame const & Fr,CompFrame & Cfr) +{ + Frame NewFrame; + u8 const * Src; + int BytesLeft,Index; + int const px=320; + int const py=200; + int const Size=px*py; + + NewFrame=Fr; + NewFrame.Crop(Rect(0,0,px,py)); + + Src=NewFrame.SeeData(); + + BytesLeft=px*py; + Cfr.WriteByte('B'); + Cfr.WriteByte(0); + Cfr.WriteWord(BMBODY_RUNSKIPDUMP); + + while (BytesLeft) + { + int Length; + + Index=(px*py)-BytesLeft; + + Length=GetRunLength(&Src[Index],BytesLeft); + + if (Length > 3) + WriteRun(Src[Index],Length,Cfr); + else + { + Length=GetDataLength(&Src[Index],BytesLeft); + WriteDataRun(&Src[Index],Length,Cfr); + } + + BytesLeft-=Length; + } + + WriteEnd(Cfr); +} + +int DpAnimFilter::GetRunLength(u8 const * Src,int BytesLeft) +{ + int Index=0; + int LastIndex; + bool Done=false; + int Previous=Src[0]; + + for (Index=0;Index= 2) + Done=true; + else + Previous=Src[Index]; + + } + +// cout<<"Data Run of "< MaxLps) + MaxLps=LpTable[f].NumOfFrames(); + } + + return(MaxLps); +} + +void GenerateLpTable(LpObjVec & LpTable,CompFrameVec & CompFrames) +{ + LpObj * ThisObj; + int LpIndex; + int CompIndex; + bool Done; + + ThisObj=NULL; + LpIndex=0; + CompIndex=0; + Done=false; + + while (!Done) + { + if (CompIndex==CompFrames.size()) + Done=TRUE; + else + { + if (!ThisObj) + { + LpTable.resize(LpTable.size()+1); + ThisObj=&LpTable[LpTable.size()-1]; + ThisObj->SetBaseRecord(CompIndex); + } + + if (ThisObj->AddFrame(CompFrames[CompIndex])) + CompIndex++; + else + ThisObj=NULL; + } + } +} + +/*---------------------------------------------------------------------- + Anim writing stuff + ---------------------------------------------------------------------- */ +void DpAnimFilter::MakeAnim(void) +{ +} + +void DpAnimFilter::WriteHdr(ofstream & Out,AnimHdr & A) +{ + Out.write((char const *) &A,sizeof(A)); +} + +void DpAnimFilter::WriteCycs(ofstream & Out) +{ + ColCycles MyCycles[MAX_NUMBER_CYCLES]; + Out.write((char const *) MyCycles,sizeof(MyCycles)); +} + +void DpAnimFilter::WritePal(ofstream & Out,Palette const & P) +{ + for (int f=0;f<256;f++) + { + Out.put((u8)P[f].GetB()); + Out.put((u8)P[f].GetG()); + Out.put((u8)P[f].GetR()); + Out.put((u8)0); + } +} + +void DpAnimFilter::WriteLpTable(ofstream & Out,LpObjVec & LpTable) +{ + int WriteFromTable; + int WriteDummy; + + int Val=Out.tellp(); + + WriteFromTable=min(MAX_LARGE_PAGE,LpTable.size()); + + for (int f=0;f0) + { + for (int f=0;f= ActualLen) + Increase(RESIZE_CHUNKS); + + BinData[Len]=Byte; + + Len+=1; +} + +void CompFrame::WriteWord(u16 Word) +{ + if ((Len+2) >= ActualLen) + Increase(RESIZE_CHUNKS); + + BinData[Len]=Word&0xff; + BinData[Len+1]=Word>>8; + + Len+=2; +} + +void CompFrame::WriteData(u8 const * Src,int Amount) +{ + if ((Len+Amount) >= ActualLen) + Increase(Amount+RESIZE_CHUNKS); + + memcpy(&BinData[Len],Src,Amount); + + Len+=Amount; + +} + +void CompFrame::Write(ofstream & Out) const +{ + if (Len) + { + char const * Data; + Data=(char const *)BinData; + + Out.write(Data,Len); + } +} + +ostream & operator<<(ostream & str,CompFrame & Fr) +{ + str<<"size: "< 64000) && MyFrames.size()) + return(false); + else + { + MyFrames.push_back(C); + return(true); + } +} + +void LpObj::operator=(LpObj const & No) +{ + BaseRecord=No.BaseRecord; + MyFrames=No.MyFrames; +} + +int LpObj::GetSizeInAll(void) const +{ + int Total=0; + + for (int f=0;f>8)); +} + + +/*=========================================================================== + end */ + + + + diff --git a/Utils/Libs/GLib/Dpanim.hpp b/Utils/Libs/GLib/Dpanim.hpp new file mode 100644 index 000000000..884102465 --- /dev/null +++ b/Utils/Libs/GLib/Dpanim.hpp @@ -0,0 +1,180 @@ +/*========================================================================= + + DPANM.HPP + + Author: Gary Liddon @ Fareham + Created: + Project: + Purpose: + + Copyright (c) 1997 Gary Liddon + +===========================================================================*/ + +#ifndef __DPANM_HPP__ +#define __DPANM_HPP__ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include +#include + +/* Glib + ---- */ + +/* Local + ----- */ +#include "gtypes.h" +#include "ganim.hpp" + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ +#define MAX_LARGE_PAGE 256 +#define MAX_RECORDS_PER_LP 256 + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +class CompFrame; +class LpObj; + +typedef std::vector LpObjVec; + +class GLIB_API DpAnimFilter : public GAnimFilter +{ +public: + DpAnimFilter(); + ~DpAnimFilter(void); + DpAnimFilter(char const * FName); + + virtual bool Load(GAnim & Anm); + virtual bool Save(GAnim & Anm); + + virtual GAnimFilter * Create(char const * Name) {return (new DpAnimFilter(Name));} + +protected: + + class AnimHdr + { + public: + AnimHdr(); + void InitHdr(void); + + + ULONG id; // ID == "IPF " + UWORD maxLps; // Maximum Number of LPs + UWORD nLps; // Number of LPs in file + ULONG lRecords; // Number of records in file + UWORD maxRecsPerLp; // Maximum number of records per LP + UWORD lpfTableOffset; // Offset to start of LP Table + ULONG contentType; // == "ANIM" + UWORD width; // Width in pixels of drawing + UWORD height; // Height in pixel of drawing + UBYTE variant; + UBYTE version; + UBYTE hasLastDelta; // Has a delta for first to last + UBYTE lastDeltaValid; // The delta is valid + UBYTE pixelType; + UBYTE highestBBComp; + UBYTE otherRecordsPerFrame; + UBYTE bitmapRecordsPerFrame; + UBYTE recordTypes[32]; + ULONG nFrames; // Number of frames in the file + UWORD framesPerSecond; + UWORD pad2[29]; + }; + + struct ColCycles + { + ColCycles(); + + UWORD count; + UWORD rate; + UWORD flags; + UBYTE low, high; + }; + + struct LpTable + { + UWORD baseRecord; + UWORD nRecords; + UWORD nBytes; + }; + + struct LpTableMem + { + UWORD baseRecord; + UWORD nRecords; + UWORD nBytes; + UWORD size[MAX_RECORDS_PER_LP]; + }; + + +protected: + void GetPal(Palette & MyPal); + + + ULONG anm_init2(char const * filename); + ULONG anm_init(FILE * filename); + int anm_read_next_frame(void); + void anm_read_first_frame(void); + + void read_LP(UWORD LP_to_load); + void PlayRunSkipDump(UBYTE const *src, UBYTE *dst); + + UWORD loaded_LP; // Which LP is currently loaded + + UWORD current_Frame; // Which frame is being displayed + FILE * inANM; // Input anm file + UBYTE * lp_buffer; // Input buffer for the currently loaded LP + UBYTE * dst_buffer; // Location to play the frames to + + void InitVars(void); + void MakeVars(void); + void DeleteVars(void); + + AnimHdr lpfHdr; + ColCycles Color_Cycles; + LpTable * lpfTable; + LpTableMem * current_LP; + + + void MakeAnim(void); + void WriteHdr(std::ofstream & Out,AnimHdr & A); + void WriteCycs(std::ofstream & Out); + void WritePal(std::ofstream & Out,Palette const & P); + void WriteLpTable(std::ofstream & Out,LpObjVec & LpTable); + void WriteLps(std::ofstream & Out,LpObjVec & LpTable); + void DiscardAnim(void); + + + void Encode(Frame const & Fr,CompFrame & Cfr); + void WriteRun(u8 Val,int Length,CompFrame &Cfr); + void WriteDataRun(u8 const * Data,int Length,CompFrame &Cfr); + int GetDataLength(u8 const * Src,int BytesLeft); + int GetRunLength(u8 const * Src,int BytesLeft); + void WriteEnd(CompFrame &Cfr); + + enum + { +// MAX_RUN_WRITE = 16384-10 + MAX_RUN_WRITE = 10240 + }; + + + +}; + +/*---------------------------------------------------------------------- */ + +#endif /* __DPANM_HPP__ */ + +/*=========================================================================== + end */ + + diff --git a/Utils/Libs/GLib/Frame.cpp b/Utils/Libs/GLib/Frame.cpp new file mode 100644 index 000000000..a626d9014 --- /dev/null +++ b/Utils/Libs/GLib/Frame.cpp @@ -0,0 +1,1658 @@ +/*========================================================================= + + FRAME.CPP + + Author: Gary Liddon @ Fareham + Created: + Project: + Purpose: + + Copyright (c) 1997 G R Liddon + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include +#include + +/* STL + --- */ +//#include +#include + + +/* Glib + ---- */ + +/* Local + ----- */ +#include "frame.hpp" +#include "ilbm.hpp" +#include "gutils.h" +#include "misc.hpp" + +using namespace std; + +static void load_bmp(Frame & frm,char const *filename); + + +/*---------------------------------------------------------------------- + Function: Frame::Frame(void) + ---------------------------------------------------------------------- */ +Frame::Frame(void) +{ + InitFrame(); +} + +/*---------------------------------------------------------------------- + Function: Copy Constructor + ---------------------------------------------------------------------- */ +Frame::Frame(Frame const & Fr) +{ + InitFrame(); + CopyFrame(Fr); +} + +/*---------------------------------------------------------------------- + Function: Destructor + ---------------------------------------------------------------------- */ +Frame::~Frame(void) +{ + if (Buffa) + delete Buffa; +} + +/*---------------------------------------------------------------------- + Function: Init the Frame + ---------------------------------------------------------------------- */ +void Frame::InitFrame(void) +{ + Buffa=NULL; + X=0; + Y=0; +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void Frame::CopyFrame(Frame const & Fr) +{ + DumpStuff(); + + Width=Fr.Width; + Height=Fr.Height; + + if (Fr.Buffa) + { + if (!(Buffa=new u8[Width*Height])) + Error(ERM_OUTOFMEM); + + memcpy(Buffa,Fr.Buffa,Width*Height); + } + else + Buffa=NULL; + + Name=Fr.Name; + + MyPal=Fr.MyPal; + + X=Fr.X; + Y=Fr.Y; +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void Frame::DumpStuff(void) +{ + if (Buffa) + delete Buffa; + Width=0; + Height=0; + X=0; + Y=0; + +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +bool Frame::IsBlank(u8 BlankVal) +{ + if (Buffa) + { + for (int f=0;fGetR(); + g = C->GetG();; + b = C->GetB();; + + lastr = LC->GetR(); + lastg = LC->GetG(); + lastb = LC->GetB(); + + nextr = NC->GetR(); + nextg = NC->GetG(); + nextb = NC->GetB(); + + dist1 = ((lastr-r)*(lastr-r)) + ((lastg-g)*(lastg-g)) + ((lastb-b)*(lastb-b)); + dist2 = ((nextr-r)*(nextr-r)) + ((nextg-g)*(nextg-g)) + ((nextb-b)*(nextb-b)); + + if(dist1 < dist2) + { + if(dist1 <= Threshold) + { + *p = lastcol; + } + } + else + { + if(dist2 <= Threshold) + *p = nextcol; + } + } + lastcol = *p++; + } + } +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void Frame::ReduceSize(void) +{ + Rect MRect=FindBoundingRect(); + Crop(MRect); +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void Frame::Crop(Rect const & NewRect) +{ + if (Buffa && NewRect) + { + Rect CopyRect; + CopyRect=*this; + CopyRect.X=0; + CopyRect.Y=0; + + Rect MyRect=NewRect; + + CopyRect.ClipRect(MyRect); + CopyRect=MyRect; + + if (CopyRect) + { + u8 * DestBuffa; + + if (!(DestBuffa=new u8[NewRect.Area()])) + Error(ERM_OUTOFMEM); + + memset(DestBuffa,0,NewRect.Area()); + + u8 * Src=&Buffa[CopyRect.X+CopyRect.Y*Width]; + u8 * Dst=DestBuffa; + + for (int yy=0;yy NewPic; + + NewPic.resize(NewWidth*NewHeight); + + for (int y=0;y=0 && Bottom==-1;y--) + { + for (x=0;x=Left && Right==-1;x--) + { + for (y=Top;y<=Bottom && Right==-1;y++) + if (Bmap[y*Width+x]) + Right=x; + } + + if (Right !=-1) + { + RetRect.X=Left; + RetRect.Y=Top; + RetRect.W=Right-Left+1; + RetRect.H=Bottom-Top+1; + } + else + Error(ERR_FATAL,"Strange frame"); + } + else + Error(ERR_FATAL,"Strange frame"); + } + else + Error(ERR_FATAL,"Strange frame"); + } + } + + return(RetRect); +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void Frame::SaveLbm(char const * Lbm) +{ + u8 * Pal; + + Pal=MyPal.MakeDpPal(); + nilbm::SavePbm((char *)Lbm,Pal,Buffa,Width,Height); + delete Pal; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void Frame::LoadLbm(char const * Lbm) +{ + nilbm MyLbm(Lbm); + + if (!MyLbm.error()) + { + if (Buffa) + delete Buffa; + + u8 const * CMap; + + Buffa=MyLbm.TakeBmap(); + CMap=MyLbm.SeeCmap(); + + Width=MyLbm.GetWidth(); + Height=MyLbm.GetHeight(); + + int NumOfCols=1< Ret) + Ret=Buffa[f]; + } + } + + return(Ret+1); +} + + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void Frame::FlipX(void) +{ + if (Buffa && Width && Height) + { + vector Buffa2; + + Buffa2.resize(Width*Height); + + for (int y=0;y Buffa2; + + Buffa2.resize(Width*Height); + + for (int y=0;y Colours; + Colours.resize(256); + + for (f=0;f<256;f++) + Colours[f].Uses=0; + + for (f=0;f0;f--) + { + int ColIndex=255-63+f; + + if (Colours[ColIndex].Uses) + NewPal[f]=Colours[ColIndex].C; + } + Remap(NewPal); + } +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void Frame::AddPixelSurround(int Col) +{ + if (Buffa && Col < MyPal.GetNumOfCols()) + { + Frame NewFrame; + + NewFrame=*this; + NewFrame.Width+=2; + NewFrame.Height+=2; + + NewFrame.DumpStuff(); + + if(!(NewFrame.Buffa=new u8[NewFrame.Width*NewFrame.Height])) + Error(ERM_OUTOFMEM); + + memset(NewFrame.Buffa,0,NewFrame.Width*NewFrame.Height); + + PlotTrans(NewFrame,0,0); + PlotTrans(NewFrame,0,1); + PlotTrans(NewFrame,0,2); + PlotTrans(NewFrame,1,0); + PlotTrans(NewFrame,1,2); + PlotTrans(NewFrame,2,0); + PlotTrans(NewFrame,2,1); + PlotTrans(NewFrame,2,2); + + for (int f=0;f (X+W)) + RectToClip.W-=(RectToClip.X+RectToClip.W)-(X+W); + + if ((RectToClip.Y+RectToClip.H) > (Y+H)) + RectToClip.H-=(RectToClip.Y+RectToClip.H)-(Y+H); + } + else + { + RectToClip.W=0; + RectToClip.H=0; + RectToClip.X=0; + RectToClip.Y=0; + } +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +bool Rect::IsColiding(Rect const & NewRect) const +{ + if ((NewRect.X+NewRect.W) <= X) + return(false); + + if ((X+W) <= NewRect.X) + return(false); + + if ((NewRect.Y+NewRect.H) <= Y) + return(false); + + if ((Y+H) <= NewRect.Y) + return(false); + + return(true); +} + + +struct RGB +{ + u8 r,g,b; +}; + +class BITMAP +{ +public: + BITMAP(void) + { + m_width=0; + m_height=0; + } + + void setSize(int width,int height) + { + bitMap.resize(width*height); + + m_width=width; + m_height=height; + } + + void clear(void) + { + if (m_width && m_height) + memset(&bitMap[0],0,m_width*m_height); + } + + void line(unsigned int y,unsigned int x,u8 pix) + { + if (x >= m_width || y >= m_height) + GObject::Error(ERR_FATAL,"out of bounds"); + else + bitMap[y*m_width+x]=pix; + } + + u8 const * getBitMap(void) const + {return(&bitMap[0]);} + +protected: + int m_width; + int m_height; + vector bitMap; +}; + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +#include +#include +#include +#include + + +#define BI_RGB 0 +#define BI_RLE8 1 +#define BI_RLE4 2 +#define BI_BITFIELDS 3 + +#define OS2INFOHEADERSIZE 12 +#define WININFOHEADERSIZE 40 + + +typedef struct BITMAPFILEHEADER +{ + unsigned long bfType; + unsigned long bfSize; + unsigned short bfReserved1; + unsigned short bfReserved2; + unsigned long bfOffBits; +} BITMAPFILEHEADER; + + +/* Used for both OS/2 and Windows BMP. + * Contains only the parameters needed to load the image + */ +typedef struct BITMAPINFOHEADER +{ + unsigned long biWidth; + unsigned long biHeight; + unsigned short biBitCount; + unsigned long biCompression; +} BITMAPINFOHEADER; + + +typedef struct WINBMPINFOHEADER /* size: 40 */ +{ + unsigned long biWidth; + unsigned long biHeight; + unsigned short biPlanes; + unsigned short biBitCount; + unsigned long biCompression; + unsigned long biSizeImage; + unsigned long biXPelsPerMeter; + unsigned long biYPelsPerMeter; + unsigned long biClrUsed; + unsigned long biClrImportant; +} WINBMPINFOHEADER; + + +typedef struct OS2BMPINFOHEADER /* size: 12 */ +{ + unsigned short biWidth; + unsigned short biHeight; + unsigned short biPlanes; + unsigned short biBitCount; +} OS2BMPINFOHEADER; + + + +/* read_bmfileheader: + * Reads a BMP file header and check that it has the BMP magic number. + */ +static int read_bmfileheader(Gifstream & f, BITMAPFILEHEADER *fileheader) +{ + fileheader->bfType = f.Get16(); + fileheader->bfSize= f.Get32(); + fileheader->bfReserved1= f.Get16(); + fileheader->bfReserved2= f.Get16(); + fileheader->bfOffBits= f.Get32(); + + if (fileheader->bfType != 19778) + return -1; + + return 0; +} + + + +/* read_win_bminfoheader: + * Reads information from a BMP file header. + */ +static int read_win_bminfoheader(Gifstream & f, BITMAPINFOHEADER *infoheader) +{ + WINBMPINFOHEADER win_infoheader; + + win_infoheader.biWidth = f.Get32(); + win_infoheader.biHeight = f.Get32(); + win_infoheader.biPlanes = f.Get16(); + win_infoheader.biBitCount = f.Get16(); + win_infoheader.biCompression = f.Get32(); + win_infoheader.biSizeImage = f.Get32(); + win_infoheader.biXPelsPerMeter = f.Get32(); + win_infoheader.biYPelsPerMeter = f.Get32(); + win_infoheader.biClrUsed = f.Get32(); + win_infoheader.biClrImportant = f.Get32(); + + infoheader->biWidth = win_infoheader.biWidth; + infoheader->biHeight = win_infoheader.biHeight; + infoheader->biBitCount = win_infoheader.biBitCount; + infoheader->biCompression = win_infoheader.biCompression; + + return 0; +} + + + +/* read_os2_bminfoheader: + * Reads information from an OS/2 format BMP file header. + */ +static int read_os2_bminfoheader(Gifstream & f, BITMAPINFOHEADER *infoheader) +{ + OS2BMPINFOHEADER os2_infoheader; + + os2_infoheader.biWidth = f.Get16(); + os2_infoheader.biHeight = f.Get32(); + os2_infoheader.biPlanes = f.Get32(); + os2_infoheader.biBitCount = f.Get32(); + + infoheader->biWidth = os2_infoheader.biWidth; + infoheader->biHeight = os2_infoheader.biHeight; + infoheader->biBitCount = os2_infoheader.biBitCount; + infoheader->biCompression = 0; + + return 0; +} + + +/* read_bmicolors: + * Loads the color pallete for 1,4,8 bit formats. + */ +static void read_bmicolors(int ncols, RGB *pal, Gifstream & f,int win_flag) +{ + int i; + + for (i=0; i> 1; + } + } + + pix = b[j]; + bmp->line(line,i,pix); + } +} + + + +/* read_4bit_line: + * Support function for reading the 4 bit bitmap file format. + */ +static void read_4bit_line(int length, Gifstream & f, BITMAP *bmp, int line) +{ + unsigned char b[8]; + unsigned long n; + int i, j, k; + int temp; + int pix; + + for (i=0; i> 4; + b[k*2] = temp & 15; + n = n >> 8; + } + } + + pix = b[j]; + bmp->line(line,i,pix); + } +} + + + +/* read_8bit_line: + * Support function for reading the 8 bit bitmap file format. + */ +static void read_8bit_line(int length, Gifstream & f, BITMAP *bmp, int line) +{ + unsigned char b[4]; + unsigned long n; + int i, j, k; + int pix; + + for (i=0; i> 8; + } + } + pix = b[j]; + bmp->line(line,i,pix); + } +} + + +/* read_24bit_line: + * Support function for reading the 24 bit bitmap file format, doing + * our best to convert it down to a 256 color pallete. + */ +static void read_24bit_line(int length, Gifstream & f, BITMAP *bmp, int line) +{ +#if 0 + int i, nbytes; + RGB c; + + nbytes=0; + + for (i=0; iline[line][i*3+_rgb_r_shift_24/8] = c.r; + bmp->line[line][i*3+_rgb_g_shift_24/8] = c.g; + bmp->line[line][i*3+_rgb_b_shift_24/8] = c.b; + nbytes += 3; + } + + nbytes = nbytes % 4; + if (nbytes != 0) + for (i=nbytes; i<4; i++) + f.get();; +#endif +} + + + +/* read_image: + * For reading the noncompressed BMP image format. + */ +static void read_image(Gifstream & f, BITMAP *bmp, BITMAPINFOHEADER *infoheader) +{ + int i, line; + + for (i=0; i<(int)infoheader->biHeight; i++) { + line = i; + + switch (infoheader->biBitCount) { + + case 1: + read_1bit_line(infoheader->biWidth, f, bmp, infoheader->biHeight-i-1); + break; + + case 4: + read_4bit_line(infoheader->biWidth, f, bmp, infoheader->biHeight-i-1); + break; + + case 8: + read_8bit_line(infoheader->biWidth, f, bmp, infoheader->biHeight-i-1); + break; + + case 24: + read_24bit_line(infoheader->biWidth, f, bmp, infoheader->biHeight-i-1); + break; + } + } +} + + + +/* read_RLE8_compressed_image: + * For reading the 8 bit RLE compressed BMP image format. + */ +static void read_RLE8_compressed_image(Gifstream & f, BITMAP *bmp, BITMAPINFOHEADER *infoheader) +{ + unsigned char count, val, val0; + int j, pos, line; + int eolflag, eopicflag; + + eopicflag = 0; + line = infoheader->biHeight - 1; + + while (eopicflag == 0) { + pos = 0; /* x position in bitmap */ + eolflag = 0; /* end of line flag */ + + while ((eolflag == 0) && (eopicflag == 0)) { + count = f.get(); + val = f.get(); + + if (count > 0) { /* repeat pixel count times */ + for (j=0;jline(line,pos,val); + pos++; + } + } + else { + switch (val) { + + case 0: /* end of line flag */ + eolflag=1; + break; + + case 1: /* end of picture flag */ + eopicflag=1; + break; + + case 2: /* displace picture */ + count = f.get(); + val = f.get(); + pos += count; + line -= val; + break; + + default: /* read in absolute mode */ + for (j=0; jline(line,pos,val0); + pos++; + } + + if (j%2 == 1) + val0 = f.get(); /* align on word boundary */ + break; + + } + } + + if (pos > (int)infoheader->biWidth) + eolflag=1; + } + + line--; + if (line < 0) + eopicflag = 1; + } +} + + + +/* read_RLE4_compressed_image: + * For reading the 4 bit RLE compressed BMP image format. + */ +static void read_RLE4_compressed_image(Gifstream & f, BITMAP *bmp, BITMAPINFOHEADER *infoheader) +{ + unsigned char b[8]; + unsigned char count; + unsigned short val0, val; + int j, k, pos, line; + int eolflag, eopicflag; + + eopicflag = 0; /* end of picture flag */ + line = infoheader->biHeight - 1; + + while (eopicflag == 0) { + pos = 0; + eolflag = 0; /* end of line flag */ + + while ((eolflag == 0) && (eopicflag == 0)) { + count = f.get(); + val = f.get(); + + if (count > 0) { /* repeat pixels count times */ + b[1] = val & 15; + b[0] = (val >> 4) & 15; + for (j=0; jline(line,pos,b[j%2]); + pos++; + } + } + else { + switch (val) { + + case 0: /* end of line */ + eolflag=1; + break; + + case 1: /* end of picture */ + eopicflag=1; + break; + + case 2: /* displace image */ + count = f.get(); + val = f.get(); + pos += count; + line -= val; + break; + + default: /* read in absolute mode */ + for (j=0; j> 4; + b[2*k] = val0 & 15; + val0 = val0 >> 4; + } + } + bmp->line(line,pos,b[j%4]); + pos++; + } + break; + } + } + + if (pos > (int)infoheader->biWidth) + eolflag=1; + } + + line--; + if (line < 0) + eopicflag = 1; + } +} + + + +/* load_bmp: + * Loads a Windows BMP file, returning a bitmap structure and storing + * the pallete data in the specified pallete (this should be an array of + * at least 256 RGB structures). + * + * Thanks to Seymour Shlien for contributing this function. + */ + +static void load_bmp(Frame & frm,char const *filename) +{ + BITMAPFILEHEADER fileheader; + BITMAPINFOHEADER infoheader; + + RGB pal[256]; + BITMAP bmp; + + Gifstream f(Gifstream::LITTLE_ENDIAN); + + int ncol; + unsigned long biSize; + + f.open(filename,ios::in|ios::binary); + + if (!f) + GObject::Error(ERR_FATAL,"couldn't open file %s",filename); + + if (read_bmfileheader(f, &fileheader) != 0) + { + GObject::Error(ERR_FATAL,"error reading bmp hdr for %s",filename); + } + + biSize = f.Get32(); + + if (biSize == WININFOHEADERSIZE) + { + if (read_win_bminfoheader(f, &infoheader) != 0) + GObject::Error(ERR_FATAL,"error reading windows bmp info hdr for %s",filename); + + /* compute number of colors recorded */ + ncol = (fileheader.bfOffBits - 54) / 4; + read_bmicolors(ncol, pal, f, 1); + } + else if (biSize == OS2INFOHEADERSIZE) + { + if (read_os2_bminfoheader(f, &infoheader) != 0) + GObject::Error(ERR_FATAL,"error reading os2 bmp info hdr for %s",filename); + + /* compute number of colors recorded */ + ncol = (fileheader.bfOffBits - 26) / 3; + read_bmicolors(ncol, pal, f, 0); + } + else + { + GObject::Error(ERR_FATAL,"error finding correct hdr for bmp %s",filename); + } + + if (infoheader.biBitCount != 4 && infoheader.biBitCount != 8) + GObject::Error(ERR_FATAL,"only handles 4 && 8 bit bmps not %d : %s",infoheader.biBitCount,filename); + + + bmp.setSize(infoheader.biWidth, infoheader.biHeight); + + bmp.clear(); + + switch (infoheader.biCompression) + { + + case BI_RGB: + read_image(f, &bmp, &infoheader); + break; + + case BI_RLE8: + read_RLE8_compressed_image(f, &bmp, &infoheader); + break; + + case BI_RLE4: + read_RLE4_compressed_image(f, &bmp, &infoheader); + break; + + default: + GObject::Error(ERR_FATAL,"unknown compression foramt for %s",filename); + break; + } + + f.close(); + + + { + Palette palObj; + + + + for (int f=0;f +#include + +/* Glib + ---- */ +#include "gobject.hpp" +#include "gtypes.h" +#include "gstring.hpp" + +/* Local + ----- */ +#include "pal.hpp" + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ +class GLIB_API Rect : public GObject +{ +public: + Rect(Rect const & R) + { MakeCopy(R);} + + void operator=(Rect const & R) + { MakeCopy(R);} + + Rect(void); + Rect(int nX,int nY,int nW,int nH) + { + X=nX; + Y=nY; + W=nW; + H=nH; + } + + /* Compare by volume */ + + bool operator< (Rect const & R) const + { + return((W*H)<(R.W*R.H)); + } + + bool IsColiding(Rect const & NewRect) const; + void ClipRect(Rect & RectToClip) const; + int Area(void) const {return(W*H);} + + operator int() const{return(W || H);} + + GLIB_API friend std::ostream & operator<<(std::ostream & str,Rect const & Fr); + + int X,Y; + int W,H; + +protected: + void MakeCopy(Rect const & R) + { + X=R.X; + Y=R.Y; + W=R.W; + H=R.H; + } +}; + +class GLIB_API Frame : public GObject +{ +public: + void ReduceColours(int TargColours); + void Expand(int BorderWidth,bool DupeEdges); + void AddPixelSurround(int Col); + + void MakeRGBA(u8 * Dest,bool ZeroIsTrans=true); + + Frame(void); + Frame(Frame const &); + ~Frame(void); + + void Plot(Frame & Dest,int X,int Y); + void PlotTrans(Frame & Dest,int X,int Y,int TrCol=0); + void PlotBox(Rect const & R,int Col); + + + void operator=(Frame const &Fr){CopyFrame(Fr);} + + operator Rect() const{return(Rect(X,Y,Width,Height));} + + bool operator<(Frame const &Fr) const; + bool operator==(Frame const &Fr) const; + + void SetFrame(u8 const * nBuffa,int W,int H,Palette const & NewPal); + + void ReduceSinglePixels(int Threshold); + void SaveLbm(char const * Lbm); + + void ReduceSize(void); + Rect FindBoundingRect(void); + void Crop(Rect const & RetRect); + + bool Grab(Frame const & Fr,Rect const & RetRect); + bool IsBlank(u8 BlankVal=0); + + u8 const * SeeData(void) const {return(Buffa);} + int GetWidth(void) const {return(Width);} + int GetHeight(void)const {return(Height);} + int GetNumOfCols(void) const; + + bool DrawBox(Rect const & R,u8 Col); + + const Palette & GetPal(void) const {return(MyPal);} + + Palette & GetPal(void){return(MyPal);} + + void SetPal(Palette const & NewPal){MyPal=NewPal;} + void Remap(Palette const & P); + void LoadLbm(char const * Lbm); + void ReplaceColour(int ColNum,int RepNum); + + void SetX(int nX) {X=nX;} + void SetY(int nY) {Y=nY;} + + int GetX(void) const {return(X);} + int GetY(void) const {return(Y);} + const char * GetName(void) const { return(Name); } + + void SetName(const char * NewName) { Name=NewName; } + void ReducePal(void); + void Clear(int Col); + + void LoadBMP(char const * Lbm); + + void FlipX(void); + void FlipY(void); + + void Resize(int NewWidth,int NewHeight); + +protected: + void CopyFrame(Frame const &); + + void DumpStuff(void); + void InitFrame(void); + + Palette MyPal; + + GString Name; + + u8 * Buffa; + int Width; + int Height; + int X,Y; +}; + +/*---------------------------------------------------------------------- */ + +#endif /* __FRAME_HPP__ */ + +/*=========================================================================== + end */ + + diff --git a/Utils/Libs/GLib/GSYS.C b/Utils/Libs/GLib/GSYS.C new file mode 100644 index 000000000..00fcc37c8 --- /dev/null +++ b/Utils/Libs/GLib/GSYS.C @@ -0,0 +1,195 @@ +/* =========================================================================== + File: GSYS.C + + Notes: MSVS PC implemtatiom of GSYS.H api + + Author: G Robert Liddon @ 73b + + Created: Wednesday 27th March 1996 + + Copyright (C) 1996 DCI Ltd All rights reserved. + ============================================================================ */ + + + /* --------------------------------------------------------------------------- + Includes + -------- */ + +/* Standard Lib + ------------ */ +#include "setjmp.h" + +/* Glib + ---- */ +#include "gtypes.h" +#include "gsys.h" + + +/* --------------------------------------------------------------------------- + Defines + ------- */ + +/* --------------------------------------------------------------------------- + Function Prototypes + ------------------- */ +static void MyFunc(void); + +/* --------------------------------------------------------------------------- + Defines + ------- */ + +/* --------------------------------------------------------------------------- + Exterenal Variables + ------------------- */ + +/* --------------------------------------------------------------------------- + Variables + --------- */ + +/* --------------------------------------------------------------------------- + Define the PSX stack + -------------------- */ +extern int _stacksize; + +/* --------------------------------------------------------------------------- + Tables + ------ */ +MEM_INFO WorkMemInfo= +{ + NULL, + 0, +}; + +/* --------------------------------------------------------------------------- + Function: const MEM_INFO *GSYS_GetMemInfo(MEM_ID Id) + + Purpose: Get description of a free mem type for this system + + Returns: -> struct containg info + + --------------------------------------------------------------------------- */ +const MEM_INFO *GSYS_GetWorkMemInfo(void) +{ + return(&WorkMemInfo); +} + +/* --------------------------------------------------------------------------- + Function: void GSYS_SetStackAndJump(void *Stack,void(*Func)(void *),void *Param) + + Purpose: Set the stack pointer and jump to a routine on PSX + + Params: Stack -> New Stack Ptr + Func -> Routine to jump to + Param Parameter to pass to function + + --------------------------------------------------------------------------- */ +static void * SaveParam; +void (*SaveFunc)(void *); + +void GSYS_SetStackAndJump(void *Stack,void(*Func)(void *),void *Param) +{ + jmp_buf GazBuff; + _JUMP_BUFFER * PcBuff; + + SaveParam=Param; + SaveFunc=Func; + PcBuff=(_JUMP_BUFFER *) GazBuff; + setjmp(GazBuff); + PcBuff->Eip=(unsigned long)MyFunc; + PcBuff->Esp=(unsigned long)Stack; + longjmp(GazBuff,0); + + Func(Param); +} + +void MyFunc(void) +{ + SaveFunc(SaveParam); +} + +/* --------------------------------------------------------------------------- + Function: void GSYS_MarkStack(void * Stack, U32 StackSize) + + Purpose: Marks a stack so that it can be checked to see if it's + been corrupted. In GSYS to account for stack direction + on different platforms + + Params: Stack -> Stack + StackSize Stack Size + + --------------------------------------------------------------------------- */ + +#define STACK_MARK_CODE 0xabcd0123 + +void GSYS_MarkStack(void * Stack, U32 StackSize) +{ + U32 * StackStart; + + StackStart=Stack; + (*StackStart)=STACK_MARK_CODE; +} + + +/* --------------------------------------------------------------------------- + Function: BOOL GSYS_IsStackCorrupted(void * Stack, U32 StackSize) + + Purpose: Check to see if a previously marked stack has been corrupted + + Params: Stack -> Stack + StackSize Stack Size + + Returns: TRUE if it has + + --------------------------------------------------------------------------- */ +BOOL GSYS_IsStackCorrupted(void * Stack, U32 StackSize) +{ + U32 * StackStart; + StackStart=Stack; + + return (*StackStart!=STACK_MARK_CODE); +} + +/* --------------------------------------------------------------------------- + Function: BOOL GSYS_InitMachine(void) + + Purpose: Initialise the machine for work + + Returns: Succesful of not + + --------------------------------------------------------------------------- */ +BOOL GSYS_InitMachine(void) +{ + return(TRUE); +} + + +/* --------------------------------------------------------------------------- + Function: BOOL GSYS_CheckPtr(void *Ptr) + + + Purpose: See if this ptr -> to an address within the machines + memor + + Returns: Succesful of not + + --------------------------------------------------------------------------- */ +BOOL GSYS_CheckPtr(void *Ptr) +{ + return(TRUE); +} + +/* --------------------------------------------------------------------------- + Function: BOOL GSYS_IsStackOutOfBounds(void* Stack, U32 StackSize) + + Purpose: Is the current sp outside the range of the stack + + Returns: TRUE if so + + --------------------------------------------------------------------------- */ +BOOL GSYS_IsStackOutOfBounds(void* Stack, U32 StackSize) +{ + return(FALSE); +} + +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/GTIMSYS.C b/Utils/Libs/GLib/GTIMSYS.C new file mode 100644 index 000000000..d26c6f1be --- /dev/null +++ b/Utils/Libs/GLib/GTIMSYS.C @@ -0,0 +1,64 @@ +/* =========================================================================== + File: GTIMSYS.C + + Notes: Timing system stuff needed by gtim + + Author: G Robert Liddon @ 73b + + Copyright (C) 1996 DCI Ltd All rights reserved. + ============================================================================ */ + +/* --------------------------------------------------------------------------- + Standard Lib Includes + --------------------- */ + +/* Standard Lib + ------------ */ +#include "stdio.h" + +/* PSX Os + ------ */ + +/* Glib + ---- */ +#include "gdebug.h" + +/* Headers + ------- */ +#include "gtimsys.h" + +/* --------------------------------------------------------------------------- + Defines and Enums + ----------------- */ + +/* --------------------------------------------------------------------------- + Structs + ------- */ + +/* --------------------------------------------------------------------------- + Vars + ---- */ + +/* --------------------------------------------------------------------------- + Code n that + --------------------------------------------------------------------------- */ + +U32 GTIMSYS_GetTimer(void) +{ + return(0); + +} + +void GTIMSYS_ResetTimer(void) +{ +} + + +U32 GTIMSYS_InitTimer(void) +{ + return(0); +} + + +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/Ganim.cpp b/Utils/Libs/GLib/Ganim.cpp new file mode 100644 index 000000000..87efdb88b --- /dev/null +++ b/Utils/Libs/GLib/Ganim.cpp @@ -0,0 +1,164 @@ +/*========================================================================= + + GANIM.CPP + + Author: Gary Liddon @ Fareham + Created: + Project: + Purpose: + + Copyright (c) 1997 Gary Liddon + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ + +/* STL + --- */ + +/* Glib + ---- */ +#include "gfname.hpp" + +/* Local + ----- */ +#include "ganim.hpp" + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Function types + -------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ +std::vector FilterDetails::AllDetails ; + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Function: GAnim::GAnim(void) + Notes: Constructor + ---------------------------------------------------------------------- */ +GAnim::GAnim(void) +{ + TheFrames.reserve(2000); +} + +GAnim::~GAnim(void) +{ +} + + +bool GAnim::Load(GAnimFilter & Af,char const * IoName) +{ + bool RetVal; + + if (IoName) + Af.SetName(IoName); + + RetVal=Af.Load(*this); + + return(RetVal); + +} + +bool GAnim::Save(GAnimFilter & Af,char const * IoName) +{ + bool RetVal; + + if (IoName) + Af.SetName(IoName); + + RetVal=Af.Save(*this); + + return(RetVal); +} + +Frame & GAnim::GetNewFrame(void) +{ + return(operator[](TheFrames.size())); +} + +int GAnim::AddPalette(Palette & P) +{ + for (int f=0;fCreate(Fname); + } + + return(RetFilter); +} + + + +Frame & GAnim::operator[](int Index) +{ + if (Index >= TheFrames.capacity()) + TheFrames.reserve(TheFrames.capacity()+1000); + + if (Index >= TheFrames.size()) + TheFrames.resize(TheFrames.size()+1); + + return(TheFrames[Index]); +} + +/*=========================================================================== + end */ + + + + diff --git a/Utils/Libs/GLib/Ganim.hpp b/Utils/Libs/GLib/Ganim.hpp new file mode 100644 index 000000000..d95cb88e9 --- /dev/null +++ b/Utils/Libs/GLib/Ganim.hpp @@ -0,0 +1,127 @@ +/*========================================================================= + + GANIM.HPP + + Author: Gary Liddon @ Fareham + Created: + Project: + Purpose: + + Copyright (c) 1997 Gary Liddon + +===========================================================================*/ + +#ifndef __GANIM_HPP__ +#define __GANIM_HPP__ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ + +/* STL + --- */ +#include + +/* Glib + ---- */ +#include "gobject.hpp" +#include "gstring.hpp" + +/* Local + ----- */ +#include "frame.hpp" + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +class GLIB_API GAnimFilter; + +class GLIB_API GAnim : public GObject +{ +public: + GAnim(void); + ~GAnim(void); + + bool Load(GAnimFilter & Af,char const * Name=NULL); + bool Save(GAnimFilter & Af,char const * Name=NULL); + Frame & GetNewFrame(void); + + int AddPalette(Palette & Pal); + + int NumOfFrames(void) {return(TheFrames.size());} + + Palette & GetPal(int PalId) + { + if (PalId >= ThePals.size()) + Error(ERR_FATAL,"Bounds error pals %d",PalId); + return(ThePals[PalId]); + } + + Frame & operator[](int Index); + +protected: + typedef std::vector PalVec; + typedef PalVec::iterator PalVecIt; + + typedef std::vector FrameVec; + typedef FrameVec::iterator FrameVecIt; + + PalVec ThePals; + FrameVec TheFrames; + +}; + + + +class GLIB_API GAnimFilter : public GObject +{ +public: + GAnimFilter(){;} + GAnimFilter(char const * FName); + + virtual bool Load(GAnim & Anm)=0; + virtual bool Save(GAnim & Anm)=0; + + virtual GAnimFilter * Create(char const *)=0; + void SetName(char const * NewName) {FileName=NewName;} + char const * GetName(void) const {return(FileName);} + +protected: + GString FileName; + +}; + +class GLIB_API FilterDetails +{ +public: + FilterDetails(); + + FilterDetails(GAnimFilter & nAnmFilter,char const * nExt); + + static GAnimFilter * GetFilter(char const * Fname); + + bool operator<(FilterDetails const &) const; + bool operator==(FilterDetails const &) const; + +protected: + GString Ext; + GAnimFilter * AnmFilter; + + static std::vector AllDetails; +}; + +/*---------------------------------------------------------------------- */ + +#endif /* __GANIM_HPP__ */ + +/*=========================================================================== + end */ + + diff --git a/Utils/Libs/GLib/Gdebug.c b/Utils/Libs/GLib/Gdebug.c new file mode 100644 index 000000000..9c94fa773 --- /dev/null +++ b/Utils/Libs/GLib/Gdebug.c @@ -0,0 +1,123 @@ +/* =========================================================================== + File: GDEBUG.C + + Notes: PSX Implementation of glib debug api + + Author: G Robert Liddon @ 73b + + Created: Wednesday 27th March 1996 + + Copyright (C) 1996 DCI Ltd All rights reserved. + ============================================================================ */ + + +/* --------------------------------------------------------------------------- + Standard Lib Includes + --------------------- */ +#include "stdio.h" + +/* --------------------------------------------------------------------------- + Glib Includes + ------------- */ +#include "gdebug.h" + +/* --------------------------------------------------------------------------- + Game Includes + ------------- */ + +/* --------------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/* --------------------------------------------------------------------------- + Vars + ---- */ +void (*MsgFunc)(char *e,va_list argptr); +void (*ErrorFunc)(char *Text,char *File,int Line); + +/* --------------------------------------------------------------------------- + Function: BOOL DBG_OpenModule(void); + + Purpose: Initialise the debug module + + Returns: FALSE if unable to init + + --------------------------------------------------------------------------- */ +GLIB_API BOOL DBG_OpenModule(void) +{ + return(TRUE); +} + + +/* --------------------------------------------------------------------------- + Function: void DBG_PollHost(void) + + Purpose: Poll the host to enable debugging + + --------------------------------------------------------------------------- */ +GLIB_API void DBG_PollHost(void) +{ +} + +/* --------------------------------------------------------------------------- + Function: void DBG_Halt(void) + + Purpose: Stop where I am + + --------------------------------------------------------------------------- */ +GLIB_API void DBG_Halt(void) +{ + while (1); +} + + +/* --------------------------------------------------------------------------- + Function: void DBG_SendMessage(char *e,...) + + Purpose: Send a diagnostic messgae + + --------------------------------------------------------------------------- */ + +GLIB_API void DBG_SendMessage(char *e,...) +{ +} + +/* --------------------------------------------------------------------------- + Function: void DBG_SetMessageHandler(void (*Func)(char *e,va_list argptr)) + + Purpose: Set the message handler + + --------------------------------------------------------------------------- */ +GLIB_API void DBG_SetMessageHandler(void (*Func)(char *e,va_list argptr)) +{ + MsgFunc=Func; +} +/* --------------------------------------------------------------------------- + Function: void DBG_Error(char *Text,char *File,int Line); + + Purpose: Send a msg to psyq host + + --------------------------------------------------------------------------- */ + +GLIB_API void DBG_Error(char *Text,char *File,int Line) +{ +} + +GLIB_API void DBG_SetErrorFunc(void (*EFunc)(char *Text,char *File,int Line)) +{ + ErrorFunc=EFunc; +} + +/* --------------------------------------------------------------------------- + Function: static void SendPsyqString(char *e) + + Purpose: Send a msg to psyq host + + --------------------------------------------------------------------------- */ +void SendPsyqString(char *e) +{ +} + + +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/Gfname.cpp b/Utils/Libs/GLib/Gfname.cpp new file mode 100644 index 000000000..12b8cf357 --- /dev/null +++ b/Utils/Libs/GLib/Gfname.cpp @@ -0,0 +1,326 @@ +/*========================================================================= + + GFNAME.HPP + + Author: Gary Liddon @ Watford + Created: 2nd March 1991 + Purpose: Filename manipulation class + + Copyright (c) 1991-1997 Gary Lidon + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include + +/* Glib + ---- */ + + +/* Local + ----- */ +#include "gfname.hpp" +#include "gstring.hpp" + +using namespace std; + +/*---------------------------------------------------------------------- + Function: Destructor + ---------------------------------------------------------------------- */ +GFName::~GFName(void) +{ +} + +/*---------------------------------------------------------------------- + Function: Constructor + ---------------------------------------------------------------------- */ +GFName::GFName(void) +{ + TDrive[0]=0; + TPath[0]=0; + TDir[0]=0; + TFile[0]=0; +} + + +/*---------------------------------------------------------------------- + Function: Constructor witn + ---------------------------------------------------------------------- */ +GFName::GFName(const char *zfname) +{ + FullName(zfname); +} + +void GFName::FullName(const char *zfname) +{ + if (zfname) + { + _splitpath(zfname,TDrive,TDir,TFile,TExt); + + if (strlen(TDir) > 1) + { + if (TDir[strlen(TDir)-1]=='\\') + TDir[strlen(TDir)-1]=0; + } + + Ext(TExt); + } + else + { + TDrive[0]=0; + TPath[0]=0; + TDir[0]=0; + TFile[0]=0; + } +} + +/*---------------------------------------------------------------------- + Function: Make a full name + ---------------------------------------------------------------------- */ +char const *GFName::FullName(void) +{ + _makepath(TPath,TDrive,TDir,TFile,TExt); + return RetStr(TPath); +} + +const char * GFName::RetStr(const char * Val) +{ + if (Val[0]=='\0') + return(NULL); + else + return(Val); +} + +void GFName::SetStr(char * Dest,char const * Source) +{ + if (Source) + { + char Temp[1000]; + strcpy(Temp,Source); + strcpy(Dest,Temp); + } + else + Dest[0]=0; +} + +/*---------------------------------------------------------------------- + Function: Make a full name + ---------------------------------------------------------------------- */ +void GFName::AddDir(char const *add) +{ + GString CurrDir; + CurrDir=Dir(); + + CurrDir+="\\"; + CurrDir+=add; + Dir(CurrDir); +} + +#define FF_PATHNAMEMAX _MAX_PATH+1 + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Convert relative to absolute paths +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +char *GFName::makeabsolute(char const *basepath,char const *offset, char * outstr) +{ + int i, j, len; + char basefilename[FF_PATHNAMEMAX]; + + if (offset[1] == ':') // Drive letter makes it absolute + strcpy(outstr, offset); + else + { + // Find path of the file + strcpy(outstr, basepath); + j = 0; + len = strlen(outstr); + for (i = len - 1; i >= 0; i--) + if (outstr[i] == '\\') + break; + else + outstr[i] = 0; + + // Find the filename + if (i > 0) + strcpy(basefilename, &basepath[i+1]); + else + basefilename[0] = 0; + len = strlen(basefilename); + for (i = len - 1; i >= 0; i--) + { + if (basefilename[i] == '.') + { + basefilename[i] = 0; + break; + } + else + basefilename[i] = 0; + } + + // Is it from the root directory? + if (offset[0] == '\\') + { + outstr[2] = 0; // Leave only the drive letter + } + else + { + // Parse the ../ paths + while ((offset[j] == '.') && (offset[j+1] == '.') && + (offset[j+2] == '\\')) + { + len = strlen(outstr); + if (len > 1) + outstr[len-1] = 0; + for (i = len - 2; i >= 0; i--) + if (outstr[i] == '\\') + break; + else + outstr[i] = 0; + j += 3; + } + } + + // Add the remaining offset to the name + + len = strlen(outstr); + + + while (offset[j]) + { +/* if (offset[j] == '#') // Same filename + { + strcat(outstr, basefilename); + len = strlen(outstr); + } + else + { + outstr[len++] = offset[j]; + outstr[len] = 0; + } +*/ + outstr[len++] = offset[j]; + outstr[len] = 0; + j++; + } + } + return outstr; +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Convert absolute to relative paths +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +char *GFName::makerelative(char const *basepath,char const *newpath, char *outstr) +{ + char temp1[FF_PATHNAMEMAX]; + char temp2[FF_PATHNAMEMAX]; + int i, j, match; + + // Are the filenames the same? + match = 0; + for (j = 0; j < FF_PATHNAMEMAX; j++) + { + if (basepath[j] != newpath[j]) + { + match = 0; + break; + } + else if (basepath[j] == '.') // Matching + { + match = 1; + break; + } + else if (basepath[j] == 0) // Identical + { + match = 0; + break; + } + } + if (match) // Matching paths and filenames + { + strcpy(outstr, "#"); + strcat(outstr, &newpath[j]); + } + else if (basepath[0] != newpath[0]) // Drive letters are different + strcpy(outstr, newpath); + else + { + // Find the paths + strcpy(temp1, basepath); + for (i = strlen(temp1) - 1; i >= 0; i--) + if (temp1[i] == '\\') + break; + else + temp1[i] = 0; + + strcpy(temp2, newpath); + for (i = strlen(temp2) - 1; i >= 0; i--) + if (temp2[i] == '\\') + break; + else + temp2[i] = 0; + + // Are the paths the same? + strcpy(outstr, ""); + if (strcmp(temp1, temp2) == 0) // Paths are the same + { + j = strlen(temp1); + } + else // Paths are different + { + j = 2; // Drives are the same + // Find different bits + for (i = 0; i < (int) strlen(temp1); i++) + { + if ((temp1[i] == '\\') && (temp2[i] == '\\')) + j = i + 1; + else + if (temp1[i] != temp2[i]) + break; + } + if (j > 3) + { + for (i = j; i < (int) strlen(temp1); i++) + if (temp1[i] == '\\') + strcat(outstr,"..\\"); + } + else + j = 2; + } + strcat(outstr, &newpath[j]); + } + return outstr; +} + + +ostream & operator<<(ostream & str,GFName & Name) +{ + str<<"!"; + + if (Name.FullName()) + str< +#include +#include +#include + +/* Glib + ---- */ +#include "gtypes.h" + +/* Local + ----- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +class GLIB_API GFName +{ +protected: + char TDrive[_MAX_DRIVE+1]; + char TDir[_MAX_DIR+1]; + char TPath[_MAX_PATH+1]; + char TFile[_MAX_FNAME+1]; + char TExt[_MAX_EXT+1]; + +public: + + GFName(void); + GFName(char const *zfname); + + ~GFName(void); + + char const *FullName(void); + char const *Drive(){return RetStr(TDrive);} + char const *Dir(){return RetStr(TDir);} + char const *File(){return RetStr(TFile);} + char const *Ext(){return RetStr(TExt);} + + void Drive(char const *new_drive){SetStr(TDrive,new_drive);} + void Dir(char const *new_dir){SetStr(TDir,new_dir);} + void File(char const *new_file){SetStr(TFile,new_file);} + + void Ext(char const *new_ext) + { + if (new_ext) + { + if (new_ext[0]=='.') + SetStr(TExt,new_ext+1); + else + SetStr(TExt,new_ext); + } + else + TExt[0]=0; + } + + void FullName(const char *zfname); + + void operator=(const char * zfname) {FullName(zfname);} + + void AddDir(char const *add); + + static char *makerelative(const char *basepath, const char *newpath, char *outstr); + static char *makeabsolute(const char *basepath, const char *offset, char *outstr); + + friend std::ostream &operator<<(std::ostream & str,GFName & Name); + + +private: + const char * RetStr(const char *); + void SetStr(char * Dest,char const * Source); + +}; + + +/*---------------------------------------------------------------------- */ + +#endif /* __GL_PC_GFNAME__ */ + +/*=========================================================================== + end */ + + + diff --git a/Utils/Libs/GLib/Glib.dsp b/Utils/Libs/GLib/Glib.dsp index 19dcb0907..11c06cc6c 100644 --- a/Utils/Libs/GLib/Glib.dsp +++ b/Utils/Libs/GLib/Glib.dsp @@ -85,35 +85,35 @@ LIB32=link.exe -lib # PROP Default_Filter "" # Begin Source File -SOURCE=Source\Gal.c +SOURCE=Gal.c # End Source File # Begin Source File -SOURCE=Source\Gmain.c +SOURCE=Gmain.c # End Source File # Begin Source File -SOURCE=Source\Gtimer.c +SOURCE=Gtimer.c # End Source File # Begin Source File -SOURCE=Source\Gutils.c +SOURCE=Gutils.c # End Source File # Begin Source File -SOURCE=Source\Ll.c +SOURCE=Ll.c # End Source File # Begin Source File -SOURCE=Source\Objs.c +SOURCE=Objs.c # End Source File # Begin Source File -SOURCE=Source\Tasker.c +SOURCE=Tasker.c # End Source File # Begin Source File -SOURCE=Source\Tick.c +SOURCE=Tick.c # End Source File # End Group # Begin Group "Core Headers" @@ -121,51 +121,51 @@ SOURCE=Source\Tick.c # PROP Default_Filter "" # Begin Source File -SOURCE=..\..\Glib\Include\Gal.h +SOURCE=Gal.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Gdebug.h +SOURCE=Gdebug.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Gmain.h +SOURCE=Gmain.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Gsys.h +SOURCE=Gsys.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Gtimer.h +SOURCE=Gtimer.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Gtimsys.h +SOURCE=Gtimsys.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Gtypes.h +SOURCE=Gtypes.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Gutils.h +SOURCE=Gutils.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Ll.h +SOURCE=Ll.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Objs.h +SOURCE=Objs.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Tasker.h +SOURCE=Tasker.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Tick.h +SOURCE=Tick.h # End Source File # End Group # Begin Group "PC Source" @@ -173,67 +173,67 @@ SOURCE=..\..\Glib\Include\Tick.h # PROP Default_Filter "" # Begin Source File -SOURCE=Source\Pc\Dpanim.cpp +SOURCE=Dpanim.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Frame.cpp +SOURCE=Frame.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Ganim.cpp +SOURCE=Ganim.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Gdebug.c +SOURCE=Gdebug.c # End Source File # Begin Source File -SOURCE=Source\Pc\Gfname.cpp +SOURCE=Gfname.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Gobject.cpp +SOURCE=Gobject.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Gstring.cpp +SOURCE=Gstring.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Gsys.c +SOURCE=Gsys.c # End Source File # Begin Source File -SOURCE=Source\Pc\Gtimsys.c +SOURCE=Gtimsys.c # End Source File # Begin Source File -SOURCE=Source\Pc\Iff.cpp +SOURCE=Iff.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Ilbm.cpp +SOURCE=Ilbm.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Misc.cpp +SOURCE=Misc.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Niff.cpp +SOURCE=Niff.cpp # End Source File # Begin Source File -SOURCE=Source\Pc\Pal.cpp +SOURCE=Pal.cpp # End Source File # Begin Source File -SOURCE=Source\PC\REGEX.C +SOURCE=REGEX.C # End Source File # Begin Source File -SOURCE=Source\Pc\tquant.cpp +SOURCE=tquant.cpp # End Source File # End Group # Begin Group "PC Headers" @@ -241,59 +241,59 @@ SOURCE=Source\Pc\tquant.cpp # PROP Default_Filter "" # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Dpanim.hpp +SOURCE=Dpanim.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Frame.hpp +SOURCE=Frame.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Ganim.hpp +SOURCE=Ganim.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Gfname.hpp +SOURCE=Gfname.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Gobject.hpp +SOURCE=Gobject.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Gstring.hpp +SOURCE=Gstring.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Iff.hpp +SOURCE=Iff.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Ilbm.hpp +SOURCE=Ilbm.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Misc.hpp +SOURCE=Misc.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Mtypes.h +SOURCE=Mtypes.h # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Niff.hpp +SOURCE=Niff.hpp # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\Pal.hpp +SOURCE=Pal.hpp # End Source File # Begin Source File -SOURCE=..\..\GLIB\Include\PC\REGEX.H +SOURCE=REGEX.H # End Source File # Begin Source File -SOURCE=..\..\Glib\Include\Pc\tquant.h +SOURCE=tquant.h # End Source File # End Group # Begin Group "Template" @@ -319,27 +319,27 @@ SOURCE=..\..\Template\Template.h # PROP Default_Filter "" # Begin Source File -SOURCE=Source\PC\pcre\chartables.c +SOURCE=chartables.c # End Source File # Begin Source File -SOURCE=Source\PC\pcre\internal.h +SOURCE=internal.h # End Source File # Begin Source File -SOURCE=Source\PC\pcre\maketables.c +SOURCE=maketables.c # End Source File # Begin Source File -SOURCE=Source\PC\pcre\pcre.c +SOURCE=pcre.c # End Source File # Begin Source File -SOURCE=..\..\GLIB\Include\PC\pcre.h +SOURCE=pcre.h # End Source File # Begin Source File -SOURCE=Source\PC\pcre\study.c +SOURCE=study.c # End Source File # End Group # End Target diff --git a/Utils/Libs/GLib/Gobject.cpp b/Utils/Libs/GLib/Gobject.cpp new file mode 100644 index 000000000..cd892b292 --- /dev/null +++ b/Utils/Libs/GLib/Gobject.cpp @@ -0,0 +1,133 @@ +/*========================================================================= + + GOBJECT.CPP + + Author: Gary Liddon @ Watford + Created: 4th May 1991 + Purpose: Base object + + Copyright (c) 1991 - 1997 Gary Liddon + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include +#include +#include +#include + +/* Local + ----- */ +#include "gobject.hpp" + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Static Vars + ----------- */ +int GObject::NumOfErrors=0; +int GObject::MaxErrors=3; +int GObject::NumOfGobjs=0; +int GObject::NumOfWarnings=0; + +unsigned int GObject::BigObjFlags=0; + +/*---------------------------------------------------------------------- + Data + ---- */ +char const * const GObject::ErrorText[]= +{ + "Too Many Errors", + "Out of Memory", +}; + +/*---------------------------------------------------------------------- + Function: Gobject Constructor + ---------------------------------------------------------------------- */ +GObject::GObject() +{ + NumOfGobjs++; +} + +/*---------------------------------------------------------------------- + Function: Gobject Destructor + ---------------------------------------------------------------------- */ +GObject::~GObject() +{ + NumOfGobjs--; +} + + +/*---------------------------------------------------------------------- + Function: Flag a Gobject error + ---------------------------------------------------------------------- */ +void GObject::Error(int ErrNum) +{ + cerr<<"Internal GObject Error\n"; + Error(ERR_FATAL,ErrorText[ErrNum]); +} + +/*---------------------------------------------------------------------- + Function: Flag General purpose error + ---------------------------------------------------------------------- */ +void GObject::Error(int Etype,char const *e, ...) +{ + if (e) + { + switch (Etype) + { + case ERR_FATAL: + cerr<<"Fatal Error: "; + break; + case ERR_SERIOUS: + cerr<<"Serious Error: "; + break; + case ERR_WARNING: + NumOfWarnings++; + cerr<<"Warning: "; + break; + } + + va_list argptr; + va_start(argptr,e); + vprintf(e,argptr); + va_end(argptr); + } + + switch (Etype) + { + case ERR_FATAL: + exit(10); + break; + case ERR_SERIOUS: + if (NumOfErrors==MaxErrors) + Error(ERM_TOOMANYERRORS); + else + NumOfErrors++; + } + +} + + + +/*=========================================================================== + end */ + + + + diff --git a/Utils/Libs/GLib/Gobject.hpp b/Utils/Libs/GLib/Gobject.hpp new file mode 100644 index 000000000..eb7c0b442 --- /dev/null +++ b/Utils/Libs/GLib/Gobject.hpp @@ -0,0 +1,86 @@ +/*========================================================================= + + GOBJECT.HPP + + Author: Gary Liddon @ Watford + Created: 4th May 1991 + Purpose: Base object + + Copyright (c) 1991 - 1997 Gary Liddon + +===========================================================================*/ + +#ifndef __PC_GLIB_GOBJECT_HPP__ +#define __PC_GLIB_GOBJECT_HPP__ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include + +/* Glib + ---- */ +#include "gtypes.h" + +/* Local + ----- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ +enum + { + ERR_FATAL, + ERR_WARNING, + ERR_SERIOUS, + }; + +enum + { + ERM_TOOMANYERRORS, + }; + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +#define ERM_OUTOFMEM ERR_FATAL,"Mem flood line %d,file %s,",__LINE__,__FILE__ + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +class GLIB_API GObject +{ +public: + GObject(); + ~GObject(); + + + static void Error(int Etype,char const *e, ...); + static void Error(int ErrNum); +// static int GetNumOfGobjs(void){return NumOfGobjs;} +// static int GetNumOfWarnings(void){return NumOfWarnings;} + +protected: + +private: + + static int NumOfWarnings; + static int NumOfErrors; + static int MaxErrors; + static char const * const ErrorText[]; + static int NumOfGobjs; + static unsigned int BigObjFlags; +}; + + +/*---------------------------------------------------------------------- */ + +#endif /* __PC_GLIB_GOBJECT_HPP__ */ + +/*=========================================================================== + end */ + + diff --git a/Utils/Libs/GLib/Gstring.cpp b/Utils/Libs/GLib/Gstring.cpp new file mode 100644 index 000000000..429cdfeca --- /dev/null +++ b/Utils/Libs/GLib/Gstring.cpp @@ -0,0 +1,338 @@ +/*========================================================================= + + GSTRING.CPP + + Author: Gary Liddon @ Farehame + Created: 4th April 1997 + Purpose: Generic string class + + Copyright (c) 1997 Gary Liddon + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include "string.h" + +/* Glib + ---- */ + +/* Local + ----- */ +#include "gstring.hpp" + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ +using namespace std; + +/*---------------------------------------------------------------------- + Vars + ---- */ + +/*---------------------------------------------------------------------- + Function: GString::GString() + Notes: Blank constructor + ---------------------------------------------------------------------- */ +GString::GString() +{ + Init(); +} + +/*---------------------------------------------------------------------- + Function: GString::~GString() + Notes: Destructor + ---------------------------------------------------------------------- */ +GString::~GString() +{ + Dump(); +} + +/*---------------------------------------------------------------------- + Function: GString::GString(GString const & Gs) + Notes: Copy constructor + ---------------------------------------------------------------------- */ +GString::GString(GString const & Gs) +{ + Init(); + (*this)=Gs; +} + + +/*---------------------------------------------------------------------- + Function: GString & GString::operator+(GString const & Gs) + Notes: Add one string to another + ---------------------------------------------------------------------- */ +GString GString::operator+(GString const & Gs) +{ + GString RetStr; + + if (Gs.Len()) + { + if (Len()) + { + char * NewStr; + if (!(NewStr=new char[Gs.Len()+Len()+1])) + Error(ERM_OUTOFMEM); + strcpy(NewStr,*this); + strcat(NewStr,Gs); + RetStr=NewStr; + + delete NewStr; + } + else + RetStr=Gs; + } + else + RetStr=*this; + + return(RetStr); +} + +/*---------------------------------------------------------------------- + Function: + Notes: + ---------------------------------------------------------------------- */ +GString & GString::operator+=(GString const & Str) +{ + *this=operator+(Str); + return(*this); +} + + +/*---------------------------------------------------------------------- + Function: int GString::Len(void) + Notes: Get the length + ---------------------------------------------------------------------- */ +int GString::Len(void) const +{ + if (Text) + return(strlen(Text)); + else + return(0); +} + + +/*---------------------------------------------------------------------- + ---------------------------------------------------------------------- */ +bool GString::operator<(GString const & Str) const +{ + char defaultString[]=""; + + char const * stringOne; + char const * stringTwo; + + if (Empty()) + stringOne=defaultString; + else + stringOne=*this; + + if (Str,Empty()) + stringTwo=defaultString; + else + stringTwo=Str; + + return(strcmp(stringOne,stringTwo) < 0 ); +} + +/*---------------------------------------------------------------------- + Function: void GString::AssignStr(char const *NewStr) + Notes: Set this objs string + ---------------------------------------------------------------------- */ +void GString::AssignStr(char const *NewStr) +{ + /* Chuck string we're copying from into a temp + str in case the text being copied from + is ours + */ + char * ReplaceStr=NULL; + + if (NewStr) + { + if (!(ReplaceStr=new char[strlen(NewStr)+1])) + Error(ERM_OUTOFMEM); + + strcpy(ReplaceStr,NewStr); + } + + Dump(); + + Text=ReplaceStr; +} + +/*---------------------------------------------------------------------- + Function: void GString::Init(void) + Notes: Init this str + ---------------------------------------------------------------------- */ +void GString::Init(void) +{ + Text=NULL; +} + + +/*---------------------------------------------------------------------- + Function: void GString::Init(void) + Notes: Init this str + ---------------------------------------------------------------------- */ +void GString::Filter(char const * CharsToFilterOut,char ReplacementChar) +{ + if (Text) + { + for (unsigned int f=0;f= strlen(SearchString)) + { + GString NewString; + int RepLen; + bool LastMatch; + + RepLen=strlen(ReplaceString); + + for (unsigned int Checks=0;Checks <= strlen(Text)-strlen(SearchString);Checks++) + { + bool Failed; + + Failed=false; + + for (unsigned int f=0;f 0) + NewString+=GString(&Text[Checks]); + + *this=NewString; + } +} + + +/*---------------------------------------------------------------------- + Function: void GString::Dump(void) + Notes: Dump all the data + ---------------------------------------------------------------------- */ +void GString::Dump(void) +{ + if (Text) + delete Text; + + Init(); +} + +/*---------------------------------------------------------------------- + Function: void GString::Dump(void) + Notes: Dump all the data + ---------------------------------------------------------------------- */ +void GString::Lower(void) +{ + if (!Empty()) + strlwr(Text); +} + +/*---------------------------------------------------------------------- + Function: void GString::Dump(void) + Notes: Dump all the data + ---------------------------------------------------------------------- */ +void GString::Upper(void) +{ + if (!Empty()) + strupr(Text); +} + +/*---------------------------------------------------------------------- + Function: void GString::Dump(void) + Notes: Dump all the data + ---------------------------------------------------------------------- */ +bool GString::operator==(GString const & Gs) const +{ + if (Empty() && Gs.Empty()) + return(true); + + if (Empty() || Gs.Empty()) + return(false); + + return(strcmp(Gs,Text)==0); +} + + +/*---------------------------------------------------------------------- + Function: void GString::Dump(void) + Notes: Dump all the data + ---------------------------------------------------------------------- */ +ostream & operator<<(ostream & Out,GString const & G) +{ + if (!G.Empty()) + Out< +#include + +/* Stl + --- */ + +/* Glib + ---- */ +#include "gtypes.h" +#include "gobject.hpp" + +/* Local + ----- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +class GLIB_API GString : public GObject +{ +public: + GString(); + ~GString(); + GString(GString const &); + + GString(const char * Txt) + { + Init(); + *this=Txt; + } + + GString(const char * Txt,int StrLength) + { + Init(); + Text=new char[StrLength+1]; + + memcpy(Text,Txt,StrLength); + Text[StrLength]=0; + } + + bool operator==(GString const & Gs) const; + bool operator==(char const * Txt) const + { + GString MyStr; + MyStr=Txt; + return(MyStr == *this); + } + + GString & operator=(GString const & Gs) + { + /* Check for self assignment */ + + if (this!=&Gs) + AssignStr(Gs); + + return *this; + } + + GString & operator=(char const * NewStr) + { + AssignStr(NewStr); + return *this; + } + + + GString operator+(GString const &); + GString & operator+=(GString const &); + + bool operator<(GString const & Str) const; + + friend std::ostream & operator<<(std::ostream & Out,GString const & G); + + int Len(void) const; + + operator char const *() const {return(Text);} + bool Empty() const {return(Len()==0);} + + void Lower(void); + void Upper(void); + + void Filter(char const * CharsToFilterOut,char ReplacementChar='_'); + + void Append(char c); + void Replace(char const * SearchString,char const * ReplaceString); + +protected: + void Init(void); + void Dump(void); + void AssignStr(char const *NewStr); + char * Text; +}; + + + +/*---------------------------------------------------------------------- */ + +#endif /* __GSTRING_HPP__ */ + +/*=========================================================================== + end */ diff --git a/Utils/Libs/GLib/IFF.CPP b/Utils/Libs/GLib/IFF.CPP new file mode 100644 index 000000000..cdd9e193f --- /dev/null +++ b/Utils/Libs/GLib/IFF.CPP @@ -0,0 +1,131 @@ +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ + ³ FILE: IFF.CPP ³ + ³ ³ + ³ Written by Carl Muller 25 Sep 1990 ³ + ³ Modified by Carl Muller 13 Sep 1991 ³ + ³ ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ + + + +#include "iff.hpp" + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Start writing an IFF file ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +FILE *IFF_FILE::write(char const *filename) +{ + stackptr = 0; + file = fopen(filename, "wb"); + return file; +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Close an IFF file ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +void IFF_FILE::close() +{ + fclose(file); +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Start an IFF form ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +void IFF_FILE::form(char name[5]) +{ + fputc('F', file); + fputc('O', file); + fputc('R', file); + fputc('M', file); + push(ftell(file)); + fputc(0, file); + fputc(0, file); + fputc(0, file); + fputc(0, file); + fputc(name[0], file); + fputc(name[1], file); + fputc(name[2], file); + fputc(name[3], file); +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Finish an IFF form ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +void IFF_FILE::endform() +{ + long pos1, pos2, len; + + pos2 = ftell(file); + if (pos2 & 1) + { + fputc(0, file); + pos2++; + } + + pos1 = pop(); + len = pos2 - pos1 - 4L; + + fseek(file, pos1, SEEK_SET); + fputc((UCHAR)(len>>24), file); + fputc((UCHAR)(len>>16) & 255, file); + fputc((UCHAR)(len>>8) & 255, file); + fputc((UCHAR)(len&0xff), file); + + fseek(file,pos2,SEEK_SET); +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Start an IFF chunk ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +void IFF_FILE::chunk(char name[5]) +{ + fputc(name[0], file); + fputc(name[1], file); + fputc(name[2], file); + fputc(name[3], file); + push(ftell(file)); + fputc(0, file); + fputc(0, file); + fputc(0, file); + fputc(0, file); +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Finish an IFF chunk ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +void IFF_FILE::endchunk() +{ + long pos1, pos2, len; + + pos2 = ftell(file); + if (pos2 & 1) + { + fputc(0, file); + pos2++; + } + pos1 = pop(); + len = pos2 - pos1 - 4L; + fseek(file, pos1, SEEK_SET); + fputc((UCHAR)(len>>24), file); + fputc((UCHAR)(len>>16) & 255, file); + fputc((UCHAR)(len>>8) & 255, file); + fputc((UCHAR)(len&255), file); + fseek(file, pos2, SEEK_SET); +} + +long IFF_FILE::tell() +{ + return ftell(file); +} diff --git a/Utils/Libs/GLib/IFF.HPP b/Utils/Libs/GLib/IFF.HPP new file mode 100644 index 000000000..86633cffd --- /dev/null +++ b/Utils/Libs/GLib/IFF.HPP @@ -0,0 +1,122 @@ +/*========================================================================= + + IFF.HPP + + Author: Carl Muller + Created: 25 Sep 1990 + Purpose: IFF write stuff, a bit manky + + Copyright (c) 1990 - 1997 Carl Muller + +===========================================================================*/ + +#ifndef __CP_GLIB_IFF_HPP__ +#define __CP_GLIB_IFF_HPP__ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include +#include + + +/* Glib + ---- */ +#include "gtypes.h" + + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ +const MAXIFFNESTING = 16; + +enum Masking +{ + mskNone = 0, + mskHasMask = 1, + mskHasTransparentColor = 2, + mskLasso = 3 +}; + +enum Compression +{ + cmpNone = 0, + cmpByteRun1 = 1 +}; + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +class GLIB_API IFF_FILE +{ + private : + FILE * file; + int stackptr; + long stack[MAXIFFNESTING]; + + public : + FILE * write(char const *filename); + void close(void); + + long tell(void); + void form(char name[5]); + void endform(void); + void chunk(char name[5]); + void endchunk(void); + + void writeword(U16 it) { fputc(it >> 8, file); fputc(it & 255, file); } + void writelong(ULONG it) { fputc((UCHAR)((it >> 24)&0xff), file);fputc((UCHAR)((it >> 16)&0xff), file);fputc((UCHAR)((it >> 8)&0xff), file); fputc((UCHAR)(it & 0xff), file); } + U16 readword(void) { U16 ch=fgetc(file); return (ch<<8)+(U16)fgetc(file); } + void writechar(U16 it) { fputc(it, file); } + U16 readchar(void) { return fgetc(file); } + + void push(long it) { stack[stackptr++] = it; } + long pop(void) { return stack[--stackptr]; } +}; + + + +/*---------------------------------------------------------------------- + Inline Functions + ---------------- */ +inline U16 getword(FILE *file) +{ + U16 ch = fgetc(file); + return (ch << 8) + (U16) (fgetc(file)); +} + +inline void putword(U16 theword, FILE *file) +{ + fputc(theword >> 8, file); + fputc(theword & 255, file); +} + +inline long getlong(FILE *file) +{ + long ch; + ch = ((long) fgetc(file)) << 24; + ch |= ((long) fgetc(file)) << 16; + ch |= ((long) fgetc(file)) << 8; + ch |= ((long) fgetc(file)); + return ch; +} + +inline void get4chars(char *name, FILE *file) +{ + name[0] = fgetc(file); + name[1] = fgetc(file); + name[2] = fgetc(file); + name[3] = fgetc(file); + name[4] = 0; +} + + +/*---------------------------------------------------------------------- */ + +#endif /* __CP_GLIB_IFF_HPP__ */ + +/*=========================================================================== + end */ diff --git a/Utils/Libs/GLib/Ilbm.cpp b/Utils/Libs/GLib/Ilbm.cpp new file mode 100644 index 000000000..9aca0a5ae --- /dev/null +++ b/Utils/Libs/GLib/Ilbm.cpp @@ -0,0 +1,446 @@ +/*========================================================================= + + ILBM.HPP + + Author: Gary Liddon @ Watford + Created: 30/03/92 + Purpose: Crappy ilbm reader + + Copyright (c) 1992 - 1997 Gary Liddon + +===========================================================================*/ + +/* ----------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include + +/* Glib + ---- */ +#include "gutils.h" + +/* Local + ----- */ +#include "ilbm.hpp" +#include "iff.hpp" + +/* Graphics + -------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +nilbm::nilbm(char const *name) : niff(name,ILBM) +{ + if (err_no==NOT_FORM) + { + if (open(name,PBM)) + { + err_no=NO_ERROR; + } + } + + if (!err_no) + starta(); +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +nilbm::nilbm(FILE *fp) : niff(fp,ILBM) +{ + if (err_no==PASSED_ERR) + if (mount_form(PBM)) + err_no=NO_ERROR; + + starta(); +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +nilbm::~nilbm() +{ + DiscardCmap(); + DiscardBmap(); +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void nilbm::starta() +{ + cmap=NULL; + Bmap=NULL; + + if (err_no) + return; + + if ((!file_opened) && (!form_mounted)) + return; + + GetBmHeadFromDisk(); + + + if (goto_hunk(CMAP)) + cmap=get_hunk(); + + Bmap=GetBmapFromDisk(); +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +U8 *nilbm::TakeBmap() +{ + U8* Retp=Bmap; + Bmap=NULL; + return Retp; +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +U8 *nilbm::TakeCmap() +{ + U8* Retp=cmap; + cmap=NULL; + return Retp; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void nilbm::DiscardBmap() +{ + if (Bmap) + delete Bmap; + + Bmap=NULL; +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void nilbm::DiscardCmap() +{ + if (cmap) + delete cmap; + + cmap=NULL; +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void nilbm::GetBmHeadFromDisk() +{ + if (goto_hunk(BMHD)) + { + GetIntelLong(); + GetIntelLong(); + + w=GetIntelWord(); + h=GetIntelWord(); + + GetIntelWord(); + GetIntelWord(); + + planes=fgetc(fp); + + fgetc(fp); + comp=fgetc(fp); + + rh = h; + + } +} + + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +U8 *nilbm::GetBmapFromDisk() +{ + U8 *buffa=NULL; + U8 *Ptr; + + if (file_opened) + { + if (goto_hunk(BODY)) + { + long temp; + + fread(&temp,1,sizeof(ULONG),fp); + fread(&temp,1,sizeof(ULONG),fp); + + if (!(buffa=new U8[w*h])) + Error(ERR_FATAL,"Allocating ILBM body (%ld)",(long)w*(long)h); + + U8 *line_buffer; + + int NormWidth=((w+7)&(0xfff8)); + + int bwidth=NormWidth/8; + + if (!(line_buffer=new U8[NormWidth*4])) //MA increased for safety incase of bad encoding + return NULL; + else + { + int z,dest; // Init source count + + for (int line=0;line<(h);line++) + { + dest=0; // Destination count + + memset(line_buffer,0,NormWidth); // Clear out buffer + + if (form_name==PBM) + { + if (comp) + { + dest=0; + + while (dest < w) + { + s8 val = fgetc(fp); + + if (val<0) + { + val=(0-val)+1; + U8 ch=fgetc(fp); + + while (val--) + { + line_buffer[dest]=ch; + dest++; + } + } + else if (val>=0) + { + val++; + + while (val--) + { + line_buffer[dest]=fgetc(fp); + dest++; + } + } + } + } + else + { + int WidthToRead = GU_AlignVal(w, 2); + fread(line_buffer,WidthToRead,1,fp); + } + } + else + { + for (int p=0;p=0;z--) + *Ptr++ |= ((ch>>(z))&1)<=0) + { + val++; + + while (val--) + { + U8 ch=fgetc(fp); + + for (z=7;z>=0;z--) + *Ptr++ |= ((ch>>(z))&1)<=0;z--) + *Ptr++ |= ((ch>>(z))&1)< +#include +#include +#include + +#include + +#include "misc.hpp" +#include "gfname.hpp" + +using namespace std; + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + + +bool copyFile(char const * Dest,char const * Source,bool Overwrite) +{ + ifstream InFile; + vector Data; + + int Size; + + Size=FileSize(Source); + + if (Size >= 0) + { + Data.resize(Size); + + ifstream In; + In.open(Source,ios::binary); + In.read((char *)(&Data[0]),Size); + In.close(); + + if (In) + { + ofstream OutFile; + int OpenMode; + + OpenMode=ios::binary; + + if (Overwrite) + OpenMode|=ios::trunc; + + OutFile.open(Dest,OpenMode); + + if (OutFile) + { + OutFile.write((char*)&Data[0],Data.size()); + OutFile.close(); + } + else + GObject::Error(ERR_FATAL,"Unable to open out file %s",Dest); + } + else + GObject::Error(ERR_FATAL,"Unable to open in file %s",Source); + } + + return(true); +} + +int FileCycler::DoCycle(char const *Spec,bool Recurse) +{ + int FileNum=0; + int Handle; + GFName InName(Spec); + char * Speccy; + + if(!(Speccy=strdup(Spec))) + GObject::Error(ERM_OUTOFMEM); + + _finddata_t FindBlock; + + /* Do dirs first */ + + if (Recurse) + { + GFName DirName(Spec); + + DirName.File("*"); + DirName.Ext("*"); + + if ((Handle = _findfirst((char *) DirName.FullName(),&FindBlock))!=-1) + { + BOOL Done; + + Done=FALSE; + + while (!Done) + { + if (FindBlock.attrib&_A_SUBDIR) + { + if (strcmp(FindBlock.name,".") && strcmp(FindBlock.name,"..")) + { + GFName TempName(InName.FullName()); + + if (TempName.Dir()) + { + char * NewSub; + + if (!(NewSub=new char [strlen(TempName.Dir()) +strlen (FindBlock.name)+1+2] )) + GObject::Error(ERM_OUTOFMEM); + + sprintf(NewSub,"%s\\%s",TempName.Dir(),FindBlock.name); + + TempName.Dir(NewSub); + + delete NewSub; + } + else + TempName.Dir(FindBlock.name); + + DoCycle(TempName.FullName(),Recurse); + } + } + Done=_findnext(Handle,&FindBlock); + } + } + } + + if ((Handle = _findfirst(Speccy,&FindBlock))!=-1) + { + BOOL Done; + Done=FALSE; + + while (!Done) + { + + if (!(FindBlock.attrib&_A_SUBDIR)) + { + GFName TempName(InName.FullName()); + GFName Temp2(FindBlock.name); + TempName.File(Temp2.File()); + TempName.Ext(Temp2.Ext()); + FileCallback(TempName.FullName(),FileNum++); + } + + Done=_findnext(Handle,&FindBlock); + } + } + + free(Speccy); + + return FileNum; +} + + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Cycle through files of a particular spec, Returns Number of Files found +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +int CycleFiles(char const *Spec,void (*Func)(char const *Fname,int Num),BOOL Recurse) +{ + int FileNum=0; + int Handle; + GFName InName(Spec); + char * Speccy; + + if(!(Speccy=strdup(Spec))) + GObject::Error(ERM_OUTOFMEM); + + _finddata_t FindBlock; + + /* Do dirs first */ + + if (Recurse) + { + GFName DirName(Spec); + + DirName.File("*"); + DirName.Ext("*"); + + if ((Handle = _findfirst((char *) DirName.FullName(),&FindBlock))!=-1) + { + BOOL Done; + + Done=FALSE; + + while (!Done) + { + if (FindBlock.attrib&_A_SUBDIR) + { + if (strcmp(FindBlock.name,".") && strcmp(FindBlock.name,"..")) + { + GFName TempName(InName.FullName()); + + if (TempName.Dir()) + { + char * NewSub; + + if (!(NewSub=new char [strlen(TempName.Dir()) +strlen (FindBlock.name)+1+2] )) + GObject::Error(ERM_OUTOFMEM); + + sprintf(NewSub,"%s\\%s",TempName.Dir(),FindBlock.name); + + TempName.Dir(NewSub); + + delete NewSub; + } + else + TempName.Dir(FindBlock.name); + + CycleFiles(TempName.FullName(),Func,Recurse); + } + } + Done=_findnext(Handle,&FindBlock); + } + } + } + + if ((Handle = _findfirst(Speccy,&FindBlock))!=-1) + { + BOOL Done; + Done=FALSE; + + while (!Done) + { + + if (!(FindBlock.attrib&_A_SUBDIR)) + { + GFName TempName(InName.FullName()); + GFName Temp2(FindBlock.name); + TempName.File(Temp2.File()); + TempName.Ext(Temp2.Ext()); + Func(TempName.FullName(),FileNum++); + } + + Done=_findnext(Handle,&FindBlock); + } + } + + free(Speccy); + + return FileNum; +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Get Switch Info +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +BOOL SwitchInfo(char *String) +{ + if (strlen(String)!=3 || (String[2] != '+' && String[2] != '-')) + { + GObject::Error(ERR_FATAL,"%s option formatted incorrectly",String); + return 0; + } + else + return String[2]=='+' ? 1 : 0; +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Command Line Constructor +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +CommandLine::CommandLine(int argc,char **argv,char *(*Func)(char *String,int Num)) +{ + LastItem=NULL; + InStream=NULL; + CommandItem=0; + + if (!(LastItem=new char[1000])) + Error(ERM_OUTOFMEM); + + MyArgc=argc; + MyArgv=argv; + + GetNextItem(); // Skip progname + + int x=0; + + char *SpamString; + + while ((SpamString=GetNextItem())) + { + Func(SpamString,x); + x++; + } +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Command Line Destructor +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +CommandLine::~CommandLine() +{ + if (InStream) + delete InStream; + + if (LastItem) + delete LastItem; +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Get The Next Item in the Commandline string +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +char *CommandLine::GetNextItem() +{ + char *RetStuff=NULL; + + if (!InStream) + RetStuff=GetNextCommandLineItem(); + else + RetStuff=GetNextScriptFileItem(); + + return RetStuff; +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Get The Next Item in the Argv list +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +char *CommandLine::GetNextCommandLineItem() +{ + char *RetStuff=NULL; + + if (CommandItem!=MyArgc) + { + strcpy(LastItem,MyArgv[CommandItem]); + RetStuff=LastItem; + + CommandItem++; + + if (*RetStuff=='@') + { + if (InStream) + delete InStream; + + if (!(InStream = new ifstream (&RetStuff[1]))) + Error(ERM_OUTOFMEM); + + if (!(*InStream)) + Error(ERR_FATAL,"Cannot open script file %s",&RetStuff[1]); + + if (!(RetStuff=GetNextScriptFileItem())) + RetStuff=GetNextCommandLineItem(); + } + } + + return RetStuff; +} + + + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Get The Next Item from the currently open script file +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +char *CommandLine::GetNextScriptFileItem() +{ + char *RetStuff=NULL; + + (*InStream)>>LastItem; + + if (InStream->eof()) + { + delete InStream; + InStream=NULL; + RetStuff=GetNextCommandLineItem(); + } + else + RetStuff=LastItem; + + return RetStuff; +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Does this file exist? +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +bool FileExists(const char *String) +{ + ifstream InStream; + bool RetVal; + + InStream.open(String,ios::in); + if (InStream) + RetVal=true; + else + RetVal=false; + + InStream.close(); + return(RetVal); +} + + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// What is the size of this file? +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +int FileSize(const char *String) +{ + ifstream InStream; + int RetVal; + + RetVal=-1; + + InStream.open(String,ios::in); + + if (InStream) + { + InStream.seekg(0,ios::end); + RetVal=InStream.tellg(); + InStream.close(); + } + + return(RetVal); +} + + + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +//ends diff --git a/Utils/Libs/GLib/MISC.HPP b/Utils/Libs/GLib/MISC.HPP new file mode 100644 index 000000000..5fefd14dc --- /dev/null +++ b/Utils/Libs/GLib/MISC.HPP @@ -0,0 +1,297 @@ +/*========================================================================= + + MISC.HPP + + Author: Gary Liddon @ Watford + Created: 4th May 1991 + Purpose: Shitey misc stuff + + Copyright (c) 1991 - 1997 Gary Liddon + +===========================================================================*/ + +#ifndef __PC_GLIB_MISC_HPP__ +#define __PC_GLIB_MISC_HPP__ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include +#include + +/* Glib + ---- */ +#include "gtypes.h" + +/* Local + ----- */ +#include "gobject.hpp" +#include "gstring.hpp" +#include "gutils.h" + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +class GLIB_API CommandLine : public GObject +{ +public: + CommandLine(int argc,char **argv,char *(*Func)(char *String,int Num)); + ~CommandLine(); + char *GetNextItem(); + +protected: + std::ifstream *InStream; + char *InStreamFile; + + char *GetNextScriptFileItem(); + char *GetNextCommandLineItem(); + +private: + int CommandItem; + int MyArgc; + char **MyArgv; + char *LastItem; +}; + +class GLIB_API Gofstream : public std::ofstream +{ +public: + enum ENDIAN + { + BIG_ENDIAN, + LITTLE_ENDIAN, + }; + + Gofstream(ENDIAN NewEndian=BIG_ENDIAN) + {SetEndian(NewEndian);} + + void SetEndian(ENDIAN NewEndian) + {Endian=NewEndian;} + + void Put16(u16 PVal) + { + switch(Endian) + { + case BIG_ENDIAN: + put(u8(PVal>>8)); + put(u8(PVal&0xff)); + break; + + case LITTLE_ENDIAN: + put(u8(PVal&0xff)); + put(u8(PVal>>8)); + break; + } + } + + void Put32(u32 PVal) + { + switch(Endian) + { + case BIG_ENDIAN: + put(u8(PVal>>24)); + put(u8(PVal>>16)); + put(u8(PVal>>8)); + put(u8(PVal>>0)); + break; + + case LITTLE_ENDIAN: + put(u8(PVal>>0)); + put(u8(PVal>>8)); + put(u8(PVal>>16)); + put(u8(PVal>>24)); + break; + } + } + + int Align(int Alignment,char const * Pad=NULL) + { + int NewPos; + + int CurrPos; + + CurrPos=tellp(); + + if (Alignment && CurrPos) + { + NewPos=CurrPos+((CurrPos%Alignment) == 0 ? 0 : Alignment-(CurrPos%Alignment)); + + if (NewPos != CurrPos) + { + if (Pad) + { + int StrSize; + + StrSize=strlen(Pad); + + for (;CurrPos & NewList) : List(NewList) + { + DoCycle(FileSpec,Recurse); + } + +protected: + + virtual void FileCallback(char const * FName,int FileNum) + { + List.push_front(FName); + } + + std::list & List; +}; + + +/*---------------------------------------------------------------------- + Globals + ------- */ + +/* Vars + ---- */ + +/* Data + ---- */ + +/* Functions + --------- */ +GLIB_API int CycleFiles(char const *Spec,void (*Func)(char const *Fname,int Num),BOOL Recurse); +GLIB_API BOOL SwitchInfo(char *String); +GLIB_API bool FileExists(const char *String); +GLIB_API int FileSize(const char *String); + +GLIB_API bool copyFile(char const * Dest,char const * Source,bool Overwrite = false); + +/*---------------------------------------------------------------------- */ + +#endif /* __PC_GLIB_MISC_HPP__ */ + +/*=========================================================================== + end */ diff --git a/Utils/Libs/GLib/Mtypes.h b/Utils/Libs/GLib/Mtypes.h new file mode 100644 index 000000000..f8a6ffaf8 --- /dev/null +++ b/Utils/Libs/GLib/Mtypes.h @@ -0,0 +1,49 @@ +/* Ä-------------------------------------------------------------------------- + File: MTYPES.H + + Notes: Machine dependant typedefs for msvc + + Target: PC MSVC + + Author: G Robert Liddon @ Cliffs + Created: 18th February 1996 + + Copyright (C) 1997 Gary Liddon + + All rights reserved. + --------------------------------------------------------------------------- */ + +#ifndef __MTYPES_H__ +#define __MTYPES_H__ + +/* --------------------------------------------------------------------------- + Includes + -------- */ + +/* --------------------------------------------------------------------------- + Defines + ------- */ +#define __GLIB_No64__ +#define __GLIB_BOOLAsInt__ + + +#ifdef __GL_WIN32DLL__ +#define GLIB_API __declspec(dllexport) +#endif + +/* --------------------------------------------------------------------------- + Typedefs + -------- */ +typedef signed char s8; +typedef signed short s16; +typedef signed long int s32; +typedef double long s64; + +typedef unsigned char u8; +typedef unsigned short int u16; +typedef unsigned long int u32; + +/* --------------------------------------------------------------------------- */ +#endif /* __MTYPES_H__ */ +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/NIFF.HPP b/Utils/Libs/GLib/NIFF.HPP new file mode 100644 index 000000000..01277e2a2 --- /dev/null +++ b/Utils/Libs/GLib/NIFF.HPP @@ -0,0 +1,128 @@ +/*========================================================================= + + NIFF.HPP + + Author: Gary Liddon @ Watford + Created: 27th May 1991 + Purpose: Base (and i do mean base) iff class + + Copyright (c) 1991 - 1997 Gary Liddon + +===========================================================================*/ + +#ifndef __PC_GLIB_NIFF_HPP__ +#define __PC_GLIB_NIFF_HPP__ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include + +/* Glib + ---- */ +#include "gtypes.h" + +/* Local + ----- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ +#define NIFF_SEEK_SET SEEK_SET +#define NIFF_SEEK_CUR SEEK_CUR +#define NIFF_SEEK_END SEEK_END + +#define MAKE_ID(a,b,c,d) (U32) ((U32)(d)<<24|(U32)(c)<<16|(U32)(b)<<8|(U32)(a)) + +#define FORM MAKE_ID('F','O','R','M') +#define PROP MAKE_ID('P','R','O','P') +#define LIST MAKE_ID('L','I','S','T') +#define CAT MAKE_ID('C','A','T',' ') + +#define ILBM MAKE_ID('I','L','B','M') +#define PBM MAKE_ID('P','B','M',' ') +#define BMHD MAKE_ID('B','M','H','D') +#define BODY MAKE_ID('B','O','D','Y') +#define CMAP MAKE_ID('C','M','A','P') +//#define ANIM MAKE_ID('A','N','I','M') +#define DLTA MAKE_ID('D','L','T','A') + +enum errors +{ + NO_ERROR, + OPEN_ERROR, + NOT_IFF, + LOST_FORM, + LOST_HUNK, + CORRUPT_BMHD, + LOST_BODY, + NO_MEM, + MANGLED_IFF, + LOST_COLOUR, + NOT_PBM, + PASSED_ERR, + ANIM_PIC_E, + NOT_FORM, +}; + +/*---------------------------------------------------------------------- + Class defintions + ---------------- */ +class GLIB_API niff +{ + public: + niff(char const *name=NULL,U32 form=0L); + niff(FILE *fp,U32 form=0L); + ~niff(); + + int GetNumOfHunks(U32 FormName); + + BOOL open(char const *name,U32 form=0L); + BOOL close(); + + FILE *get_fp() {return fp;} + + char *error(); + + BOOL next_form(U32 form=0L); + BOOL next_hunk(U32 form=0L); + BOOL mount_form(U32 form=0L); + U8 *get_hunk(); + BOOL goto_hunk(U32 form); + void goto_form_start(); + + U32 rev_long(U32); + U16 rev_word(U16 val); + + U32 GetIntelLong(); + U16 GetIntelWord(); + + int seek(long offset,int orig); + long tell(); + + void Read(U8 *Buf,long bytes); + + protected: + U32 full_len(U32 val); + + BOOL form_mounted,passed_file,file_opened; + FILE *fp; + + int err_no; + + U32 form_name,hunk_name,splen; + long form_start,form_end,iff_base; +}; + + + +/*---------------------------------------------------------------------- */ + +#endif /* __PC_GLIB_NIFF_HPP__ */ + +/*=========================================================================== + end */ + diff --git a/Utils/Libs/GLib/Niff.cpp b/Utils/Libs/GLib/Niff.cpp new file mode 100644 index 000000000..a8e28eb4e --- /dev/null +++ b/Utils/Libs/GLib/Niff.cpp @@ -0,0 +1,452 @@ +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ + ³ IA - Deluxe Paint 3 Animation player ³ + ³ ³ + ³ NIFF.CPP ³ + ³ ³ + ³ Written by Gary Liddon 27 May 1991 ³ + ³ Modified by Carl Muller 30 Oct 1991 ³ + ³ ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ + + +#include "stdio.h" +#include "conio.h" + +#include "niff.hpp" + + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ IFF error messages ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ + +char *niff_error_text[] = +{ + NULL, + "Error opening IFF file", + "Not an IFF file", + "Can't find FORM", + "Can't find HUNK", + "Corrupted bit map header in picture file", + "Can't find BODY", + "All out of memory", + "Mangled IFF file", + "Can't find CMAP", + "No ILBM's yet", + "Error with passed IFF file", + "Error with ILBM in ANIM", + "Not aksed for FORM type", + +}; + + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Create an IFF descriptor from a filename ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +niff::niff(char const *name, U32 form) +{ + form_mounted = FALSE; + err_no = NO_ERROR; + + file_opened = FALSE; + fp = NULL; + passed_file = FALSE; + + if (name) + open(name,form); + +} + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Create an IFF descriptor from a file pointer ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +niff::niff(FILE * new_file, U32 form) +{ + form_mounted = FALSE; + err_no = NO_ERROR; + file_opened = TRUE; // File pre-opened + + fp = new_file; + iff_base = ftell(fp); // Get base pos + passed_file = TRUE; // This is a passed file + + if (!mount_form(form)) + err_no = PASSED_ERR; +} + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Open an IFF file for reading ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +BOOL niff::open(char const *name, U32 form) +{ + if (passed_file) // Passed files can't be re-opened + { + err_no = OPEN_ERROR; + return FALSE; + } + + if (file_opened) // Already open so don't bother + { + err_no = OPEN_ERROR; + return FALSE; + } + + if (!(fp = fopen(name,"rb"))) + { + err_no = OPEN_ERROR; + + return FALSE; + } + + if (!mount_form(form)) // Mount this form + { + fclose(fp); + if (!err_no) + err_no = NOT_FORM; + return FALSE; + } + + iff_base = 0L; // Set position to zero + file_opened = TRUE; + return TRUE; +} + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Read an IFF hunk into memory ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +U8 *niff::get_hunk() +{ + if ((!file_opened) && (!form_mounted)) + return NULL; + + U8 *hunk=NULL; + long name,len,temp; + + temp = ftell(fp); + + fread(&name, 1, sizeof(U32), fp); + fread(&len, 1, sizeof(U32), fp); + + len = rev_long(len); + + + hunk = new U8[len]; + + splen = len; + + if (hunk) + fread(hunk,1,len,fp); + + fseek(fp,temp,SEEK_SET); + + return hunk; +} + + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Reset to beginning of form ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +void niff::goto_form_start() +{ + fseek(fp, form_start, SEEK_SET); +} + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Mount form at filepos ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +BOOL niff::mount_form(U32 form) +{ + form_mounted=FALSE; + U32 temp,temp_name; // Check to see if we've an iff + + if (fread(&temp,sizeof(U32),1,fp) != 1) + { + err_no= MANGLED_IFF; + return(false); + } + + if (temp != FORM) // We haven't. + return FALSE; + + fread(&temp,1,sizeof(U32),fp); + form_end = full_len(temp)+ftell(fp); + + fread (&temp_name,1,sizeof(U32),fp); + + if (form) // Are we checking for a specific + { // form? + if (temp_name != form) + return FALSE; + } + + form_name=temp_name; + form_start=ftell(fp); + form_mounted=TRUE; + return TRUE; +} + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Goto a hunk ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +BOOL niff::goto_hunk(U32 form) +{ + U32 len, namer; + + if ((!form_mounted) || (!file_opened)) + return FALSE; + + goto_form_start(); + fread(&namer, 1, sizeof(U32), fp); + fread(&len, 1, sizeof(U32), fp); + + goto_form_start(); + + if (namer == form) + return TRUE; + + return next_hunk(form); +} + + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Get the number of hunks of a certain name in this file +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +int niff::GetNumOfHunks(U32 FormName) +{ + int NumOfHunks=0; + + if (goto_hunk(FormName)) + { + do + { + NumOfHunks++; + } + while (next_hunk(FormName)); + } + + return NumOfHunks; +} + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Goto next hunk in current form ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +BOOL niff::next_hunk(U32 form) +{ + if ((!form_mounted) || (!file_opened)) + return FALSE; + + U32 temp = ftell(fp); + long len; + U32 name = 0L; + BOOL found = FALSE; + + fread(&name, 1, sizeof(U32), fp); + fread(&len, 1, sizeof(U32), fp); + + while ((ftell(fp) < form_end) && (!found)) + { + fseek(fp, full_len(len), SEEK_CUR); + fread(&name, 1, sizeof(U32), fp); + fread(&len, 1, sizeof(U32), fp); + + if (form) + { + if (form == name) + found = TRUE; + } + else + { + if (form != FORM) + found = TRUE; + } + } + + if (found && (ftell(fp) < form_end)) + { + fseek(fp, -8L, SEEK_CUR); + return TRUE; + } + + fseek(fp, temp, SEEK_SET); + return FALSE; +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Goto Next form in current form ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +BOOL niff::next_form(U32 form) +{ + if ((!form_mounted) || (!file_opened)) + return FALSE; + + U32 len; + U32 name = 0L; + U32 fname = 0L; + BOOL found=FALSE; + + fread(&name, 1, sizeof(U32), fp); + fread(&len, 1, sizeof(U32), fp); + + do { + fseek(fp, full_len(len), SEEK_CUR); + + fread(&name, 1, sizeof(U32), fp); + fread(&len, 1, sizeof(U32), fp); + + if (name == FORM) + { + fread(&fname, 1, sizeof(U32), fp); + if (!form) + found = TRUE; + else if (form == fname) + found = TRUE; + len -= 4L; + } + } while (!found && (ftell(fp) < form_end)); + + if (found) // If we got what we wanted + { // then seek back to start of + fseek(fp, -12L, SEEK_CUR); // header and flag success + return TRUE; + } + + goto_form_start(); // Else back to start of form + return FALSE; +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Close an IFF file ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +BOOL niff::close() +{ + if (passed_file || (!file_opened)) // Passed or unopened files + return FALSE; // can't be closed + else if (fclose(fp) == 0) // If close succesful + { + file_opened=FALSE; // Flag file as closed + return TRUE; // Return Success + } + else + return FALSE; // Else an error +} + + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Do a Iff file seek +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +int niff::seek(long offset,int orig) +{ + return fseek(fp,offset,orig); +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Do a Iff file seek +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +long niff::tell() +{ + return ftell(fp); +} +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Read Data from current pos +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +void niff::Read(U8 *Buf,long bytes) +{ + fread(Buf,bytes,1,fp); +} + + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Return an Intel Word from current file pos +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +U16 niff::GetIntelWord() +{ + U16 Word=0; + + if (file_opened) + { + fread((U8 *) &Word,sizeof(U16),1,fp); + return rev_word(Word); + } + else + return 0; + +} + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Return an Intel U32 from current file pos +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +U32 niff::GetIntelLong() +{ + U32 Long=0; + + if (file_opened) + { + fread((U8 *) &Long,sizeof(U32),1,fp); + return rev_long(Long); + } + else + return 0; +} + + +//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +// Return the error string +// ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +char *niff::error() +{ + return niff_error_text[err_no]; +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Destroy an IFF descriptor ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +niff::~niff() +{ + close(); +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Return full chunk length ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +U32 niff::full_len(U32 val) +{ + return (rev_long(val)+1)&0xfffffffeL; +} + + + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Reverse a long: Motorola <-> Intel ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +U32 niff::rev_long(U32 val) +{ + return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | + ((val << 24) & 0xff000000) | ((val << 8) & 0x00ff0000); +} + +/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Reverse a long: Motorola <-> Intel ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ */ +U16 niff::rev_word(U16 val) +{ + return ((val >> 8) & 0x000000ff) | ((val << 8) & 0x0000ff00); +} diff --git a/Utils/Libs/GLib/Pal.cpp b/Utils/Libs/GLib/Pal.cpp new file mode 100644 index 000000000..d75eb761f --- /dev/null +++ b/Utils/Libs/GLib/Pal.cpp @@ -0,0 +1,293 @@ +/*========================================================================= + + PAL.CPP + + Author: Gary Liddon @ Fareham + Created: + Project: + Purpose: + + Copyright (c) 1997 G R Liddon + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ +#include +#include +#include + +/* STL + --- */ + +/* Glib + ---- */ +#include "ilbm.hpp" + +/* Local + ----- */ +#include "pal.hpp" + + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Palette Class Stuff + ---------------------------------------------------------------------- */ +Palette::Palette(void) +{ +// TheColours.reserve(300); +} + + +Palette::Palette(Palette const &Fr) +{ + CopyPal(Fr); +} + +Palette::~Palette(void) +{ +} + +void Palette::operator=(Palette const &Fr) +{ + CopyPal(Fr); +} + +void Palette::CopyPal(Palette const &Fr) +{ + TheColours.resize(Fr.TheColours.size()); + + for (int f=0;f=TheColours.capacity()) + { + TheColours.reserve(TheColours.capacity()+300); + } + + + if (f>=TheColours.size()) + TheColours.resize(f+1); + + return(TheColours[f]); +} + +Colour const & Palette::operator[](int f) const +{ + if (f>=TheColours.size()) + return(DummyCol); + else + return(TheColours[f]); +} + +u8 * Palette::MakeDpPal() const +{ + u8 * Pals; + + if (!(Pals=new u8[256*3])) + Error(ERM_OUTOFMEM); + + memset(Pals,0,256*3); + + for (int f=0;f + +/* Glib + ---- */ +#include "gobject.hpp" +#include "gtypes.h" + +/* Local + ----- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ +class GLIB_API Colour : public GObject +{ +public: + Colour(int nR=0,int nG=0,int nB=0); + Colour(Colour const & Col) {CopyCol(Col);} + + void operator=(Colour const &Col) {CopyCol(Col);} + + int GetR(void) const {return (R);} + int GetG(void) const {return (G);} + int GetB(void) const {return (B);} + + void SetR(int n){R=n;} + void SetG(int n){G=n;} + void SetB(int n){B=n;} + + void SetRGB(int nr,int ng,int nb) + { + SetR(nr); + SetG(ng); + SetB(nb); + } + + float Distance(Colour const &Col) const; + int DistanceUnroot(Colour const &Col) const; + + + bool operator==(Colour const &Col) const; + bool operator<(Colour const &Col) const; + + bool operator!=(Colour const &Col) const; + +protected: + void CopyCol(Colour const &); + int R; + int G; + int B; +}; + + +class GLIB_API Palette : public GObject +{ +public: + Palette(void); + Palette(Palette const &); + ~Palette(void); + + void operator=(Palette const &Fr); + bool operator==(Palette const &Fr) const; + bool operator<(Palette const &Fr) const; + int GetNumOfCols(void) const {return TheColours.size();} + + int * MakeRemapTable(Palette const & P) const; + + Colour const & operator[](int) const; + Colour & operator[](int); + + u8 * MakeDpPal(void) const; + + bool FromLbm(char const * Name); + void SetPalSize(int NumOfCols); + bool IsIntersecting(Palette const & ComPal) const; + +protected: + void CopyPal(Palette const &Fr); + + typedef std::vector ColVec; + typedef ColVec::iterator ColVecIt; + + + ColVec TheColours; + Colour DummyCol; +}; + + +/*---------------------------------------------------------------------- */ + +#endif /* __PAL_HPP__ */ + +/*=========================================================================== + end */ + + diff --git a/Utils/Libs/GLib/REGEX.C b/Utils/Libs/GLib/REGEX.C new file mode 100644 index 000000000..b22323af9 --- /dev/null +++ b/Utils/Libs/GLib/REGEX.C @@ -0,0 +1,4952 @@ +/* Extended regular expression matching and search library, + version 0.12. + (Implements POSIX draft P10003.2/D11.2, except for + internationalization features.) + + Copyright (C) 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* AIX requires this to be the first thing in the file. */ +#define __STDC__ 1 +#define STDC_HEADERS 1 +#define REGEX_MALLOC + +#if defined (_AIX) && !defined (REGEX_MALLOC) + #pragma alloca +#endif + +#define _GNU_SOURCE + +/* We need this for `regex.h', and perhaps for the Emacs include files. */ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* The `emacs' switch turns on certain matching commands + that make sense only in Emacs. */ +#ifdef emacs + +#include "lisp.h" +#include "buffer.h" +#include "syntax.h" + +/* Emacs uses `NULL' as a predicate. */ +#undef NULL + +#else /* not emacs */ + +/* We used to test for `BSTRING' here, but only GCC and Emacs define + `BSTRING', as far as I know, and neither of them use this code. */ +#if HAVE_STRING_H || STDC_HEADERS +#include +#ifndef bcmp +#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n)) +#endif +#ifndef bcopy +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif +#ifndef bzero +#define bzero(s, n) memset ((s), 0, (n)) +#endif +#else +#include +#endif + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +char *realloc (); +#endif + + +/* Define the syntax stuff for \<, \>, etc. */ + +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +#ifndef Sword +#define Sword 1 +#endif + +#ifdef SYNTAX_TABLE + +extern char *re_syntax_table; + +#else /* not SYNTAX_TABLE */ + +/* How many characters in the character set. */ +#define CHAR_SET_SIZE 256 + +static char re_syntax_table[CHAR_SET_SIZE]; + +static void +init_syntax_once () +{ + register int c; + static int done = 0; + + if (done) + return; + + bzero (re_syntax_table, sizeof re_syntax_table); + + for (c = 'a'; c <= 'z'; c++) + re_syntax_table[c] = Sword; + + for (c = 'A'; c <= 'Z'; c++) + re_syntax_table[c] = Sword; + + for (c = '0'; c <= '9'; c++) + re_syntax_table[c] = Sword; + + re_syntax_table['_'] = Sword; + + done = 1; +} + +#endif /* not SYNTAX_TABLE */ + +#define SYNTAX(c) re_syntax_table[c] + +#endif /* not emacs */ + +/* Get the interface, including the syntax bits. */ +#include "regex.h" + +/* isalpha etc. are used for the character classes. */ +#include + +#ifndef isascii +#define isascii(c) 1 +#endif + +#ifdef isblank +#define ISBLANK(c) (isascii (c) && isblank (c)) +#else +#define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif +#ifdef isgraph +#define ISGRAPH(c) (isascii (c) && isgraph (c)) +#else +#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c)) +#endif + +#define ISPRINT(c) (isascii (c) && isprint (c)) +#define ISDIGIT(c) (isascii (c) && isdigit (c)) +#define ISALNUM(c) (isascii (c) && isalnum (c)) +#define ISALPHA(c) (isascii (c) && isalpha (c)) +#define ISCNTRL(c) (isascii (c) && iscntrl (c)) +#define ISLOWER(c) (isascii (c) && islower (c)) +#define ISPUNCT(c) (isascii (c) && ispunct (c)) +#define ISSPACE(c) (isascii (c) && isspace (c)) +#define ISUPPER(c) (isascii (c) && isupper (c)) +#define ISXDIGIT(c) (isascii (c) && isxdigit (c)) + +#ifndef NULL +#define NULL 0 +#endif + +/* We remove any previous definition of `SIGN_EXTEND_CHAR', + since ours (we hope) works properly with all combinations of + machines, compilers, `char' and `unsigned char' argument types. + (Per Bothner suggested the basic approach.) */ +#undef SIGN_EXTEND_CHAR +#if __STDC__ +#define SIGN_EXTEND_CHAR(c) ((signed char) (c)) +#else /* not __STDC__ */ +/* As in Harbison and Steele. */ +#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128) +#endif + +/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we + use `alloca' instead of `malloc'. This is because using malloc in + re_search* or re_match* could cause memory leaks when C-g is used in + Emacs; also, malloc is slower and causes storage fragmentation. On + the other hand, malloc is more portable, and easier to debug. + + Because we sometimes use alloca, some routines have to be macros, + not functions -- `alloca'-allocated space disappears at the end of the + function it is called in. */ + +#ifdef REGEX_MALLOC + +#define REGEX_ALLOCATE malloc +#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize) + +#else /* not REGEX_MALLOC */ + +/* Emacs already defines alloca, sometimes. */ +#ifndef alloca + +/* Make alloca work the best possible way. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#if HAVE_ALLOCA_H +#include +#else /* not __GNUC__ or HAVE_ALLOCA_H */ +#ifndef _AIX /* Already did AIX, up at the top. */ +char *alloca (); +#endif /* not _AIX */ +#endif /* not HAVE_ALLOCA_H */ +#endif /* not __GNUC__ */ + +#endif /* not alloca */ + +#define REGEX_ALLOCATE alloca + +/* Assumes a `char *destination' variable. */ +#define REGEX_REALLOCATE(source, osize, nsize) \ + (destination = (char *) alloca (nsize), \ + bcopy (source, destination, osize), \ + destination) + +#endif /* not REGEX_MALLOC */ + + +/* True if `size1' is non-NULL and PTR is pointing anywhere inside + `string1' or just past its end. This works if PTR is NULL, which is + a good thing. */ +#define FIRST_STRING_P(ptr) \ + (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) + +/* (Re)Allocate N items of type T using malloc, or fail. */ +#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t))) +#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t))) +#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t))) + +#define BYTEWIDTH 8 /* In bits. */ + +#define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +typedef char boolean; +#define false 0 +#define true 1 + +/* These are the command codes that appear in compiled regular + expressions. Some opcodes are followed by argument bytes. A + command code can specify any interpretation whatsoever for its + arguments. Zero bytes may appear in the compiled regular expression. + + The value of `exactn' is needed in search.c (search_buffer) in Emacs. + So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of + `exactn' we use here must also be 1. */ + +typedef enum +{ + no_op = 0, + + /* Followed by one byte giving n, then by n literal bytes. */ + exactn = 1, + + /* Matches any (more or less) character. */ + anychar, + + /* Matches any one char belonging to specified set. First + following byte is number of bitmap bytes. Then come bytes + for a bitmap saying which chars are in. Bits in each byte + are ordered low-bit-first. A character is in the set if its + bit is 1. A character too large to have a bit in the map is + automatically not in the set. */ + charset, + + /* Same parameters as charset, but match any character that is + not one of those specified. */ + charset_not, + + /* Start remembering the text that is matched, for storing in a + register. Followed by one byte with the register number, in + the range 0 to one less than the pattern buffer's re_nsub + field. Then followed by one byte with the number of groups + inner to this one. (This last has to be part of the + start_memory only because we need it in the on_failure_jump + of re_match_2.) */ + start_memory, + + /* Stop remembering the text that is matched and store it in a + memory register. Followed by one byte with the register + number, in the range 0 to one less than `re_nsub' in the + pattern buffer, and one byte with the number of inner groups, + just like `start_memory'. (We need the number of inner + groups here because we don't have any easy way of finding the + corresponding start_memory when we're at a stop_memory.) */ + stop_memory, + + /* Match a duplicate of something remembered. Followed by one + byte containing the register number. */ + duplicate, + + /* Fail unless at beginning of line. */ + begline, + + /* Fail unless at end of line. */ + endline, + + /* Succeeds if at beginning of buffer (if emacs) or at beginning + of string to be matched (if not). */ + begbuf, + + /* Analogously, for end of buffer/string. */ + endbuf, + + /* Followed by two byte relative address to which to jump. */ + jump, + + /* Same as jump, but marks the end of an alternative. */ + jump_past_alt, + + /* Followed by two-byte relative address of place to resume at + in case of failure. */ + on_failure_jump, + + /* Like on_failure_jump, but pushes a placeholder instead of the + current string position when executed. */ + on_failure_keep_string_jump, + + /* Throw away latest failure point and then jump to following + two-byte relative address. */ + pop_failure_jump, + + /* Change to pop_failure_jump if know won't have to backtrack to + match; otherwise change to jump. This is used to jump + back to the beginning of a repeat. If what follows this jump + clearly won't match what the repeat does, such that we can be + sure that there is no use backtracking out of repetitions + already matched, then we change it to a pop_failure_jump. + Followed by two-byte address. */ + maybe_pop_jump, + + /* Jump to following two-byte address, and push a dummy failure + point. This failure point will be thrown away if an attempt + is made to use it for a failure. A `+' construct makes this + before the first repeat. Also used as an intermediary kind + of jump when compiling an alternative. */ + dummy_failure_jump, + + /* Push a dummy failure point and continue. Used at the end of + alternatives. */ + push_dummy_failure, + + /* Followed by two-byte relative address and two-byte number n. + After matching N times, jump to the address upon failure. */ + succeed_n, + + /* Followed by two-byte relative address, and two-byte number n. + Jump to the address N times, then fail. */ + jump_n, + + /* Set the following two-byte relative address to the + subsequent two-byte number. The address *includes* the two + bytes of number. */ + set_number_at, + + wordchar, /* Matches any word-constituent character. */ + notwordchar, /* Matches any char that is not a word-constituent. */ + + wordbeg, /* Succeeds if at word beginning. */ + wordend, /* Succeeds if at word end. */ + + wordbound, /* Succeeds if at a word boundary. */ + notwordbound /* Succeeds if not at a word boundary. */ + +#ifdef emacs + ,before_dot, /* Succeeds if before point. */ + at_dot, /* Succeeds if at point. */ + after_dot, /* Succeeds if after point. */ + + /* Matches any character whose syntax is specified. Followed by + a byte which contains a syntax code, e.g., Sword. */ + syntaxspec, + + /* Matches any character whose syntax is not that specified. */ + notsyntaxspec +#endif /* emacs */ +} re_opcode_t; + +/* Common operations on the compiled pattern. */ + +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ + +#define STORE_NUMBER(destination, number) \ + do { \ + (destination)[0] = (number) & 0377; \ + (destination)[1] = (number) >> 8; \ + } while (0) + +/* Same as STORE_NUMBER, except increment DESTINATION to + the byte after where the number is stored. Therefore, DESTINATION + must be an lvalue. */ + +#define STORE_NUMBER_AND_INCR(destination, number) \ + do { \ + STORE_NUMBER (destination, number); \ + (destination) += 2; \ + } while (0) + +/* Put into DESTINATION a number stored in two contiguous bytes starting + at SOURCE. */ + +#define EXTRACT_NUMBER(destination, source) \ + do { \ + (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \ + } while (0) + +#ifdef DEBUG +static void +extract_number (dest, source) + int *dest; + unsigned char *source; +{ + int temp = SIGN_EXTEND_CHAR (*(source + 1)); + *dest = *source & 0377; + *dest += temp << 8; +} + +#ifndef EXTRACT_MACROS /* To debug the macros. */ +#undef EXTRACT_NUMBER +#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src) +#endif /* not EXTRACT_MACROS */ + +#endif /* DEBUG */ + +/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number. + SOURCE must be an lvalue. */ + +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + do { \ + EXTRACT_NUMBER (destination, source); \ + (source) += 2; \ + } while (0) + +#ifdef DEBUG +static void +extract_number_and_incr (destination, source) + int *destination; + unsigned char **source; +{ + extract_number (destination, *source); + *source += 2; +} + +#ifndef EXTRACT_MACROS +#undef EXTRACT_NUMBER_AND_INCR +#define EXTRACT_NUMBER_AND_INCR(dest, src) \ + extract_number_and_incr (&dest, &src) +#endif /* not EXTRACT_MACROS */ + +#endif /* DEBUG */ + +/* If DEBUG is defined, Regex prints many voluminous messages about what + it is doing (if the variable `debug' is nonzero). If linked with the + main program in `iregex.c', you can enter patterns and strings + interactively. And if linked with the main program in `main.c' and + the other test files, you can run the already-written tests. */ + +#ifdef DEBUG + +/* We use standard I/O for debugging. */ +#include + +/* It is useful to test things that ``must'' be true when debugging. */ +#include + +static int debug = 0; + +#define DEBUG_STATEMENT(e) e +#define DEBUG_PRINT1(x) if (debug) printf (x) +#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2) +#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3) +#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4) +#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \ + if (debug) print_partial_compiled_pattern (s, e) +#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \ + if (debug) print_double_string (w, s1, sz1, s2, sz2) + + +extern void printchar (); + +/* Print the fastmap in human-readable form. */ + +void +print_fastmap (fastmap) + char *fastmap; +{ + unsigned was_a_range = 0; + unsigned i = 0; + + while (i < (1 << BYTEWIDTH)) + { + if (fastmap[i++]) + { + was_a_range = 0; + printchar (i - 1); + while (i < (1 << BYTEWIDTH) && fastmap[i]) + { + was_a_range = 1; + i++; + } + if (was_a_range) + { + printf ("-"); + printchar (i - 1); + } + } + } + putchar ('\n'); +} + + +/* Print a compiled pattern string in human-readable form, starting at + the START pointer into it and ending just before the pointer END. */ + +void +print_partial_compiled_pattern (start, end) + unsigned char *start; + unsigned char *end; +{ + int mcnt, mcnt2; + unsigned char *p = start; + unsigned char *pend = end; + + if (start == NULL) + { + printf ("(null)\n"); + return; + } + + /* Loop over pattern commands. */ + while (p < pend) + { + switch ((re_opcode_t) *p++) + { + case no_op: + printf ("/no_op"); + break; + + case exactn: + mcnt = *p++; + printf ("/exactn/%d", mcnt); + do + { + putchar ('/'); + printchar (*p++); + } + while (--mcnt); + break; + + case start_memory: + mcnt = *p++; + printf ("/start_memory/%d/%d", mcnt, *p++); + break; + + case stop_memory: + mcnt = *p++; + printf ("/stop_memory/%d/%d", mcnt, *p++); + break; + + case duplicate: + printf ("/duplicate/%d", *p++); + break; + + case anychar: + printf ("/anychar"); + break; + + case charset: + case charset_not: + { + register int c; + + printf ("/charset%s", + (re_opcode_t) *(p - 1) == charset_not ? "_not" : ""); + + assert (p + *p < pend); + + for (c = 0; c < *p; c++) + { + unsigned bit; + unsigned char map_byte = p[1 + c]; + + putchar ('/'); + + for (bit = 0; bit < BYTEWIDTH; bit++) + if (map_byte & (1 << bit)) + printchar (c * BYTEWIDTH + bit); + } + p += 1 + *p; + break; + } + + case begline: + printf ("/begline"); + break; + + case endline: + printf ("/endline"); + break; + + case on_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_jump/0/%d", mcnt); + break; + + case on_failure_keep_string_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_keep_string_jump/0/%d", mcnt); + break; + + case dummy_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/dummy_failure_jump/0/%d", mcnt); + break; + + case push_dummy_failure: + printf ("/push_dummy_failure"); + break; + + case maybe_pop_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/maybe_pop_jump/0/%d", mcnt); + break; + + case pop_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/pop_failure_jump/0/%d", mcnt); + break; + + case jump_past_alt: + extract_number_and_incr (&mcnt, &p); + printf ("/jump_past_alt/0/%d", mcnt); + break; + + case jump: + extract_number_and_incr (&mcnt, &p); + printf ("/jump/0/%d", mcnt); + break; + + case succeed_n: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2); + break; + + case jump_n: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2); + break; + + case set_number_at: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2); + break; + + case wordbound: + printf ("/wordbound"); + break; + + case notwordbound: + printf ("/notwordbound"); + break; + + case wordbeg: + printf ("/wordbeg"); + break; + + case wordend: + printf ("/wordend"); + +#ifdef emacs + case before_dot: + printf ("/before_dot"); + break; + + case at_dot: + printf ("/at_dot"); + break; + + case after_dot: + printf ("/after_dot"); + break; + + case syntaxspec: + printf ("/syntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; + + case notsyntaxspec: + printf ("/notsyntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; +#endif /* emacs */ + + case wordchar: + printf ("/wordchar"); + break; + + case notwordchar: + printf ("/notwordchar"); + break; + + case begbuf: + printf ("/begbuf"); + break; + + case endbuf: + printf ("/endbuf"); + break; + + default: + printf ("?%d", *(p-1)); + } + } + printf ("/\n"); +} + + +void +print_compiled_pattern (bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *buffer = bufp->buffer; + + print_partial_compiled_pattern (buffer, buffer + bufp->used); + printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated); + + if (bufp->fastmap_accurate && bufp->fastmap) + { + printf ("fastmap: "); + print_fastmap (bufp->fastmap); + } + + printf ("re_nsub: %d\t", bufp->re_nsub); + printf ("regs_alloc: %d\t", bufp->regs_allocated); + printf ("can_be_null: %d\t", bufp->can_be_null); + printf ("newline_anchor: %d\n", bufp->newline_anchor); + printf ("no_sub: %d\t", bufp->no_sub); + printf ("not_bol: %d\t", bufp->not_bol); + printf ("not_eol: %d\t", bufp->not_eol); + printf ("syntax: %d\n", bufp->syntax); + /* Perhaps we should print the translate table? */ +} + + +void +print_double_string (where, string1, size1, string2, size2) + const char *where; + const char *string1; + const char *string2; + int size1; + int size2; +{ + unsigned this_char; + + if (where == NULL) + printf ("(null)"); + else + { + if (FIRST_STRING_P (where)) + { + for (this_char = where - string1; this_char < size1; this_char++) + printchar (string1[this_char]); + + where = string2; + } + + for (this_char = where - string2; this_char < size2; this_char++) + printchar (string2[this_char]); + } +} + +#else /* not DEBUG */ + +#undef assert +#define assert(e) + +#define DEBUG_STATEMENT(e) +#define DEBUG_PRINT1(x) +#define DEBUG_PRINT2(x1, x2) +#define DEBUG_PRINT3(x1, x2, x3) +#define DEBUG_PRINT4(x1, x2, x3, x4) +#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) +#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) + +#endif /* not DEBUG */ + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; + return ret; +} + +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. */ + +static const char *re_error_msg[] = + { NULL, /* REG_NOERROR */ + "No match", /* REG_NOMATCH */ + "Invalid regular expression", /* REG_BADPAT */ + "Invalid collation character", /* REG_ECOLLATE */ + "Invalid character class name", /* REG_ECTYPE */ + "Trailing backslash", /* REG_EESCAPE */ + "Invalid back reference", /* REG_ESUBREG */ + "Unmatched [ or [^", /* REG_EBRACK */ + "Unmatched ( or \\(", /* REG_EPAREN */ + "Unmatched \\{", /* REG_EBRACE */ + "Invalid content of \\{\\}", /* REG_BADBR */ + "Invalid range end", /* REG_ERANGE */ + "Memory exhausted", /* REG_ESPACE */ + "Invalid preceding regular expression", /* REG_BADRPT */ + "Premature end of regular expression", /* REG_EEND */ + "Regular expression too big", /* REG_ESIZE */ + "Unmatched ) or \\)", /* REG_ERPAREN */ + }; + +/* Subroutine declarations and macros for regex_compile. */ + +static void store_op1 (), store_op2 (); +static void insert_op1 (), insert_op2 (); +static boolean at_begline_loc_p (), at_endline_loc_p (); +static boolean group_in_compile_stack (); +static reg_errcode_t compile_range (); + +/* Fetch the next character in the uncompiled pattern---translating it + if necessary. Also cast from a signed character in the constant + string passed to us by the user to an unsigned char that we can use + as an array index (in, e.g., `translate'). */ +#define PATFETCH(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + if (translate) c = translate[c]; \ + } while (0) + +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + } while (0) + +/* Go backwards one character in the pattern. */ +#define PATUNFETCH p-- + + +/* If `translate' is non-null, return translate[D], else just D. We + cast the subscript to translate because some data is declared as + `char *', to avoid warnings when a string constant is passed. But + when we use a character as a subscript we must make it unsigned. */ +#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d)) + + +/* Macros for outputting the compiled pattern into `buffer'. */ + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 32 + +/* Make sure we have at least N more bytes of space in buffer. */ +#define GET_BUFFER_SPACE(n) \ + while (b - bufp->buffer + (n) > bufp->allocated) \ + EXTEND_BUFFER () + +/* Make sure we have one more byte of buffer space and then add C to it. */ +#define BUF_PUSH(c) \ + do { \ + GET_BUFFER_SPACE (1); \ + *b++ = (unsigned char) (c); \ + } while (0) + + +/* Ensure we have two more bytes of buffer space and then append C1 and C2. */ +#define BUF_PUSH_2(c1, c2) \ + do { \ + GET_BUFFER_SPACE (2); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + } while (0) + + +/* As with BUF_PUSH_2, except for three bytes. */ +#define BUF_PUSH_3(c1, c2, c3) \ + do { \ + GET_BUFFER_SPACE (3); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + *b++ = (unsigned char) (c3); \ + } while (0) + + +/* Store a jump with opcode OP at LOC to location TO. We store a + relative address offset by the three bytes the jump itself occupies. */ +#define STORE_JUMP(op, loc, to) \ + store_op1 (op, loc, (to) - (loc) - 3) + +/* Likewise, for a two-argument jump. */ +#define STORE_JUMP2(op, loc, to, arg) \ + store_op2 (op, loc, (to) - (loc) - 3, arg) + +/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP(op, loc, to) \ + insert_op1 (op, loc, (to) - (loc) - 3, b) + +/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP2(op, loc, to, arg) \ + insert_op2 (op, loc, (to) - (loc) - 3, arg, b) + + +/* This is not an arbitrary limit: the arguments which represent offsets + into the pattern are two bytes long. So if 2^16 bytes turns out to + be too small, many things would have to change. */ +#define MAX_BUF_SIZE (1L << 16) + + +/* Extend the buffer by twice its current size via realloc and + reset the pointers that pointed into the old block to point to the + correct places in the new one. If extending the buffer results in it + being larger than MAX_BUF_SIZE, then flag memory exhausted. */ +#define EXTEND_BUFFER() \ + do { \ + unsigned char *old_buffer = bufp->buffer; \ + if (bufp->allocated == MAX_BUF_SIZE) \ + return REG_ESIZE; \ + bufp->allocated <<= 1; \ + if (bufp->allocated > MAX_BUF_SIZE) \ + bufp->allocated = MAX_BUF_SIZE; \ + bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\ + if (bufp->buffer == NULL) \ + return REG_ESPACE; \ + /* If the buffer moved, move all the pointers into it. */ \ + if (old_buffer != bufp->buffer) \ + { \ + b = (b - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (fixup_alt_jump) \ + fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } \ + } while (0) + + +/* Since we have one byte reserved for the register number argument to + {start,stop}_memory, the maximum number of groups we can report + things about is what fits in that byte. */ +#define MAX_REGNUM 255 + +/* But patterns can have more than `MAX_REGNUM' registers. We just + ignore the excess. */ +typedef unsigned regnum_t; + + +/* Macros for the compile stack. */ + +/* Since offsets can go either forwards or backwards, this type needs to + be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ +typedef int pattern_offset_t; + +typedef struct +{ + pattern_offset_t begalt_offset; + pattern_offset_t fixup_alt_jump; + pattern_offset_t inner_group_offset; + pattern_offset_t laststart_offset; + regnum_t regnum; +} compile_stack_elt_t; + + +typedef struct +{ + compile_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} compile_stack_type; + + +#define INIT_COMPILE_STACK_SIZE 32 + +#define COMPILE_STACK_EMPTY (compile_stack.avail == 0) +#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size) + +/* The next available element. */ +#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail]) + + +/* Set the bit for character C in a list. */ +#define SET_LIST_BIT(c) \ + (b[((unsigned char) (c)) / BYTEWIDTH] \ + |= 1 << (((unsigned char) c) % BYTEWIDTH)) + + +/* Get the next unsigned number in the uncompiled pattern. */ +#define GET_UNSIGNED_NUMBER(num) \ + { if (p != pend) \ + { \ + PATFETCH (c); \ + while (ISDIGIT (c)) \ + { \ + if (num < 0) \ + num = 0; \ + num = num * 10 + c - '0'; \ + if (p == pend) \ + break; \ + PATFETCH (c); \ + } \ + } \ + } + +#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ + +#define IS_CHAR_CLASS(string) \ + (STREQ (string, "alpha") || STREQ (string, "upper") \ + || STREQ (string, "lower") || STREQ (string, "digit") \ + || STREQ (string, "alnum") || STREQ (string, "xdigit") \ + || STREQ (string, "space") || STREQ (string, "print") \ + || STREQ (string, "punct") || STREQ (string, "graph") \ + || STREQ (string, "cntrl") || STREQ (string, "blank")) + +/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX. + Returns one of error codes defined in `regex.h', or zero for success. + + Assumes the `allocated' (and perhaps `buffer') and `translate' + fields are set in BUFP on entry. + + If it succeeds, results are put in BUFP (if it returns an error, the + contents of BUFP are undefined): + `buffer' is the compiled pattern; + `syntax' is set to SYNTAX; + `used' is set to the length of the compiled pattern; + `fastmap_accurate' is zero; + `re_nsub' is the number of subexpressions in PATTERN; + `not_bol' and `not_eol' are zero; + + The `fastmap' and `newline_anchor' fields are neither + examined nor set. */ + +static reg_errcode_t +regex_compile (pattern, size, syntax, bufp) + const char *pattern; + int size; + reg_syntax_t syntax; + struct re_pattern_buffer *bufp; +{ + /* We fetch characters from PATTERN here. Even though PATTERN is + `char *' (i.e., signed), we declare these variables as unsigned, so + they can be reliably used as array indices. */ + register unsigned char c, c1; + + /* A random tempory spot in PATTERN. */ + const char *p1; + + /* Points to the end of the buffer, where we should append. */ + register unsigned char *b; + + /* Keeps track of unclosed groups. */ + compile_stack_type compile_stack; + + /* Points to the current (ending) position in the pattern. */ + const char *p = pattern; + const char *pend = pattern + size; + + /* How to translate the characters in the pattern. */ + char *translate = bufp->translate; + + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell if a new exact-match + character can be added to that command or if the character requires + a new `exactn' command. */ + unsigned char *pending_exact = 0; + + /* Address of start of the most recently finished expression. + This tells, e.g., postfix * where to find the start of its + operand. Reset at the beginning of groups and alternatives. */ + unsigned char *laststart = 0; + + /* Address of beginning of regexp, or inside of last group. */ + unsigned char *begalt; + + /* Place in the uncompiled pattern (i.e., the {) to + which to go back if the interval is invalid. */ + const char *beg_interval; + + /* Address of the place where a forward jump should go to the end of + the containing expression. Each alternative of an `or' -- except the + last -- ends with a forward jump of this sort. */ + unsigned char *fixup_alt_jump = 0; + + /* Counts open-groups as they are encountered. Remembered for the + matching close-group on the compile stack, so the same register + number is put in the stop_memory as the start_memory. */ + regnum_t regnum = 0; + +#ifdef DEBUG + DEBUG_PRINT1 ("\nCompiling pattern: "); + if (debug) + { + unsigned debug_count; + + for (debug_count = 0; debug_count < size; debug_count++) + printchar (pattern[debug_count]); + putchar ('\n'); + } +#endif /* DEBUG */ + + /* Initialize the compile stack. */ + compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t); + if (compile_stack.stack == NULL) + return REG_ESPACE; + + compile_stack.size = INIT_COMPILE_STACK_SIZE; + compile_stack.avail = 0; + + /* Initialize the pattern buffer. */ + bufp->syntax = syntax; + bufp->fastmap_accurate = 0; + bufp->not_bol = bufp->not_eol = 0; + + /* Set `used' to zero, so that if we return an error, the pattern + printer (for debugging) will think there's no pattern. We reset it + at the end. */ + bufp->used = 0; + + /* Always count groups, whether or not bufp->no_sub is set. */ + bufp->re_nsub = 0; + +#if !defined (emacs) && !defined (SYNTAX_TABLE) + /* Initialize the syntax table. */ + init_syntax_once (); +#endif + + if (bufp->allocated == 0) + { + if (bufp->buffer) + { /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. */ + RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char); + } + else + { /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char); + } + if (!bufp->buffer) return REG_ESPACE; + + bufp->allocated = INIT_BUF_SIZE; + } + + begalt = b = bufp->buffer; + + /* Loop through the uncompiled pattern until we're at the end. */ + while (p != pend) + { + PATFETCH (c); + + switch (c) + { + case '^': + { + if ( /* If at start of pattern, it's an operator. */ + p == pattern + 1 + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's come before. */ + || at_begline_loc_p (pattern, p, syntax)) + BUF_PUSH (begline); + else + goto normal_char; + } + break; + + + case '$': + { + if ( /* If at end of pattern, it's an operator. */ + p == pend + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's next. */ + || at_endline_loc_p (p, pend, syntax)) + BUF_PUSH (endline); + else + goto normal_char; + } + break; + + + case '+': + case '?': + if ((syntax & RE_BK_PLUS_QM) + || (syntax & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern... */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + return REG_BADRPT; + else if (!(syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + + { + /* Are we optimizing this jump? */ + boolean keep_string_p = false; + + /* 1 means zero (many) matches is allowed. */ + char zero_times_ok = 0, many_times_ok = 0; + + /* If there is a sequence of repetition chars, collapse it + down to just one (the right one). We can't combine + interval operators with these because of, e.g., `a{2}*', + which should only match an even number of `a's. */ + + for (;;) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + + if (p == pend) + break; + + PATFETCH (c); + + if (c == '*' + || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))) + ; + + else if (syntax & RE_BK_PLUS_QM && c == '\\') + { + if (p == pend) return REG_EESCAPE; + + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + + c = c1; + } + else + { + PATUNFETCH; + break; + } + + /* If we get here, we found another repeat character. */ + } + + /* Star, etc. applied to an empty pattern is equivalent + to an empty pattern. */ + if (!laststart) + break; + + /* Now we know whether or not zero matches is allowed + and also whether or not two or more matches is allowed. */ + if (many_times_ok) + { /* More than one repetition is allowed, so put in at the + end a backward relative jump from `b' to before the next + jump we're going to put in below (which jumps from + laststart to after this jump). + + But if we are at the `*' in the exact sequence `.*\n', + insert an unconditional jump backwards to the ., + instead of the beginning of the loop. This way we only + push a failure point once, instead of every time + through the loop. */ + assert (p - 1 > pattern); + + /* Allocate the space for the jump. */ + GET_BUFFER_SPACE (3); + + /* We know we are not at the first character of the pattern, + because laststart was nonzero. And we've already + incremented `p', by the way, to be the character after + the `*'. Do we have to do something analogous here + for null bytes, because of RE_DOT_NOT_NULL? */ + if (TRANSLATE (*(p - 2)) == TRANSLATE ('.') + && zero_times_ok + && p < pend && TRANSLATE (*p) == TRANSLATE ('\n') + && !(syntax & RE_DOT_NEWLINE)) + { /* We have .*\n. */ + STORE_JUMP (jump, b, laststart); + keep_string_p = true; + } + else + /* Anything else. */ + STORE_JUMP (maybe_pop_jump, b, laststart - 3); + + /* We've added more stuff to the buffer. */ + b += 3; + } + + /* On failure, jump from laststart to b + 3, which will be the + end of the buffer after this jump is inserted. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump + : on_failure_jump, + laststart, b + 3); + pending_exact = 0; + b += 3; + + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + `dummy_failure_jump' before the initial + `on_failure_jump' instruction of the loop. This + effects a skip over that instruction the first time + we hit that loop. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6); + b += 3; + } + } + break; + + + case '.': + laststart = b; + BUF_PUSH (anychar); + break; + + + case '[': + { + boolean had_char_class = false; + + if (p == pend) return REG_EBRACK; + + /* Ensure that we have enough space to push a charset: the + opcode, the length count, and the bitset; 34 bytes in all. */ + GET_BUFFER_SPACE (34); + + laststart = b; + + /* We test `*p == '^' twice, instead of using an if + statement, so we only need one BUF_PUSH. */ + BUF_PUSH (*p == '^' ? charset_not : charset); + if (*p == '^') + p++; + + /* Remember the first position in the bracket expression. */ + p1 = p; + + /* Push the number of bytes in the bitmap. */ + BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + + /* Clear the whole map. */ + bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); + + /* charset_not matches newline according to a syntax bit. */ + if ((re_opcode_t) b[-2] == charset_not + && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) + SET_LIST_BIT ('\n'); + + /* Read in characters and ranges, setting map bits. */ + for (;;) + { + if (p == pend) return REG_EBRACK; + + PATFETCH (c); + + /* \ might escape characters inside [...] and [^...]. */ + if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') + { + if (p == pend) return REG_EESCAPE; + + PATFETCH (c1); + SET_LIST_BIT (c1); + continue; + } + + /* Could be the end of the bracket expression. If it's + not (i.e., when the bracket expression is `[]' so + far), the ']' character bit gets set way below. */ + if (c == ']' && p != p1 + 1) + break; + + /* Look ahead to see if it's a range when the last thing + was a character class. */ + if (had_char_class && c == '-' && *p != ']') + return REG_ERANGE; + + /* Look ahead to see if it's a range when the last thing + was a character: if this is a hyphen not at the + beginning or the end of a list, then it's the range + operator. */ + if (c == '-' + && !(p - 2 >= pattern && p[-2] == '[') + && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^') + && *p != ']') + { + reg_errcode_t ret + = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) return ret; + } + + else if (p[0] == '-' && p[1] != ']') + { /* This handles ranges made up of characters only. */ + reg_errcode_t ret; + + /* Move past the `-'. */ + PATFETCH (c1); + + ret = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) return ret; + } + + /* See if we're at the beginning of a possible character + class. */ + + else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') + { /* Leave room for the null. */ + char str[CHAR_CLASS_MAX_LENGTH + 1]; + + PATFETCH (c); + c1 = 0; + + /* If pattern is `[[:'. */ + if (p == pend) return REG_EBRACK; + + for (;;) + { + PATFETCH (c); + if (c == ':' || c == ']' || p == pend + || c1 == CHAR_CLASS_MAX_LENGTH) + break; + str[c1++] = c; + } + str[c1] = '\0'; + + /* If isn't a word bracketed by `[:' and:`]': + undo the ending character, the letters, and leave + the leading `:' and `[' (but set bits for them). */ + if (c == ':' && *p == ']') + { + int ch; + boolean is_alnum = STREQ (str, "alnum"); + boolean is_alpha = STREQ (str, "alpha"); + boolean is_blank = STREQ (str, "blank"); + boolean is_cntrl = STREQ (str, "cntrl"); + boolean is_digit = STREQ (str, "digit"); + boolean is_graph = STREQ (str, "graph"); + boolean is_lower = STREQ (str, "lower"); + boolean is_print = STREQ (str, "print"); + boolean is_punct = STREQ (str, "punct"); + boolean is_space = STREQ (str, "space"); + boolean is_upper = STREQ (str, "upper"); + boolean is_xdigit = STREQ (str, "xdigit"); + + if (!IS_CHAR_CLASS (str)) return REG_ECTYPE; + + /* Throw away the ] at the end of the character + class. */ + PATFETCH (c); + + if (p == pend) return REG_EBRACK; + + for (ch = 0; ch < 1 << BYTEWIDTH; ch++) + { + if ( (is_alnum && ISALNUM (ch)) + || (is_alpha && ISALPHA (ch)) + || (is_blank && ISBLANK (ch)) + || (is_cntrl && ISCNTRL (ch)) + || (is_digit && ISDIGIT (ch)) + || (is_graph && ISGRAPH (ch)) + || (is_lower && ISLOWER (ch)) + || (is_print && ISPRINT (ch)) + || (is_punct && ISPUNCT (ch)) + || (is_space && ISSPACE (ch)) + || (is_upper && ISUPPER (ch)) + || (is_xdigit && ISXDIGIT (ch))) + SET_LIST_BIT (ch); + } + had_char_class = true; + } + else + { + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + SET_LIST_BIT (':'); + had_char_class = false; + } + } + else + { + had_char_class = false; + SET_LIST_BIT (c); + } + } + + /* Discard any (non)matching list bytes that are all 0 at the + end of the map. Decrease the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + } + break; + + + case '(': + if (syntax & RE_NO_BK_PARENS) + goto handle_open; + else + goto normal_char; + + + case ')': + if (syntax & RE_NO_BK_PARENS) + goto handle_close; + else + goto normal_char; + + + case '\n': + if (syntax & RE_NEWLINE_ALT) + goto handle_alt; + else + goto normal_char; + + + case '|': + if (syntax & RE_NO_BK_VBAR) + goto handle_alt; + else + goto normal_char; + + + case '{': + if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) + goto handle_interval; + else + goto normal_char; + + + case '\\': + if (p == pend) return REG_EESCAPE; + + /* Do not translate the character after the \, so that we can + distinguish, e.g., \B from \b, even if we normally would + translate, e.g., B to b. */ + PATFETCH_RAW (c); + + switch (c) + { + case '(': + if (syntax & RE_NO_BK_PARENS) + goto normal_backslash; + + handle_open: + bufp->re_nsub++; + regnum++; + + if (COMPILE_STACK_FULL) + { + RETALLOC (compile_stack.stack, compile_stack.size << 1, + compile_stack_elt_t); + if (compile_stack.stack == NULL) return REG_ESPACE; + + compile_stack.size <<= 1; + } + + /* These are the values to restore when we hit end of this + group. They are all relative offsets, so that if the + whole pattern moves because of realloc, they will still + be valid. */ + COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; + COMPILE_STACK_TOP.fixup_alt_jump + = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; + COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; + COMPILE_STACK_TOP.regnum = regnum; + + /* We will eventually replace the 0 with the number of + groups inner to this one. But do not push a + start_memory for groups beyond the last one we can + represent in the compiled pattern. */ + if (regnum <= MAX_REGNUM) + { + COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; + BUF_PUSH_3 (start_memory, regnum, 0); + } + + compile_stack.avail++; + + fixup_alt_jump = 0; + laststart = 0; + begalt = b; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + break; + + + case ')': + if (syntax & RE_NO_BK_PARENS) goto normal_backslash; + + if (COMPILE_STACK_EMPTY) + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_backslash; + else + return REG_ERPAREN; + + handle_close: + if (fixup_alt_jump) + { /* Push a dummy failure point at the end of the + alternative for a possible future + `pop_failure_jump' to pop. See comments at + `push_dummy_failure' in `re_match_2'. */ + BUF_PUSH (push_dummy_failure); + + /* We allocated space for this jump when we assigned + to `fixup_alt_jump', in the `handle_alt' case below. */ + STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); + } + + /* See similar code for backslashed left paren above. */ + if (COMPILE_STACK_EMPTY) + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_char; + else + return REG_ERPAREN; + + /* Since we just checked for an empty stack above, this + ``can't happen''. */ + assert (compile_stack.avail != 0); + { + /* We don't just want to restore into `regnum', because + later groups should continue to be numbered higher, + as in `(ab)c(de)' -- the second group is #2. */ + regnum_t this_group_regnum; + + compile_stack.avail--; + begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; + fixup_alt_jump + = COMPILE_STACK_TOP.fixup_alt_jump + ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 + : 0; + laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; + this_group_regnum = COMPILE_STACK_TOP.regnum; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + + /* We're at the end of the group, so now we know how many + groups were inside this one. */ + if (this_group_regnum <= MAX_REGNUM) + { + unsigned char *inner_group_loc + = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; + + *inner_group_loc = regnum - this_group_regnum; + BUF_PUSH_3 (stop_memory, this_group_regnum, + regnum - this_group_regnum); + } + } + break; + + + case '|': /* `\|'. */ + if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) + goto normal_backslash; + handle_alt: + if (syntax & RE_LIMITED_OPS) + goto normal_char; + + /* Insert before the previous alternative a jump which + jumps to this alternative if the former fails. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (on_failure_jump, begalt, b + 6); + pending_exact = 0; + b += 3; + + /* The alternative before this one has a jump after it + which gets executed if it gets matched. Adjust that + jump so it will jump to this alternative's analogous + jump (put in below, which in turn will jump to the next + (if any) alternative's such jump, etc.). The last such + jump jumps to the correct final destination. A picture: + _____ _____ + | | | | + | v | v + a | b | c + + If we are at `b', then fixup_alt_jump right now points to a + three-byte space after `a'. We'll put in the jump, set + fixup_alt_jump to right after `b', and leave behind three + bytes which we'll fill in when we get to after `c'. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + /* Mark and leave space for a jump after this alternative, + to be filled in later either by next alternative or + when know we're at the end of a series of alternatives. */ + fixup_alt_jump = b; + GET_BUFFER_SPACE (3); + b += 3; + + laststart = 0; + begalt = b; + break; + + + case '{': + /* If \{ is a literal. */ + if (!(syntax & RE_INTERVALS) + /* If we're at `\{' and it's not the open-interval + operator. */ + || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + || (p - 2 == pattern && p == pend)) + goto normal_backslash; + + handle_interval: + { + /* If got here, then the syntax allows intervals. */ + + /* At least (most) this many matches must be made. */ + int lower_bound = -1, upper_bound = -1; + + beg_interval = p - 1; + + if (p == pend) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + return REG_EBRACE; + } + + GET_UNSIGNED_NUMBER (lower_bound); + + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) upper_bound = RE_DUP_MAX; + } + else + /* Interval such as `{1}' => match exactly once. */ + upper_bound = lower_bound; + + if (lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + return REG_BADBR; + } + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (c != '\\') return REG_EBRACE; + + PATFETCH (c); + } + + if (c != '}') + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + return REG_BADBR; + } + + /* We just parsed a valid interval. */ + + /* If it's invalid to have no preceding re. */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + return REG_BADRPT; + else if (syntax & RE_CONTEXT_INDEP_OPS) + laststart = b; + else + goto unfetch_interval; + } + + /* If the upper bound is zero, don't want to succeed at + all; jump from `laststart' to `b + 3', which will be + the end of the buffer after we insert the jump. */ + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + INSERT_JUMP (jump, laststart, b + 3); + b += 3; + } + + /* Otherwise, we have a nontrivial interval. When + we're all done, the pattern will look like: + set_number_at + set_number_at + succeed_n + + jump_n + (The upper bound and `jump_n' are omitted if + `upper_bound' is 1, though.) */ + else + { /* If the upper bound is > 1, we need to insert + more at the end of the loop. */ + unsigned nbytes = 10 + (upper_bound > 1) * 10; + + GET_BUFFER_SPACE (nbytes); + + /* Initialize lower bound of the `succeed_n', even + though it will be set during matching by its + attendant `set_number_at' (inserted next), + because `re_compile_fastmap' needs to know. + Jump to the `jump_n' we might insert below. */ + INSERT_JUMP2 (succeed_n, laststart, + b + 5 + (upper_bound > 1) * 5, + lower_bound); + b += 5; + + /* Code to initialize the lower bound. Insert + before the `succeed_n'. The `5' is the last two + bytes of this `set_number_at', plus 3 bytes of + the following `succeed_n'. */ + insert_op2 (set_number_at, laststart, 5, lower_bound, b); + b += 5; + + if (upper_bound > 1) + { /* More than one repetition is allowed, so + append a backward jump to the `succeed_n' + that starts this interval. + + When we've reached this during matching, + we'll have matched the interval once, so + jump back only `upper_bound - 1' times. */ + STORE_JUMP2 (jump_n, b, laststart + 5, + upper_bound - 1); + b += 5; + + /* The location we want to set is the second + parameter of the `jump_n'; that is `b-2' as + an absolute address. `laststart' will be + the `set_number_at' we're about to insert; + `laststart+3' the number to set, the source + for the relative address. But we are + inserting into the middle of the pattern -- + so everything is getting moved up by 5. + Conclusion: (b - 2) - (laststart + 3) + 5, + i.e., b - laststart. + + We insert this at the beginning of the loop + so that if we fail during matching, we'll + reinitialize the bounds. */ + insert_op2 (set_number_at, laststart, b - laststart, + upper_bound - 1, b); + b += 5; + } + } + pending_exact = 0; + beg_interval = NULL; + } + break; + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + assert (beg_interval); + p = beg_interval; + beg_interval = NULL; + + /* normal_char and normal_backslash need `c'. */ + PATFETCH (c); + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (p > pattern && p[-1] == '\\') + goto normal_backslash; + } + goto normal_char; + +#ifdef emacs + /* There is no way to specify the before_dot and after_dot + operators. rms says this is ok. --karl */ + case '=': + BUF_PUSH (at_dot); + break; + + case 's': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]); + break; +#endif /* emacs */ + + + case 'w': + laststart = b; + BUF_PUSH (wordchar); + break; + + + case 'W': + laststart = b; + BUF_PUSH (notwordchar); + break; + + + case '<': + BUF_PUSH (wordbeg); + break; + + case '>': + BUF_PUSH (wordend); + break; + + case 'b': + BUF_PUSH (wordbound); + break; + + case 'B': + BUF_PUSH (notwordbound); + break; + + case '`': + BUF_PUSH (begbuf); + break; + + case '\'': + BUF_PUSH (endbuf); + break; + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (syntax & RE_NO_BK_REFS) + goto normal_char; + + c1 = c - '0'; + + if (c1 > regnum) + return REG_ESUBREG; + + /* Can't back reference to a subexpression if inside of it. */ + if (group_in_compile_stack (compile_stack, c1)) + goto normal_char; + + laststart = b; + BUF_PUSH_2 (duplicate, c1); + break; + + + case '+': + case '?': + if (syntax & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backslash; + + default: + normal_backslash: + /* You might think it would be useful for \ to mean + not to translate; but if we don't translate it + it will never match anything. */ + c = TRANSLATE (c); + goto normal_char; + } + break; + + + default: + /* Expects the character in `c'. */ + normal_char: + /* If no exactn currently being built. */ + if (!pending_exact + + /* If last exactn not at current position. */ + || pending_exact + *pending_exact + 1 != b + + /* We have only one byte following the exactn for the count. */ + || *pending_exact == (1 << BYTEWIDTH) - 1 + + /* If followed by a repetition operator. */ + || *p == '*' || *p == '^' + || ((syntax & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?')) + || ((syntax & RE_INTERVALS) + && ((syntax & RE_NO_BK_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) + { + /* Start building a new exactn. */ + + laststart = b; + + BUF_PUSH_2 (exactn, 0); + pending_exact = b - 1; + } + + BUF_PUSH (c); + (*pending_exact)++; + break; + } /* switch (c) */ + } /* while p != pend */ + + + /* Through the pattern now. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + if (!COMPILE_STACK_EMPTY) + return REG_EPAREN; + + free (compile_stack.stack); + + /* We have succeeded; set the length of the buffer. */ + bufp->used = b - bufp->buffer; + +#ifdef DEBUG + if (debug) + { + DEBUG_PRINT1 ("\nCompiled pattern: "); + print_compiled_pattern (bufp); + } +#endif /* DEBUG */ + + return REG_NOERROR; +} /* regex_compile */ + +/* Subroutines for `regex_compile'. */ + +/* Store OP at LOC followed by two-byte integer parameter ARG. */ + +static void +store_op1 (op, loc, arg) + re_opcode_t op; + unsigned char *loc; + int arg; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg); +} + + +/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +store_op2 (op, loc, arg1, arg2) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg1); + STORE_NUMBER (loc + 3, arg2); +} + + +/* Copy the bytes from LOC to END to open up three bytes of space at LOC + for OP followed by two-byte integer parameter ARG. */ + +static void +insert_op1 (op, loc, arg, end) + re_opcode_t op; + unsigned char *loc; + int arg; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 3; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op1 (op, loc, arg); +} + + +/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +insert_op2 (op, loc, arg1, arg2, end) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 5; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op2 (op, loc, arg1, arg2); +} + + +/* P points to just after a ^ in PATTERN. Return true if that ^ comes + after an alternative or a begin-subexpression. We assume there is at + least one character before the ^. */ + +static boolean +at_begline_loc_p (pattern, p, syntax) + const char *pattern, *p; + reg_syntax_t syntax; +{ + const char *prev = p - 2; + boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; + + return + /* After a subexpression? */ + (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) + /* After an alternative? */ + || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash)); +} + + +/* The dual of at_begline_loc_p. This one is for $. We assume there is + at least one character after the $, i.e., `P < PEND'. */ + +static boolean +at_endline_loc_p (p, pend, syntax) + const char *p, *pend; + int syntax; +{ + const char *next = p; + boolean next_backslash = *next == '\\'; + const char *next_next = p + 1 < pend ? p + 1 : NULL; + + return + /* Before a subexpression? */ + (syntax & RE_NO_BK_PARENS ? *next == ')' + : next_backslash && next_next && *next_next == ')') + /* Before an alternative? */ + || (syntax & RE_NO_BK_VBAR ? *next == '|' + : next_backslash && next_next && *next_next == '|'); +} + + +/* Returns true if REGNUM is in one of COMPILE_STACK's elements and + false if it's not. */ + +static boolean +group_in_compile_stack (compile_stack, regnum) + compile_stack_type compile_stack; + regnum_t regnum; +{ + int this_element; + + for (this_element = compile_stack.avail - 1; + this_element >= 0; + this_element--) + if (compile_stack.stack[this_element].regnum == regnum) + return true; + + return false; +} + + +/* Read the ending character of a range (in a bracket expression) from the + uncompiled pattern *P_PTR (which ends at PEND). We assume the + starting character is in `P[-2]'. (`P[-1]' is the character `-'.) + Then we set the translation of all bits between the starting and + ending characters (inclusive) in the compiled pattern B. + + Return an error code. + + We use these short variable names so we can use the same macros as + `regex_compile' itself. */ + +static reg_errcode_t +compile_range (p_ptr, pend, translate, syntax, b) + const char **p_ptr, *pend; + char *translate; + reg_syntax_t syntax; + unsigned char *b; +{ + unsigned this_char; + + const char *p = *p_ptr; + int range_start, range_end; + + if (p == pend) + return REG_ERANGE; + + /* Even though the pattern is a signed `char *', we need to fetch + with unsigned char *'s; if the high bit of the pattern character + is set, the range endpoints will be negative if we fetch using a + signed char *. + + We also want to fetch the endpoints without translating them; the + appropriate translation is done in the bit-setting loop below. */ + range_start = ((unsigned char *) p)[-2]; + range_end = ((unsigned char *) p)[0]; + + /* Have to increment the pointer into the pattern string, so the + caller isn't still at the ending character. */ + (*p_ptr)++; + + /* If the start is after the end, the range is empty. */ + if (range_start > range_end) + return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR; + + /* Here we see why `this_char' has to be larger than an `unsigned + char' -- the range is inclusive, so if `range_end' == 0xff + (assuming 8-bit characters), we would otherwise go into an infinite + loop, since all characters <= 0xff. */ + for (this_char = range_start; this_char <= range_end; this_char++) + { + SET_LIST_BIT (TRANSLATE (this_char)); + } + + return REG_NOERROR; +} + +/* Failure stack declarations and macros; both re_compile_fastmap and + re_match_2 use a failure stack. These have to be macros because of + REGEX_ALLOCATE. */ + + +/* Number of failure points for which to initially allocate space + when matching. If this number is exceeded, we allocate more + space, so it is not a hard limit. */ +#ifndef INIT_FAILURE_ALLOC +#define INIT_FAILURE_ALLOC 5 +#endif + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always used MAX_FAILURE_SPACE each time we failed. + This is a variable only so users of regex can assign to it; we never + change it ourselves. */ +int re_max_failures = 2000; + +typedef const unsigned char *fail_stack_elt_t; + +typedef struct +{ + fail_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} fail_stack_type; + +#define FAIL_STACK_EMPTY() (fail_stack.avail == 0) +#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) +#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) +#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail]) + + +/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */ + +#define INIT_FAIL_STACK() \ + do { \ + fail_stack.stack = (fail_stack_elt_t *) \ + REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \ + \ + if (fail_stack.stack == NULL) \ + return -2; \ + \ + fail_stack.size = INIT_FAILURE_ALLOC; \ + fail_stack.avail = 0; \ + } while (0) + + +/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items. + + Return 1 if succeeds, and 0 if either ran out of memory + allocating space for it or it was already too large. + + REGEX_REALLOCATE requires `destination' be declared. */ + +#define DOUBLE_FAIL_STACK(fail_stack) \ + ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \ + ? 0 \ + : ((fail_stack).stack = (fail_stack_elt_t *) \ + REGEX_REALLOCATE ((fail_stack).stack, \ + (fail_stack).size * sizeof (fail_stack_elt_t), \ + ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \ + \ + (fail_stack).stack == NULL \ + ? 0 \ + : ((fail_stack).size <<= 1, \ + 1))) + + +/* Push PATTERN_OP on FAIL_STACK. + + Return 1 if was able to do so and 0 if ran out of memory allocating + space to do so. */ +#define PUSH_PATTERN_OP(pattern_op, fail_stack) \ + ((FAIL_STACK_FULL () \ + && !DOUBLE_FAIL_STACK (fail_stack)) \ + ? 0 \ + : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \ + 1)) + +/* This pushes an item onto the failure stack. Must be a four-byte + value. Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_ITEM(item) \ + fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item + +/* The complement operation. Assumes `fail_stack' is nonempty. */ +#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail] + +/* Used to omit pushing failure point id's when we're not debugging. */ +#ifdef DEBUG +#define DEBUG_PUSH PUSH_FAILURE_ITEM +#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM () +#else +#define DEBUG_PUSH(item) +#define DEBUG_POP(item_addr) +#endif + + +/* Push the information about the state we will need + if we ever fail back to it. + + Requires variables fail_stack, regstart, regend, reg_info, and + num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be + declared. + + Does `return FAILURE_CODE' if runs out of memory. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ + do { \ + char *destination; \ + /* Must be int, so when we don't save any registers, the arithmetic \ + of 0 + -1 isn't done as unsigned. */ \ + int this_reg; \ + \ + DEBUG_STATEMENT (failure_id++); \ + DEBUG_STATEMENT (nfailure_points_pushed++); \ + DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ + DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ + DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ + \ + DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \ + DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ + \ + /* Ensure we have enough space allocated for what we will push. */ \ + while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ + { \ + if (!DOUBLE_FAIL_STACK (fail_stack)) \ + return failure_code; \ + \ + DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ + (fail_stack).size); \ + DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ + } \ + \ + /* Push the info, starting with the registers. */ \ + DEBUG_PRINT1 ("\n"); \ + \ + for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ + this_reg++) \ + { \ + DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ + DEBUG_STATEMENT (num_regs_pushed++); \ + \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + PUSH_FAILURE_ITEM (regstart[this_reg]); \ + \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + PUSH_FAILURE_ITEM (regend[this_reg]); \ + \ + DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ + DEBUG_PRINT2 (" match_null=%d", \ + REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ + DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ + DEBUG_PRINT2 (" matched_something=%d", \ + MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT2 (" ever_matched=%d", \ + EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT1 ("\n"); \ + PUSH_FAILURE_ITEM (reg_info[this_reg].word); \ + } \ + \ + DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\ + PUSH_FAILURE_ITEM (lowest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\ + PUSH_FAILURE_ITEM (highest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ + PUSH_FAILURE_ITEM (pattern_place); \ + \ + DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \ + DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ + size2); \ + DEBUG_PRINT1 ("'\n"); \ + PUSH_FAILURE_ITEM (string_place); \ + \ + DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ + DEBUG_PUSH (failure_id); \ + } while (0) + +/* This is the number of items that are pushed and popped on the stack + for each register. */ +#define NUM_REG_ITEMS 3 + +/* Individual items aside from the registers. */ +#ifdef DEBUG +#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */ +#else +#define NUM_NONREG_ITEMS 4 +#endif + +/* We push at most this many items on the stack. */ +#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS) + +/* We actually push this many items. */ +#define NUM_FAILURE_ITEMS \ + ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \ + + NUM_NONREG_ITEMS) + +/* How many items can still be added to the stack without overflowing it. */ +#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) + + +/* Pops what PUSH_FAIL_STACK pushes. + + We restore into the parameters, all of which should be lvalues: + STR -- the saved data position. + PAT -- the saved pattern position. + LOW_REG, HIGH_REG -- the highest and lowest active registers. + REGSTART, REGEND -- arrays of string positions. + REG_INFO -- array of information about each subexpression. + + Also assumes the variables `fail_stack' and (if debugging), `bufp', + `pend', `string1', `size1', `string2', and `size2'. */ + +#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ +{ \ + DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \ + int this_reg; \ + const unsigned char *string_temp; \ + \ + assert (!FAIL_STACK_EMPTY ()); \ + \ + /* Remove failure points and point to how many regs pushed. */ \ + DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ + DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ + DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ + \ + assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ + \ + DEBUG_POP (&failure_id); \ + DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ + \ + /* If the saved string location is NULL, it came from an \ + on_failure_keep_string_jump opcode, and we want to throw away the \ + saved NULL, thus retaining our current position in the string. */ \ + string_temp = POP_FAILURE_ITEM (); \ + if (string_temp != NULL) \ + str = (const char *) string_temp; \ + \ + DEBUG_PRINT2 (" Popping string 0x%x: `", str); \ + DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ + DEBUG_PRINT1 ("'\n"); \ + \ + pat = (unsigned char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ + \ + /* Restore register info. */ \ + high_reg = (unsigned) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \ + \ + low_reg = (unsigned) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \ + \ + for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ + { \ + DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ + \ + reg_info[this_reg].word = POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ + \ + regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + \ + regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + } \ + \ + DEBUG_STATEMENT (nfailure_points_popped++); \ +} /* POP_FAILURE_POINT */ + +/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in + BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible + characters can start a string that matches the pattern. This fastmap + is used by re_search to skip quickly over impossible starting points. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as BUFP->fastmap. + + We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in + the pattern buffer. + + Returns 0 if we succeed, -2 if an internal error. */ + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + int j, k; + fail_stack_type fail_stack; +#ifndef REGEX_MALLOC + char *destination; +#endif + /* We don't push any register information onto the failure stack. */ + unsigned num_regs = 0; + + register char *fastmap = bufp->fastmap; + unsigned char *pattern = bufp->buffer; + unsigned long size = bufp->used; + const unsigned char *p = pattern; + register unsigned char *pend = pattern + size; + + /* Assume that each path through the pattern can be null until + proven otherwise. We set this false at the bottom of switch + statement, to which we get only if a particular path doesn't + match the empty string. */ + boolean path_can_be_null = true; + + /* We aren't doing a `succeed_n' to begin with. */ + boolean succeed_n_p = false; + + assert (fastmap != NULL && p != NULL); + + INIT_FAIL_STACK (); + bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ + bufp->fastmap_accurate = 1; /* It will be when we're done. */ + bufp->can_be_null = 0; + + while (p != pend || !FAIL_STACK_EMPTY ()) + { + if (p == pend) + { + bufp->can_be_null |= path_can_be_null; + + /* Reset for next path. */ + path_can_be_null = true; + + p = fail_stack.stack[--fail_stack.avail]; + } + + /* We should never be about to go beyond the end of the pattern. */ + assert (p < pend); + +#ifdef SWITCH_ENUM_BUG + switch ((int) ((re_opcode_t) *p++)) +#else + switch ((re_opcode_t) *p++) +#endif + { + + /* I guess the idea here is to simply not bother with a fastmap + if a backreference is used, since it's too hard to figure out + the fastmap for the corresponding group. Setting + `can_be_null' stops `re_search_2' from using the fastmap, so + that is all we do. */ + case duplicate: + bufp->can_be_null = 1; + return 0; + + + /* Following are the cases which match a character. These end + with `break'. */ + + case exactn: + fastmap[p[1]] = 1; + break; + + + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + fastmap[j] = 1; + break; + + + case charset_not: + /* Chars beyond end of map must be allowed. */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + fastmap[j] = 1; + break; + + + case wordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == Sword) + fastmap[j] = 1; + break; + + + case notwordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != Sword) + fastmap[j] = 1; + break; + + + case anychar: + /* `.' matches anything ... */ + for (j = 0; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + /* ... except perhaps newline. */ + if (!(bufp->syntax & RE_DOT_NEWLINE)) + fastmap['\n'] = 0; + + /* Return if we have already set `can_be_null'; if we have, + then the fastmap is irrelevant. Something's wrong here. */ + else if (bufp->can_be_null) + return 0; + + /* Otherwise, have to check alternative paths. */ + break; + + +#ifdef emacs + case syntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == (enum syntaxcode) k) + fastmap[j] = 1; + break; + + + case notsyntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != (enum syntaxcode) k) + fastmap[j] = 1; + break; + + + /* All cases after this match the empty string. These end with + `continue'. */ + + + case before_dot: + case at_dot: + case after_dot: + continue; +#endif /* not emacs */ + + + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + case push_dummy_failure: + continue; + + + case jump_n: + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case jump_past_alt: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (j > 0) + continue; + + /* Jump backward implies we just went through the body of a + loop and matched nothing. Opcode jumped to should be + `on_failure_jump' or `succeed_n'. Just treat it like an + ordinary jump. For a * loop, it has pushed its failure + point already; if so, discard that as redundant. */ + if ((re_opcode_t) *p != on_failure_jump + && (re_opcode_t) *p != succeed_n) + continue; + + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + + /* If what's on the stack is where we are now, pop it. */ + if (!FAIL_STACK_EMPTY () + && fail_stack.stack[fail_stack.avail - 1] == p) + fail_stack.avail--; + + continue; + + + case on_failure_jump: + case on_failure_keep_string_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + + /* For some patterns, e.g., `(a?)?', `p+j' here points to the + end of the pattern. We don't want to push such a point, + since when we restore it above, entering the switch will + increment `p' past the end of the pattern. We don't need + to push such a point since we obviously won't find any more + fastmap entries beyond `pend'. Such a pattern can match + the null string, though. */ + if (p + j < pend) + { + if (!PUSH_PATTERN_OP (p + j, fail_stack)) + return -2; + } + else + bufp->can_be_null = 1; + + if (succeed_n_p) + { + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ + succeed_n_p = false; + } + + continue; + + + case succeed_n: + /* Get to the number of times to succeed. */ + p += 2; + + /* Increment p past the n for when k != 0. */ + EXTRACT_NUMBER_AND_INCR (k, p); + if (k == 0) + { + p -= 4; + succeed_n_p = true; /* Spaghetti code alert. */ + goto handle_on_failure_jump; + } + continue; + + + case set_number_at: + p += 4; + continue; + + + case start_memory: + case stop_memory: + p += 2; + continue; + + + default: + abort (); /* We have listed all the cases. */ + } /* switch *p++ */ + + /* Getting here means we have found the possible starting + characters for one path of the pattern -- and that the empty + string does not match. We need not follow this path further. + Instead, look at the next alternative (remembered on the + stack), or quit if no more. The test at the top of the loop + does these things. */ + path_can_be_null = false; + p = pend; + } /* while p */ + + /* Set `can_be_null' for the last path (also the first path, if the + pattern is empty). */ + bufp->can_be_null |= path_can_be_null; + return 0; +} /* re_compile_fastmap */ + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + unsigned num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = (regoff_t) 0; + } +} + +/* Searching routines. */ + +/* Like re_search_2, below, but only one string is specified, and + doesn't let you say where to stop matching. */ + +int +re_search (bufp, string, size, startpos, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, startpos, range; + struct re_registers *regs; +{ + return re_search_2 (bufp, NULL, 0, string, size, startpos, range, + regs, size); +} + + +/* Using the compiled pattern in BUFP->buffer, first tries to match the + virtual concatenation of STRING1 and STRING2, starting first at index + STARTPOS, then at STARTPOS + 1, and so on. + + STRING1 and STRING2 have length SIZE1 and SIZE2, respectively. + + RANGE is how far to scan while trying to match. RANGE = 0 means try + only at STARTPOS; in general, the last start tried is STARTPOS + + RANGE. + + In REGS, return the indices of the virtual concatenation of STRING1 + and STRING2 that matched the entire BUFP->buffer and its contained + subexpressions. + + Do not consider matching one past the index STOP in the virtual + concatenation of STRING1 and STRING2. + + We return either the position in the strings at which the match was + found, -1 if no match, or -2 if error (such as failure + stack overflow). */ + +int +re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int startpos; + int range; + struct re_registers *regs; + int stop; +{ + int val; + register char *fastmap = bufp->fastmap; + register char *translate = bufp->translate; + int total_size = size1 + size2; + int endpos = startpos + range; + + /* Check for out-of-range STARTPOS. */ + if (startpos < 0 || startpos > total_size) + return -1; + + /* Fix up RANGE if it might eventually take us outside + the virtual concatenation of STRING1 and STRING2. */ + if (endpos < -1) + range = -1 - startpos; + else if (endpos > total_size) + range = total_size - startpos; + + /* If the search isn't to be a backwards one, don't waste time in a + search for a pattern that must be anchored. */ + if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0) + { + if (startpos > 0) + return -1; + else + range = 1; + } + + /* Update the fastmap now if not correct already. */ + if (fastmap && !bufp->fastmap_accurate) + if (re_compile_fastmap (bufp) == -2) + return -2; + + /* Loop through the string, looking for a place to start matching. */ + for (;;) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot be the start of a match. If the pattern can match the + null string, however, we don't need to skip characters; we want + the first null string. */ + if (fastmap && startpos < total_size && !bufp->can_be_null) + { + if (range > 0) /* Searching forwards. */ + { + register const char *d; + register int lim = 0; + int irange = range; + + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); + + d = (startpos >= size1 ? string2 - size1 : string1) + startpos; + + /* Written out as an if-else to avoid testing `translate' + inside the loop. */ + if (translate) + while (range > lim + && !fastmap[(unsigned char) + translate[(unsigned char) *d++]]) + range--; + else + while (range > lim && !fastmap[(unsigned char) *d++]) + range--; + + startpos += irange - range; + } + else /* Searching backwards. */ + { + register char c = (size1 == 0 || startpos >= size1 + ? string2[startpos - size1] + : string1[startpos]); + + if (!fastmap[(unsigned char) TRANSLATE (c)]) + goto advance; + } + } + + /* If can't match the null string, and that's all we have left, fail. */ + if (range >= 0 && startpos == total_size && fastmap + && !bufp->can_be_null) + return -1; + + val = re_match_2 (bufp, string1, size1, string2, size2, + startpos, regs, stop); + if (val >= 0) + return startpos; + + if (val == -2) + return -2; + + advance: + if (!range) + break; + else if (range > 0) + { + range--; + startpos++; + } + else + { + range++; + startpos--; + } + } + return -1; +} /* re_search_2 */ + +/* Declarations and macros for re_match_2. */ + +static int bcmp_translate (); +static boolean alt_match_null_string_p (), + common_op_match_null_string_p (), + group_match_null_string_p (); + +/* Structure for per-register (a.k.a. per-group) information. + This must not be longer than one word, because we push this value + onto the failure stack. Other register information, such as the + starting and ending positions (which are addresses), and the list of + inner groups (which is a bits list) are maintained in separate + variables. + + We are making a (strictly speaking) nonportable assumption here: that + the compiler will pack our bit fields into something that fits into + the type of `word', i.e., is something that fits into one item on the + failure stack. */ +typedef union +{ + fail_stack_elt_t word; + struct + { + /* This field is one if this group can match the empty string, + zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ +#define MATCH_NULL_UNSET_VALUE 3 + unsigned match_null_string_p : 2; + unsigned is_active : 1; + unsigned matched_something : 1; + unsigned ever_matched_something : 1; + } bits; +} register_info_type; + +#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) +#define IS_ACTIVE(R) ((R).bits.is_active) +#define MATCHED_SOMETHING(R) ((R).bits.matched_something) +#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) + + +/* Call this when have matched a real character; it sets `matched' flags + for the subexpressions which we are currently inside. Also records + that those subexprs have matched. */ +#define SET_REGS_MATCHED() \ + do \ + { \ + unsigned r; \ + for (r = lowest_active_reg; r <= highest_active_reg; r++) \ + { \ + MATCHED_SOMETHING (reg_info[r]) \ + = EVER_MATCHED_SOMETHING (reg_info[r]) \ + = 1; \ + } \ + } \ + while (0) + + +/* This converts PTR, a pointer into one of the search strings `string1' + and `string2' into an offset from the beginning of that string. */ +#define POINTER_TO_OFFSET(ptr) \ + (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1) + +/* Registers are set to a sentinel when they haven't yet matched. */ +#define REG_UNSET_VALUE ((char *) -1) +#define REG_UNSET(e) ((e) == REG_UNSET_VALUE) + + +/* Macros for dealing with the split strings in re_match_2. */ + +#define MATCHING_IN_FIRST_STRING (dend == end_match_1) + +/* Call before fetching a character with *d. This switches over to + string2 if necessary. */ +#define PREFETCH() \ + while (d == dend) \ + { \ + /* End of string2 => fail. */ \ + if (dend == end_match_2) \ + goto fail; \ + /* End of string1 => advance to string2. */ \ + d = string2; \ + dend = end_match_2; \ + } + + +/* Test if at very beginning or at very end of the virtual concatenation + of `string1' and `string2'. If only one string, it's `string2'. */ +#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) +#define AT_STRINGS_END(d) ((d) == end2) + + +/* Test if D points to a character which is word-constituent. We have + two special cases to check for: if past the end of string1, look at + the first character in string2; and if before the beginning of + string2, look at the last character in string1. */ +#define WORDCHAR_P(d) \ + (SYNTAX ((d) == end1 ? *string2 \ + : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ + == Sword) + +/* Test if the character before D and the one at D differ with respect + to being word-constituent. */ +#define AT_WORD_BOUNDARY(d) \ + (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \ + || WORDCHAR_P (d - 1) != WORDCHAR_P (d)) + + +/* Free everything we malloc. */ +#ifdef REGEX_MALLOC +#define FREE_VAR(var) if (var) free (var); var = NULL +#define FREE_VARIABLES() \ + do { \ + FREE_VAR (fail_stack.stack); \ + FREE_VAR (regstart); \ + FREE_VAR (regend); \ + FREE_VAR (old_regstart); \ + FREE_VAR (old_regend); \ + FREE_VAR (best_regstart); \ + FREE_VAR (best_regend); \ + FREE_VAR (reg_info); \ + FREE_VAR (reg_dummy); \ + FREE_VAR (reg_info_dummy); \ + } while (0) +#else /* not REGEX_MALLOC */ +/* Some MIPS systems (at least) want this to free alloca'd storage. */ +#define FREE_VARIABLES() alloca (0) +#endif /* not REGEX_MALLOC */ + + +/* These values must meet several constraints. They must not be valid + register values; since we have a limit of 255 registers (because + we use only one byte in the pattern for the register number), we can + use numbers larger than 255. They must differ by 1, because of + NUM_FAILURE_ITEMS above. And the value for the lowest register must + be larger than the value for the highest register, so we do not try + to actually save any registers when none are active. */ +#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) +#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) + +/* Matching routines. */ + +#ifndef emacs /* Emacs never uses this. */ +/* re_match is like re_match_2 except it takes only a single string. */ + +int +re_match (bufp, string, size, pos, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, pos; + struct re_registers *regs; + { + return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size); +} +#endif /* not emacs */ + + +/* re_match_2 matches the compiled pattern in BUFP against the + the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 + and SIZE2, respectively). We start matching at POS, and stop + matching at STOP. + + If REGS is non-null and the `no_sub' field of BUFP is nonzero, we + store offsets for the substring each group matched in REGS. See the + documentation for exactly how many groups we fill. + + We return -1 if no match, -2 if an internal error (such as the + failure stack overflowing). Otherwise, we return the length of the + matched substring. */ + +int +re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int stop; +{ + /* General temporaries. */ + int mcnt; + unsigned char *p1; + + /* Just past the end of the corresponding string. */ + const char *end1, *end2; + + /* Pointers into string1 and string2, just past the last characters in + each to consider matching. */ + const char *end_match_1, *end_match_2; + + /* Where we are in the data, and the end of the current string. */ + const char *d, *dend; + + /* Where we are in the pattern, and the end of the pattern. */ + unsigned char *p = bufp->buffer; + register unsigned char *pend = p + bufp->used; + + /* We use this to map every character in the string. */ + char *translate = bufp->translate; + + /* Failure point stack. Each place that can handle a failure further + down the line pushes a failure point on this stack. It consists of + restart, regend, and reg_info for all registers corresponding to + the subexpressions we're currently inside, plus the number of such + registers, and, finally, two char *'s. The first char * is where + to resume scanning the pattern; the second one is where to resume + scanning the strings. If the latter is zero, the failure point is + a ``dummy''; if a failure happens and the failure point is a dummy, + it gets discarded and the next next one is tried. */ + fail_stack_type fail_stack; +#ifdef DEBUG + static unsigned failure_id = 0; + unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0; +#endif + + /* We fill all the registers internally, independent of what we + return, for use in backreferences. The number here includes + an element for register zero. */ + unsigned num_regs = bufp->re_nsub + 1; + + /* The currently active registers. */ + unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG; + unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG; + + /* Information on the contents of registers. These are pointers into + the input strings; they record just what was matched (on this + attempt) by a subexpression part of the pattern, that is, the + regnum-th regstart pointer points to where in the pattern we began + matching and the regnum-th regend points to right after where we + stopped matching the regnum-th subexpression. (The zeroth register + keeps track of what the whole pattern matches.) */ + const char **regstart, **regend; + + /* If a group that's operated upon by a repetition operator fails to + match anything, then the register for its start will need to be + restored because it will have been set to wherever in the string we + are when we last see its open-group operator. Similarly for a + register's end. */ + const char **old_regstart, **old_regend; + + /* The is_active field of reg_info helps us keep track of which (possibly + nested) subexpressions we are currently in. The matched_something + field of reg_info[reg_num] helps us tell whether or not we have + matched any of the pattern so far this time through the reg_num-th + subexpression. These two fields get reset each time through any + loop their register is in. */ + register_info_type *reg_info; + + /* The following record the register info as found in the above + variables when we find a match better than any we've seen before. + This happens as we backtrack through the failure points, which in + turn happens only if we have not yet matched the entire string. */ + unsigned best_regs_set = false; + const char **best_regstart, **best_regend; + + /* Logically, this is `best_regend[0]'. But we don't want to have to + allocate space for that if we're not allocating space for anything + else (see below). Also, we never need info about register 0 for + any of the other register vectors, and it seems rather a kludge to + treat `best_regend' differently than the rest. So we keep track of + the end of the best match so far in a separate variable. We + initialize this to NULL so that when we backtrack the first time + and need to test it, it's not garbage. */ + const char *match_end = NULL; + + /* Used when we pop values we don't care about. */ + const char **reg_dummy; + register_info_type *reg_info_dummy; + +#ifdef DEBUG + /* Counts the total number of registers pushed. */ + unsigned num_regs_pushed = 0; +#endif + + DEBUG_PRINT1 ("\n\nEntering re_match_2.\n"); + + INIT_FAIL_STACK (); + + /* Do not bother to initialize all the register variables if there are + no groups in the pattern, as it takes a fair amount of time. If + there are groups, we include space for register 0 (the whole + pattern), even though we never use it, since it simplifies the + array indexing. We should fix this. */ + if (bufp->re_nsub) + { + regstart = REGEX_TALLOC (num_regs, const char *); + regend = REGEX_TALLOC (num_regs, const char *); + old_regstart = REGEX_TALLOC (num_regs, const char *); + old_regend = REGEX_TALLOC (num_regs, const char *); + best_regstart = REGEX_TALLOC (num_regs, const char *); + best_regend = REGEX_TALLOC (num_regs, const char *); + reg_info = REGEX_TALLOC (num_regs, register_info_type); + reg_dummy = REGEX_TALLOC (num_regs, const char *); + reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type); + + if (!(regstart && regend && old_regstart && old_regend && reg_info + && best_regstart && best_regend && reg_dummy && reg_info_dummy)) + { + FREE_VARIABLES (); + return -2; + } + } +#ifdef REGEX_MALLOC + else + { + /* We must initialize all our variables to NULL, so that + `FREE_VARIABLES' doesn't try to free them. */ + regstart = regend = old_regstart = old_regend = best_regstart + = best_regend = reg_dummy = NULL; + reg_info = reg_info_dummy = (register_info_type *) NULL; + } +#endif /* REGEX_MALLOC */ + + /* The starting position is bogus. */ + if (pos < 0 || pos > size1 + size2) + { + FREE_VARIABLES (); + return -1; + } + + /* Initialize subexpression text positions to -1 to mark ones that no + start_memory/stop_memory has been seen for. Also initialize the + register information struct. */ + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + regstart[mcnt] = regend[mcnt] + = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; + + REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; + IS_ACTIVE (reg_info[mcnt]) = 0; + MATCHED_SOMETHING (reg_info[mcnt]) = 0; + EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0; + } + + /* We move `string1' into `string2' if the latter's empty -- but not if + `string1' is null. */ + if (size2 == 0 && string1 != NULL) + { + string2 = string1; + size2 = size1; + string1 = 0; + size1 = 0; + } + end1 = string1 + size1; + end2 = string2 + size2; + + /* Compute where to stop matching, within the two strings. */ + if (stop <= size1) + { + end_match_1 = string1 + stop; + end_match_2 = string2; + } + else + { + end_match_1 = end1; + end_match_2 = string2 + stop - size1; + } + + /* `p' scans through the pattern as `d' scans through the data. + `dend' is the end of the input string that `d' points within. `d' + is advanced into the following input string whenever necessary, but + this happens before fetching; therefore, at the beginning of the + loop, `d' can be pointing at the end of a string, but it cannot + equal `string2'. */ + if (size1 > 0 && pos <= size1) + { + d = string1 + pos; + dend = end_match_1; + } + else + { + d = string2 + pos - size1; + dend = end_match_2; + } + + DEBUG_PRINT1 ("The compiled pattern is: "); + DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend); + DEBUG_PRINT1 ("The string to match is: `"); + DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2); + DEBUG_PRINT1 ("'\n"); + + /* This loops over pattern commands. It exits by returning from the + function if the match is complete, or it drops through if the match + fails at this starting point in the input data. */ + for (;;) + { + DEBUG_PRINT2 ("\n0x%x: ", p); + + if (p == pend) + { /* End of pattern means we might have succeeded. */ + DEBUG_PRINT1 ("end of pattern ... "); + + /* If we haven't matched the entire string, and we want the + longest match, try backtracking. */ + if (d != end_match_2) + { + DEBUG_PRINT1 ("backtracking.\n"); + + if (!FAIL_STACK_EMPTY ()) + { /* More failure points to try. */ + boolean same_str_p = (FIRST_STRING_P (match_end) + == MATCHING_IN_FIRST_STRING); + + /* If exceeds best match so far, save it. */ + if (!best_regs_set + || (same_str_p && d > match_end) + || (!same_str_p && !MATCHING_IN_FIRST_STRING)) + { + best_regs_set = true; + match_end = d; + + DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); + + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + + /* If no failure points, don't restore garbage. */ + else if (best_regs_set) + { + restore_best_regs: + /* Restore best match. It may happen that `dend == + end_match_1' while the restored d is in string2. + For example, the pattern `x.*y.*z' against the + strings `x-' and `y-z-', if the two strings are + not consecutive in memory. */ + DEBUG_PRINT1 ("Restoring best registers.\n"); + + d = match_end; + dend = ((d >= string1 && d <= end1) + ? end_match_1 : end_match_2); + + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } /* d != end_match_2 */ + + DEBUG_PRINT1 ("Accepting match.\n"); + + /* If caller wants register contents data back, do it. */ + if (regs && !bufp->no_sub) + { + /* Have the register data arrays been allocated? */ + if (bufp->regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. We need one + extra element beyond `num_regs' for the `-1' marker + GNU code uses. */ + regs->num_regs = MAX (RE_NREGS, num_regs + 1); + regs->start = TALLOC (regs->num_regs, regoff_t); + regs->end = TALLOC (regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + return -2; + bufp->regs_allocated = REGS_REALLOCATE; + } + else if (bufp->regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (regs->num_regs < num_regs + 1) + { + regs->num_regs = num_regs + 1; + RETALLOC (regs->start, regs->num_regs, regoff_t); + RETALLOC (regs->end, regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + return -2; + } + } + else + assert (bufp->regs_allocated == REGS_FIXED); + + /* Convert the pointer data in `regstart' and `regend' to + indices. Register zero has to be set differently, + since we haven't kept track of any info for it. */ + if (regs->num_regs > 0) + { + regs->start[0] = pos; + regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1 + : d - string2 + size1); + } + + /* Go through the first `min (num_regs, regs->num_regs)' + registers, since that is all we initialized. */ + for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++) + { + if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt])) + regs->start[mcnt] = regs->end[mcnt] = -1; + else + { + regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]); + regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]); + } + } + + /* If the regs structure we return has more elements than + were in the pattern, set the extra elements to -1. If + we (re)allocated the registers, this is the case, + because we always allocate enough to have at least one + -1 at the end. */ + for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; + } /* regs && !bufp->no_sub */ + + FREE_VARIABLES (); + DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", + nfailure_points_pushed, nfailure_points_popped, + nfailure_points_pushed - nfailure_points_popped); + DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); + + mcnt = d - pos - (MATCHING_IN_FIRST_STRING + ? string1 + : string2 - size1); + + DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); + + return mcnt; + } + + /* Otherwise match next pattern command. */ +#ifdef SWITCH_ENUM_BUG + switch ((int) ((re_opcode_t) *p++)) +#else + switch ((re_opcode_t) *p++) +#endif + { + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case no_op: + DEBUG_PRINT1 ("EXECUTING no_op.\n"); + break; + + + /* Match the next n pattern characters exactly. The following + byte in the pattern defines n, and the n bytes after that + are the characters to match. */ + case exactn: + mcnt = *p++; + DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt); + + /* This is written out as an if-else so we don't waste time + testing `translate' inside the loop. */ + if (translate) + { + do + { + PREFETCH (); + if (translate[(unsigned char) *d++] != (char) *p++) + goto fail; + } + while (--mcnt); + } + else + { + do + { + PREFETCH (); + if (*d++ != (char) *p++) goto fail; + } + while (--mcnt); + } + SET_REGS_MATCHED (); + break; + + + /* Match any character except possibly a newline or a null. */ + case anychar: + DEBUG_PRINT1 ("EXECUTING anychar.\n"); + + PREFETCH (); + + if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n') + || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000')) + goto fail; + + SET_REGS_MATCHED (); + DEBUG_PRINT2 (" Matched `%d'.\n", *d); + d++; + break; + + + case charset: + case charset_not: + { + register unsigned char c; + boolean not = (re_opcode_t) *(p - 1) == charset_not; + + DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : ""); + + PREFETCH (); + c = TRANSLATE (*d); /* The character to match. */ + + /* Cast to `unsigned' instead of `unsigned char' in case the + bit list is a full 32 bytes long. */ + if (c < (unsigned) (*p * BYTEWIDTH) + && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + p += 1 + *p; + + if (!not) goto fail; + + SET_REGS_MATCHED (); + d++; + break; + } + + + /* The beginning of a group is represented by start_memory. + The arguments are the register number in the next byte, and the + number of groups inner to this one in the next. The text + matched within the group is recorded (in the internal + registers data structure) under the register number. */ + case start_memory: + DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]); + + /* Find out if this group can match the empty string. */ + p1 = p; /* To send to group_match_null_string_p. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[*p]) + = group_match_null_string_p (&p1, pend, reg_info); + + /* Save the position in the string where we were the last time + we were at this open-group operator in case the group is + operated upon by a repetition operator, e.g., with `(a*)*b' + against `ab'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regstart[*p]) ? d : regstart[*p] + : regstart[*p]; + DEBUG_PRINT2 (" old_regstart: %d\n", + POINTER_TO_OFFSET (old_regstart[*p])); + + regstart[*p] = d; + DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p])); + + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* This is the new highest active register. */ + highest_active_reg = *p; + + /* If nothing was active before, this is the new lowest active + register. */ + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *p; + + /* Move past the register number and inner group count. */ + p += 2; + break; + + + /* The stop_memory opcode represents the end of a group. Its + arguments are the same as start_memory's: the register + number, and the number of inner groups. */ + case stop_memory: + DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]); + + /* We need to save the string position the last time we were at + this close-group operator in case the group is operated + upon by a repetition operator, e.g., with `((a*)*(b*)*)*' + against `aba'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regend[*p]) ? d : regend[*p] + : regend[*p]; + DEBUG_PRINT2 (" old_regend: %d\n", + POINTER_TO_OFFSET (old_regend[*p])); + + regend[*p] = d; + DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p])); + + /* This register isn't active anymore. */ + IS_ACTIVE (reg_info[*p]) = 0; + + /* If this was the only register active, nothing is active + anymore. */ + if (lowest_active_reg == highest_active_reg) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + { /* We must scan for the new highest active register, since + it isn't necessarily one less than now: consider + (a(b)c(d(e)f)g). When group 3 ends, after the f), the + new highest active register is 1. */ + unsigned char r = *p - 1; + while (r > 0 && !IS_ACTIVE (reg_info[r])) + r--; + + /* If we end up at register zero, that means that we saved + the registers as the result of an `on_failure_jump', not + a `start_memory', and we jumped to past the innermost + `stop_memory'. For example, in ((.)*) we save + registers 1 and 2 as a result of the *, but when we pop + back to the second ), we are at the stop_memory 1. + Thus, nothing is active. */ + if (r == 0) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + highest_active_reg = r; + } + + /* If just failed to match something this time around with a + group that's operated on by a repetition operator, try to + force exit from the ``loop'', and restore the register + information for this group that we had before trying this + last match. */ + if ((!MATCHED_SOMETHING (reg_info[*p]) + || (re_opcode_t) p[-3] == start_memory) + && (p + 2) < pend) + { + boolean is_a_jump_n = false; + + p1 = p + 2; + mcnt = 0; + switch ((re_opcode_t) *p1++) + { + case jump_n: + is_a_jump_n = true; + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (is_a_jump_n) + p1 += 2; + break; + + default: + /* do nothing */ ; + } + p1 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump right before the start_memory + corresponding to this stop_memory, exit from the loop + by forcing a failure after pushing on the stack the + on_failure_jump's jump in the pattern, and d. */ + if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump + && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) + { + /* If this group ever matched anything, then restore + what its registers were before trying this last + failed match, e.g., with `(a*)*b' against `ab' for + regstart[1], and, e.g., with `((a*)*(b*)*)*' + against `aba' for regend[3]. + + Also restore the registers for inner groups for, + e.g., `((a*)(b*))*' against `aba' (register 3 would + otherwise get trashed). */ + + if (EVER_MATCHED_SOMETHING (reg_info[*p])) + { + unsigned r; + + EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* Restore this and inner groups' (if any) registers. */ + for (r = *p; r < *p + *(p + 1); r++) + { + regstart[r] = old_regstart[r]; + + /* xx why this test? */ + if ((int) old_regend[r] >= (int) regstart[r]) + regend[r] = old_regend[r]; + } + } + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + PUSH_FAILURE_POINT (p1 + mcnt, d, -2); + + goto fail; + } + } + + /* Move past the register number and the inner group count. */ + p += 2; + break; + + + /* \ has been turned into a `duplicate' command which is + followed by the numeric value of as the register number. */ + case duplicate: + { + register const char *d2, *dend2; + int regno = *p++; /* Get which register to match against. */ + DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno); + + /* Can't back reference a group which we've never matched. */ + if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) + goto fail; + + /* Where in input to try to start matching. */ + d2 = regstart[regno]; + + /* Where to stop matching; if both the place to start and + the place to stop matching are in the same string, then + set to the place to stop, otherwise, for now have to use + the end of the first string. */ + + dend2 = ((FIRST_STRING_P (regstart[regno]) + == FIRST_STRING_P (regend[regno])) + ? regend[regno] : end_match_1); + for (;;) + { + /* If necessary, advance to next segment in register + contents. */ + while (d2 == dend2) + { + if (dend2 == end_match_2) break; + if (dend2 == regend[regno]) break; + + /* End of string1 => advance to string2. */ + d2 = string2; + dend2 = regend[regno]; + } + /* At end of register contents => success */ + if (d2 == dend2) break; + + /* If necessary, advance to next segment in data. */ + PREFETCH (); + + /* How many characters left in this segment to match. */ + mcnt = dend - d; + + /* Want how many consecutive characters we can match in + one shot, so, if necessary, adjust the count. */ + if (mcnt > dend2 - d2) + mcnt = dend2 - d2; + + /* Compare that many; failure if mismatch, else move + past them. */ + if (translate + ? bcmp_translate (d, d2, mcnt, translate) + : bcmp (d, d2, mcnt)) + goto fail; + d += mcnt, d2 += mcnt; + } + } + break; + + + /* begline matches the empty string at the beginning of the string + (unless `not_bol' is set in `bufp'), and, if + `newline_anchor' is set, after newlines. */ + case begline: + DEBUG_PRINT1 ("EXECUTING begline.\n"); + + if (AT_STRINGS_BEG (d)) + { + if (!bufp->not_bol) break; + } + else if (d[-1] == '\n' && bufp->newline_anchor) + { + break; + } + /* In all other cases, we fail. */ + goto fail; + + + /* endline is the dual of begline. */ + case endline: + DEBUG_PRINT1 ("EXECUTING endline.\n"); + + if (AT_STRINGS_END (d)) + { + if (!bufp->not_eol) break; + } + + /* We have to ``prefetch'' the next character. */ + else if ((d == end1 ? *string2 : *d) == '\n' + && bufp->newline_anchor) + { + break; + } + goto fail; + + + /* Match at the very beginning of the data. */ + case begbuf: + DEBUG_PRINT1 ("EXECUTING begbuf.\n"); + if (AT_STRINGS_BEG (d)) + break; + goto fail; + + + /* Match at the very end of the data. */ + case endbuf: + DEBUG_PRINT1 ("EXECUTING endbuf.\n"); + if (AT_STRINGS_END (d)) + break; + goto fail; + + + /* on_failure_keep_string_jump is used to optimize `.*\n'. It + pushes NULL as the value for the string on the stack. Then + `pop_failure_point' will keep the current value for the + string, instead of restoring it. To see why, consider + matching `foo\nbar' against `.*\n'. The .* matches the foo; + then the . fails against the \n. But the next thing we want + to do is match the \n against the \n; if we restored the + string value, we would be back at the foo. + + Because this is used only in specific cases, we don't need to + check all the things that `on_failure_jump' does, to make + sure the right things get saved on the stack. Hence we don't + share its code. The only reason to push anything on the + stack at all is that otherwise we would have to change + `anychar's code to do something besides goto fail in this + case; that seems worse than this. */ + case on_failure_keep_string_jump: + DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); + + PUSH_FAILURE_POINT (p + mcnt, NULL, -2); + break; + + + /* Uses of on_failure_jump: + + Each alternative starts with an on_failure_jump that points + to the beginning of the next alternative. Each alternative + except the last ends with a jump that in effect jumps past + the rest of the alternatives. (They really jump to the + ending jump of the following alternative, because tensioning + these jumps is a hassle.) + + Repeats start with an on_failure_jump that points past both + the repetition text and either the following jump or + pop_failure_jump back to this on_failure_jump. */ + case on_failure_jump: + on_failure: + DEBUG_PRINT1 ("EXECUTING on_failure_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt); + + /* If this on_failure_jump comes right before a group (i.e., + the original * applied to a group), save the information + for that group and all inner ones, so that if we fail back + to this point, the group's information will be correct. + For example, in \(a*\)*\1, we need the preceding group, + and in \(\(a*\)b*\)\2, we need the inner group. */ + + /* We can't use `p' to check ahead because we push + a failure point to `p + mcnt' after we do this. */ + p1 = p; + + /* We need to skip no_op's before we look for the + start_memory in case this on_failure_jump is happening as + the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 + against aba. */ + while (p1 < pend && (re_opcode_t) *p1 == no_op) + p1++; + + if (p1 < pend && (re_opcode_t) *p1 == start_memory) + { + /* We have a new highest active register now. This will + get reset at the start_memory we are about to get to, + but we will have saved all the registers relevant to + this repetition op, as described above. */ + highest_active_reg = *(p1 + 1) + *(p1 + 2); + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *(p1 + 1); + } + + DEBUG_PRINT1 (":\n"); + PUSH_FAILURE_POINT (p + mcnt, d, -2); + break; + + + /* A smart repeat ends with `maybe_pop_jump'. + We change it to either `pop_failure_jump' or `jump'. */ + case maybe_pop_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt); + { + register unsigned char *p2 = p; + + /* Compare the beginning of the repeat with what in the + pattern follows its end. If we can establish that there + is nothing that they would both match, i.e., that we + would have to backtrack because of (as in, e.g., `a*a') + then we can change to pop_failure_jump, because we'll + never have to backtrack. + + This is not true in the case of alternatives: in + `(a|ab)*' we do need to backtrack to the `ab' alternative + (e.g., if the string was `ab'). But instead of trying to + detect that here, the alternative has put on a dummy + failure point which is what we will end up popping. */ + + /* Skip over open/close-group commands. */ + while (p2 + 2 < pend + && ((re_opcode_t) *p2 == stop_memory + || (re_opcode_t) *p2 == start_memory)) + p2 += 3; /* Skip over args, too. */ + + /* If we're at the end of the pattern, we can change. */ + if (p2 == pend) + { + /* Consider what happens when matching ":\(.*\)" + against ":/". I don't really understand this code + yet. */ + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 + (" End of pattern: change to `pop_failure_jump'.\n"); + } + + else if ((re_opcode_t) *p2 == exactn + || (bufp->newline_anchor && (re_opcode_t) *p2 == endline)) + { + register unsigned char c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; + p1 = p + mcnt; + + /* p1[0] ... p1[2] are the `on_failure_jump' corresponding + to the `maybe_finalize_jump' of this case. Examine what + follows. */ + if ((re_opcode_t) p1[3] == exactn && p1[5] != c) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + + else if ((re_opcode_t) p1[3] == charset + || (re_opcode_t) p1[3] == charset_not) + { + int not = (re_opcode_t) p1[3] == charset_not; + + if (c < (unsigned char) (p1[4] * BYTEWIDTH) + && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + /* `not' is equal to 1 if c would match, which means + that we can't change to pop_failure_jump. */ + if (!not) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + } + } + p -= 2; /* Point at relative address again. */ + if ((re_opcode_t) p[-1] != pop_failure_jump) + { + p[-1] = (unsigned char) jump; + DEBUG_PRINT1 (" Match => jump.\n"); + goto unconditional_jump; + } + /* Note fall through. */ + + + /* The end of a simple repeat has a pop_failure_jump back to + its matching on_failure_jump, where the latter will push a + failure point. The pop_failure_jump takes off failure + points put on by this pop_failure_jump's matching + on_failure_jump; we got through the pattern to here from the + matching on_failure_jump, so didn't fail. */ + case pop_failure_jump: + { + /* We need to pass separate storage for the lowest and + highest registers, even though we don't care about the + actual values. Otherwise, we will restore only one + register from the stack, since lowest will == highest in + `pop_failure_point'. */ + unsigned dummy_low_reg, dummy_high_reg; + unsigned char *pdummy; + const char *sdummy; + + DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); + POP_FAILURE_POINT (sdummy, pdummy, + dummy_low_reg, dummy_high_reg, + reg_dummy, reg_dummy, reg_info_dummy); + } + /* Note fall through. */ + + + /* Unconditionally jump (without popping any failure points). */ + case jump: + unconditional_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */ + DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt); + p += mcnt; /* Do the jump. */ + DEBUG_PRINT2 ("(to 0x%x).\n", p); + break; + + + /* We need this opcode so we can detect where alternatives end + in `group_match_null_string_p' et al. */ + case jump_past_alt: + DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n"); + goto unconditional_jump; + + + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at pop_failure_jump. We will end up at + pop_failure_jump, also, and with a pattern of, say, `a+', we + are skipping over the on_failure_jump, so we have to push + something meaningless for pop_failure_jump to pop. */ + case dummy_failure_jump: + DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n"); + /* It doesn't matter what we push for the string here. What + the code at `fail' tests is the value for the pattern. */ + PUSH_FAILURE_POINT (0, 0, -2); + goto unconditional_jump; + + + /* At the end of an alternative, we need to push a dummy failure + point in case we are followed by a `pop_failure_jump', because + we don't want the failure point for the alternative to be + popped. For example, matching `(a|ab)*' against `aab' + requires that we match the `ab' alternative. */ + case push_dummy_failure: + DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n"); + /* See comments just above at `dummy_failure_jump' about the + two zeroes. */ + PUSH_FAILURE_POINT (0, 0, -2); + break; + + /* Have to succeed matching what follows at least n times. + After that, handle like `on_failure_jump'. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); + + assert (mcnt >= 0); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt > 0) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR (p, mcnt); + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt); + } + else if (mcnt == 0) + { + DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2); + p[2] = (unsigned char) no_op; + p[3] = (unsigned char) no_op; + goto on_failure; + } + break; + + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); + + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER (p + 2, mcnt); + goto unconditional_jump; + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt); + STORE_NUMBER (p1, mcnt); + break; + } + + case wordbound: + DEBUG_PRINT1 ("EXECUTING wordbound.\n"); + if (AT_WORD_BOUNDARY (d)) + break; + goto fail; + + case notwordbound: + DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); + if (AT_WORD_BOUNDARY (d)) + goto fail; + break; + + case wordbeg: + DEBUG_PRINT1 ("EXECUTING wordbeg.\n"); + if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1))) + break; + goto fail; + + case wordend: + DEBUG_PRINT1 ("EXECUTING wordend.\n"); + if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1) + && (!WORDCHAR_P (d) || AT_STRINGS_END (d))) + break; + goto fail; + +#ifdef emacs +#ifdef emacs19 + case before_dot: + DEBUG_PRINT1 ("EXECUTING before_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) >= point) + goto fail; + break; + + case at_dot: + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) != point) + goto fail; + break; + + case after_dot: + DEBUG_PRINT1 ("EXECUTING after_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) <= point) + goto fail; + break; +#else /* not emacs19 */ + case at_dot: + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point) + goto fail; + break; +#endif /* not emacs19 */ + + case syntaxspec: + DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); + mcnt = *p++; + goto matchsyntax; + + case wordchar: + DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n"); + mcnt = (int) Sword; + matchsyntax: + PREFETCH (); + if (SYNTAX (*d++) != (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + + case notsyntaxspec: + DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt); + mcnt = *p++; + goto matchnotsyntax; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n"); + mcnt = (int) Sword; + matchnotsyntax: + PREFETCH (); + if (SYNTAX (*d++) == (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + +#else /* not emacs */ + case wordchar: + DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n"); + PREFETCH (); + if (!WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; + break; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n"); + PREFETCH (); + if (WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; + break; +#endif /* not emacs */ + + default: + abort (); + } + continue; /* Successfully executed one pattern command; keep going. */ + + + /* We goto here if a matching operation fails. */ + fail: + if (!FAIL_STACK_EMPTY ()) + { /* A restart point is known. Restore to that state. */ + DEBUG_PRINT1 ("\nFAIL:\n"); + POP_FAILURE_POINT (d, p, + lowest_active_reg, highest_active_reg, + regstart, regend, reg_info); + + /* If this failure point is a dummy, try the next one. */ + if (!p) + goto fail; + + /* If we failed to the end of the pattern, don't examine *p. */ + assert (p <= pend); + if (p < pend) + { + boolean is_a_jump_n = false; + + /* If failed to a backwards jump that's part of a repetition + loop, need to pop this failure point and use the next one. */ + switch ((re_opcode_t) *p) + { + case jump_n: + is_a_jump_n = true; + case maybe_pop_jump: + case pop_failure_jump: + case jump: + p1 = p + 1; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + + if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n) + || (!is_a_jump_n + && (re_opcode_t) *p1 == on_failure_jump)) + goto fail; + break; + default: + /* do nothing */ ; + } + } + + if (d >= string1 && d <= end1) + dend = end_match_1; + } + else + break; /* Matching at this starting point really fails. */ + } /* for (;;) */ + + if (best_regs_set) + goto restore_best_regs; + + FREE_VARIABLES (); + + return -1; /* Failure to match. */ +} /* re_match_2 */ + +/* Subroutine definitions for re_match_2. */ + + +/* We are passed P pointing to a register number after a start_memory. + + Return true if the pattern up to the corresponding stop_memory can + match the empty string, and false otherwise. + + If we find the matching stop_memory, sets P to point to one past its number. + Otherwise, sets P to an undefined byte less than or equal to END. + + We don't handle duplicates properly (yet). */ + +static boolean +group_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + /* Point to after the args to the start_memory. */ + unsigned char *p1 = *p + 2; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and return true or + false, as appropriate, when we get to one that can't, or to the + matching stop_memory. */ + + switch ((re_opcode_t) *p1) + { + /* Could be either a loop or a series of alternatives. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + /* If the next operation is not a jump backwards in the + pattern. */ + + if (mcnt >= 0) + { + /* Go through the on_failure_jumps of the alternatives, + seeing if any of the alternatives cannot match nothing. + The last alternative starts with only a jump, + whereas the rest start with on_failure_jump and end + with a jump, e.g., here is the pattern for `a|b|c': + + /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6 + /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3 + /exactn/1/c + + So, we have to first go through the first (n-1) + alternatives and then deal with the last one separately. */ + + + /* Deal with the first (n-1) alternatives, which start + with an on_failure_jump (see above) that jumps to right + past a jump_past_alt. */ + + while ((re_opcode_t) p1[mcnt-3] == jump_past_alt) + { + /* `mcnt' holds how many bytes long the alternative + is, including the ending `jump_past_alt' and + its number. */ + + if (!alt_match_null_string_p (p1, p1 + mcnt - 3, + reg_info)) + return false; + + /* Move to right after this alternative, including the + jump_past_alt. */ + p1 += mcnt; + + /* Break if it's the beginning of an n-th alternative + that doesn't begin with an on_failure_jump. */ + if ((re_opcode_t) *p1 != on_failure_jump) + break; + + /* Still have to check that it's not an n-th + alternative that starts with an on_failure_jump. */ + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if ((re_opcode_t) p1[mcnt-3] != jump_past_alt) + { + /* Get to the beginning of the n-th alternative. */ + p1 -= 3; + break; + } + } + + /* Deal with the last alternative: go back and get number + of the `jump_past_alt' just before it. `mcnt' contains + the length of the alternative. */ + EXTRACT_NUMBER (mcnt, p1 - 2); + + if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info)) + return false; + + p1 += mcnt; /* Get past the n-th alternative. */ + } /* if mcnt > 0 */ + break; + + + case stop_memory: + assert (p1[1] == **p); + *p = p1 + 2; + return true; + + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return false; +} /* group_match_null_string_p */ + + +/* Similar to group_match_null_string_p, but doesn't deal with alternatives: + It expects P to be the first byte of a single alternative and END one + byte past the last. The alternative can contain groups. */ + +static boolean +alt_match_null_string_p (p, end, reg_info) + unsigned char *p, *end; + register_info_type *reg_info; +{ + int mcnt; + unsigned char *p1 = p; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and break when we get + to one that can't. */ + + switch ((re_opcode_t) *p1) + { + /* It's a loop. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + break; + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return true; +} /* alt_match_null_string_p */ + + +/* Deals with the ops common to group_match_null_string_p and + alt_match_null_string_p. + + Sets P to one after the op and its arguments, if any. */ + +static boolean +common_op_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + boolean ret; + int reg_no; + unsigned char *p1 = *p; + + switch ((re_opcode_t) *p1++) + { + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbeg: + case wordend: + case wordbound: + case notwordbound: +#ifdef emacs + case before_dot: + case at_dot: + case after_dot: +#endif + break; + + case start_memory: + reg_no = *p1; + assert (reg_no > 0 && reg_no <= MAX_REGNUM); + ret = group_match_null_string_p (&p1, end, reg_info); + + /* Have to set this here in case we're checking a group which + contains a group and a back reference to it. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret; + + if (!ret) + return false; + break; + + /* If this is an optimized succeed_n for zero times, make the jump. */ + case jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (mcnt >= 0) + p1 += mcnt; + else + return false; + break; + + case succeed_n: + /* Get to the number of times to succeed. */ + p1 += 2; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + if (mcnt == 0) + { + p1 -= 4; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + } + else + return false; + break; + + case duplicate: + if (!REG_MATCH_NULL_STRING_P (reg_info[*p1])) + return false; + break; + + case set_number_at: + p1 += 4; + + default: + /* All other opcodes mean we cannot match the empty string. */ + return false; + } + + *p = p1; + return true; +} /* common_op_match_null_string_p */ + + +/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN + bytes; nonzero otherwise. */ + +static int +bcmp_translate (s1, s2, len, translate) + unsigned char *s1, *s2; + register int len; + char *translate; +{ + register unsigned char *p1 = s1, *p2 = s2; + while (len) + { + if (translate[*p1++] != translate[*p2++]) return 1; + len--; + } + return 0; +} + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length SIZE) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. + + We call regex_compile to do the actual compilation. */ + +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + int length; + struct re_pattern_buffer *bufp; +{ + reg_errcode_t ret; + + /* GNU code is written to assume at least RE_NREGS registers will be set + (and at least one extra will be -1). */ + bufp->regs_allocated = REGS_UNALLOCATED; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub. */ + bufp->no_sub = 0; + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = regex_compile (pattern, length, re_syntax_options, bufp); + + return re_error_msg[(int) ret]; +} + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them if this is an Emacs or POSIX compilation. */ + +#if !defined (emacs) && !defined (_POSIX_SOURCE) + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + + if (!s) + { + if (!re_comp_buf.buffer) + return "No previous regular expression"; + return 0; + } + + if (!re_comp_buf.buffer) + { + re_comp_buf.buffer = (unsigned char *) malloc (200); + if (re_comp_buf.buffer == NULL) + return "Memory exhausted"; + re_comp_buf.allocated = 200; + + re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH); + if (re_comp_buf.fastmap == NULL) + return "Memory exhausted"; + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf); + + /* Yes, we're discarding `const' here. */ + return (char *) re_error_msg[(int) ret]; +} + + +int +re_exec (s) + const char *s; +{ + const int len = strlen (s); + return + 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0); +} +#endif /* not emacs and not _POSIX_SOURCE */ + +/* POSIX.2 functions. Don't define these for Emacs. */ + +#ifndef emacs + +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' and `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *preg; + const char *pattern; + int cflags; +{ + reg_errcode_t ret; + unsigned syntax + = (cflags & REG_EXTENDED) ? + RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC; + + /* regex_compile will allocate the space for the compiled pattern. */ + preg->buffer = 0; + preg->allocated = 0; + + /* Don't bother to use a fastmap when searching. This simplifies the + REG_NEWLINE case: if we used a fastmap, we'd have to put all the + characters after newlines into the fastmap. This way, we just try + every character. */ + preg->fastmap = 0; + + if (cflags & REG_ICASE) + { + unsigned i; + + preg->translate = (char *) malloc (CHAR_SET_SIZE); + if (preg->translate == NULL) + return (int) REG_ESPACE; + + /* Map uppercase characters to corresponding lowercase ones. */ + for (i = 0; i < CHAR_SET_SIZE; i++) + preg->translate[i] = ISUPPER (i) ? tolower (i) : i; + } + else + preg->translate = NULL; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + + preg->no_sub = !!(cflags & REG_NOSUB); + + /* POSIX says a null character in the pattern terminates it, so we + can use strlen here in compiling the pattern. */ + ret = regex_compile (pattern, strlen (pattern), syntax, preg); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) ret = REG_EPAREN; + + return (int) ret; +} + + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *preg; + const char *string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; +{ + int ret; + struct re_registers regs; + regex_t private_preg; + int len = strlen (string); + boolean want_reg_info = !preg->no_sub && nmatch > 0; + + private_preg = *preg; + + private_preg.not_bol = !!(eflags & REG_NOTBOL); + private_preg.not_eol = !!(eflags & REG_NOTEOL); + + /* The user has told us exactly how many registers to return + information about, via `nmatch'. We have to pass that on to the + matching routines. */ + private_preg.regs_allocated = REGS_FIXED; + + if (want_reg_info) + { + regs.num_regs = nmatch; + regs.start = TALLOC (nmatch, regoff_t); + regs.end = TALLOC (nmatch, regoff_t); + if (regs.start == NULL || regs.end == NULL) + return (int) REG_NOMATCH; + } + + /* Perform the searching operation. */ + ret = re_search (&private_preg, string, len, + /* start: */ 0, /* range: */ len, + want_reg_info ? ®s : (struct re_registers *) 0); + + /* Copy the register information to the POSIX structure. */ + if (want_reg_info) + { + if (ret >= 0) + { + unsigned r; + + for (r = 0; r < nmatch; r++) + { + pmatch[r].rm_so = regs.start[r]; + pmatch[r].rm_eo = regs.end[r]; + } + } + + /* If we needed the temporary register info, free the space now. */ + free (regs.start); + free (regs.end); + } + + /* We want zero return to mean success, unlike `re_search'. */ + return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH; +} + + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +size_t +regerror (errcode, preg, errbuf, errbuf_size) + int errcode; + const regex_t *preg; + char *errbuf; + size_t errbuf_size; +{ + const char *msg; + size_t msg_size; + + if (errcode < 0 + || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0]))) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = re_error_msg[errcode]; + + /* POSIX doesn't require that we do anything in this case, but why + not be nice. */ + if (! msg) + msg = "Success"; + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (errbuf_size != 0) + { + if (msg_size > errbuf_size) + { + strncpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; + } + else + strcpy (errbuf, msg); + } + + return msg_size; +} + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + if (preg->buffer != NULL) + free (preg->buffer); + preg->buffer = NULL; + + preg->allocated = 0; + preg->used = 0; + + if (preg->fastmap != NULL) + free (preg->fastmap); + preg->fastmap = NULL; + preg->fastmap_accurate = 0; + + if (preg->translate != NULL) + free (preg->translate); + preg->translate = NULL; +} + +#endif /* not emacs */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/Utils/Libs/GLib/REGEX.H b/Utils/Libs/GLib/REGEX.H new file mode 100644 index 000000000..d58cd25c2 --- /dev/null +++ b/Utils/Libs/GLib/REGEX.H @@ -0,0 +1,493 @@ +/* Definitions for data structures and routines for the regular + expression library, version 0.12. + + Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define __STDC__ 1 +#define STDC_HEADERS 1 +#define REGEX_MALLOC + +#ifndef __REGEXP_LIBRARY_H__ +#define __REGEXP_LIBRARY_H__ + +/* POSIX says that must be included (by the caller) before + . */ + +#ifdef VMS +/* VMS doesn't have `size_t' in , even though POSIX says it + should be there. */ +#include +#endif + + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS (1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS + replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +/* Maximum number of duplicates an interval can allow. Some systems + (erroneously) define this in other header files, but we want our + value, so remove any previous define. */ +#ifdef RE_DUP_MAX +#undef RE_DUP_MAX +#endif +#define RE_DUP_MAX ((1 << 15) - 1) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Not implemented. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +struct re_pattern_buffer +{ +/* [[[begin pattern_buffer]]] */ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + char *translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + + +/* search.c (search_buffer) in Emacs needs this one opcode value. It is + defined both in `regex.c' and here. */ +#define RE_EXACTN_VALUE 1 + +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +#define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* To avoid duplicating every routine declaration -- once with a + prototype (if we are ANSI), and once without (if we aren't) -- we + use the following macro to declare argument types. This + unfortunately clutters up the declarations a bit, but I think it's + worth it. */ + +#if __STDC__ + +#define _RE_ARGS(args) args + +#else /* not __STDC__ */ + +#define _RE_ARGS(args) () + +#endif /* not __STDC__ */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern + _RE_ARGS ((const char *pattern, int length, + struct re_pattern_buffer *buffer)); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, struct re_registers *regs)); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, int stop)); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs)); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop)); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers + _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, + unsigned num_regs, regoff_t *starts, regoff_t *ends)); + +/* 4.2 bsd compatibility. */ +extern char *re_comp _RE_ARGS ((const char *)); +extern int re_exec _RE_ARGS ((const char *)); + +/* POSIX compatibility. */ +extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags)); +extern int regexec + _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch, + regmatch_t pmatch[], int eflags)); +extern size_t regerror + _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf, + size_t errbuf_size)); +extern void regfree _RE_ARGS ((regex_t *preg)); + +#endif /* not __REGEXP_LIBRARY_H__ */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/Utils/Libs/GLib/chartables.c b/Utils/Libs/GLib/chartables.c new file mode 100644 index 000000000..7bd4e7775 --- /dev/null +++ b/Utils/Libs/GLib/chartables.c @@ -0,0 +1,146 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file is automatically written by the makechartables auxiliary +program. If you edit it by hand, you might like to edit the Makefile to +prevent its ever being regenerated. + +This file is #included in the compilation of pcre.c to build the default +character tables which are used when no tables are passed to the compile +function. */ + +static unsigned char pcre_default_tables[] = { + +/* This table is a lower casing table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table is a case flipping table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table contains bit maps for digits, 'word' chars, and white +space. Each map is 32 bytes long and the bits run from the least +significant end of each byte. */ + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x02 letter + 0x04 decimal digit + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' + 0x80 regular expression metacharacter or binary zero +*/ + + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ + 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ + 0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /* X - _ */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +/* End of chartables.c */ diff --git a/Utils/Libs/GLib/gal.c b/Utils/Libs/GLib/gal.c new file mode 100644 index 000000000..7b94e8a24 --- /dev/null +++ b/Utils/Libs/GLib/gal.c @@ -0,0 +1,2652 @@ +/* =========================================================================== + File: GAL.C + + Notes: Memory allocation \ deallocation Module + + Author: G Robert Liddon @ 73b + + Version: 1.1 + + Copyright (C) 1996 DCI Ltd All rights reserved. + ============================================================================ */ + +/* --------------------------------------------------------------------------- + Includes + -------- */ + +/* Standard Library + ---------------- */ +#include "stdio.h" + +/* Glib + ---- */ +#include "gal.h" +#include "gdebug.h" +#include "tick.h" + +/* --------------------------------------------------------------------------- + Defines + ------- */ +#define MAX_MEM_BLOCKS 400 /* Maximum amount of memory blocks */ +#define VER_STR "1.4" + +#define MAX_NAME_SIZE 8 + +/* --------------------------------------------------------------------------- + Structure Declarations + ---------------------- */ + +/* Description of an area of memory, each free or alloced block has one + -------------------------------------------------------------------- */ +typedef struct MEM_HDR +{ + struct MEM_HDR * Prev; /* Previous block in list */ + struct MEM_HDR * Next; /* Next block in this list */ + + void * Mem; /* Mem owned by this block */ + u32 Size; /* Size of it */ + + u16 TimeStamp; /* A time stamp */ + u16 Type; /* Unique mem type of this block */ + u16 Owners; /* How many owners this block has */ + + U16 Handle; /* Handle for this block */ + +#ifdef __GL_DEBUG__ + u8 Name[MAX_NAME_SIZE]; +#endif + +} MEM_HDR; + + +/* Description of a region of mem + ------------------------------ */ +typedef struct MEM_REG +{ + void * Mem; + int Size; + +} MEM_REG; + +/* --------------------------------------------------------------------------- + Typedefs + -------- */ +typedef MEM_HDR * (*FIND_ROUTINE)(MEM_HDR *Head, U32 Size); + +/* --------------------------------------------------------------------------- + Module Variables + ---------------- */ +static MEM_INIT_INFO * MemInitBlocks; /* Head of list of descriptions of different types of mem */ +static MEM_HDR MemHdrBlocks[MAX_MEM_BLOCKS]; /* Memory Header Blocks */ +static MEM_HDR * FreeBlocks; /* List of unused header blocks */ +static GAL_ERROR_CODE LastError; /* Last error that happened */ +static int TimeStamp; /* Blocks alloced are stamped with this time */ +static BOOL FullErrorChecking; /* Perform full error and consistancy cheaking on mem areas after defrags */ +static U32 LastAttemptedAlloc; /* Last alloc we tried */ +static U32 LastDeallocedBlock; /* Last alloc we tried */ +static GAL_VERB_LEV VerbLev; /* How noisy gal is */ +static int NumOfFreeHdrs; /* How many headers are free */ +static u32 LastTypeAlloced; +static GAL_FILTER AllocFilter; +static BOOL HaltOnError; /* Set to true if we want GAL to halt if there is an error */ + + +/* --------------------------------------------------------------------------- + Function Prototypes for internal functions + ------------------------------------------ */ +static void GraftMemHdrList(MEM_HDR ** ToList,MEM_HDR ** FromList); +static MEM_HDR * FindNextBlock(void *Addr,MEM_HDR * Blocks); +static BOOL CollideRegions(MEM_REG *Reg1,MEM_REG *Reg2); +static U32 ShuffleBlocks(MEM_HDR *Blocks,MEM_REG *Reg,MEM_INIT_INFO *M); +static void PutBlocksInRegionIntoList(MEM_REG *Reg,MEM_HDR **ToList,MEM_HDR **FromList); +static BOOL GazDefragMem(U32 MemType); +static void DeleteEmptyBlocks(MEM_INIT_INFO *M); +static BOOL GetRegion(MEM_REG *Reg,MEM_HDR * LockedBlocks,MEM_INIT_INFO *M); +static void SortMemHdrListByAddr(MEM_HDR **Head); +static void PutAllLockedBlocksOntoList(MEM_HDR **ToHead,MEM_HDR **FromHead); + +static void AttachHdrToList(MEM_HDR **Head,MEM_HDR *Block); +static void DetachHdrFromList(MEM_HDR **Head,MEM_HDR *Block); +static BOOL IsActiveValidHandle(MHANDLE Handle); +static void * AlignPtr(void *P,U32 Align); +static U32 AlignSize(U32 Size,U32 Align); +static void MergeToEmptyList(MEM_INIT_INFO *MI,MEM_HDR *M); +static MHANDLE LoAlloc(MEM_INIT_INFO *M,MEM_HDR *Block,void *Addr,U32 Size,const char *Name); +static MEM_HDR * GetFreeMemHdrBlock(void); +static void ReleaseMemHdrBlock(MEM_HDR * Hdr); +static MEM_HDR * FindBlockInTheseBounds(MEM_HDR *Head,void *Addr,U32 Size); + +static BOOL CheckCollisions(MEM_INIT_INFO * M,MEM_HDR *MemHdr); +static BOOL AreBlocksColliding(MEM_HDR *Hdr1,MEM_HDR *Hdr2); + +static BOOL GSetError(GAL_ERROR_CODE Err); + +static MEM_INIT_INFO * GetMemInitInfoBlockFromType(U32 Type); + +static MEM_HDR * FindHighestMemBlock(MEM_HDR *Head, U32 Size); + MEM_HDR * FindClosestSizedBlock(MEM_HDR *Head, U32 Size); +static MEM_HDR * FindLowestMemBlock(MEM_HDR *Head, U32 Size); + +static int CountFreeBlocks(void); +static void SetBlockName(MEM_HDR * MemHdr,const char * NewName); +static char const * GetBlockName(MEM_HDR * MemHdr); + +static BOOL SortAddr(MEM_HDR *B1,MEM_HDR * B2); +static BOOL SortSize(MEM_HDR *B1,MEM_HDR * B2); +static void SortMemHdrList(MEM_HDR **Head,BOOL (*CompFunc)(MEM_HDR *B1,MEM_HDR * B2)); + + +/* --------------------------------------------------------------------------- + Tables + ------ */ +char *GalErrors[]= +{ + NULL, + "Run out of mem handles", + "Mem type already existed", + "Mem type added overlaps previously defined one", + "Invalid mem type", + "Invalid handle", + "Handle already unlocked", + "Memory blocks in type overlap", + "Not all memory of type covered", + "Defrag attempted but no mem functions available", + "Not enough memory to allocate block", +}; + + +/* Phantom mem you want a MHANDLE but is actually static (Not in yet) + ------------------------------------------------------------------ */ +MEM_INIT_INFO PhantomMem= +{ + NULL, + 0, + GAL_PHANTOM_MEM, +}; + + +/* --------------------------------------------------------------------------- + Function: void GAL_SetErrorChecking(BOOL OnOff) + + Purpose: Set error checking of mem regions after defrag + + Params: OnOff = what to do + + --------------------------------------------------------------------------- */ +void GAL_SetErrorChecking(BOOL OnOff) +{ + FullErrorChecking=OnOff; +} + +/* --------------------------------------------------------------------------- + Function: MHANDLE GAL_SplitBlock(MHANDLE CurBlock,U32 Split) + + Purpose: Split a Block into two. If Split point outside + block to be split then this will return an error. + Split is rounded rounded up to be on mem type alignment + boundary + + Params: CurBlock = block to split + Size = New Size + + Returns: Handle of second block else + NULL_HANDLE if unsucessful + + --------------------------------------------------------------------------- */ +MHANDLE GAL_SplitBlock(MHANDLE CurBlock,U32 Size) +{ + MEM_INIT_INFO * M; + MEM_HDR * MemHdr; + MEM_HDR * SplitBlock; + + if (!IsActiveValidHandle(CurBlock)) + { + GSetError(ERR_GAL_INVALID_MEM_HANDLE); + return NULL_HANDLE; + } + + MemHdr=&MemHdrBlocks[CurBlock]; + + if (!(M=GetMemInitInfoBlockFromType(MemHdr->Type))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return NULL_HANDLE; + } + + + /* Round up split to size lie on mem types alignment & Check to see if it's not too big */ + Size=AlignSize(Size,M->Alignment); + + if (Size >= MemHdr->Size) + return NULL_HANDLE; + + /* Get a free block to put split into */ + + if (!(SplitBlock=GetFreeMemHdrBlock())) + { + GSetError(ERR_RUN_OUT_OF_MEM_HDRS); + return NULL_HANDLE; + } + + /* Fill it up with the correct details */ + SplitBlock->Type=MemHdr->Type; + SplitBlock->Owners=1; + SplitBlock->Mem=(void *)((U32)(MemHdr->Mem)+Size); + SplitBlock->Size=MemHdr->Size-Size; + SplitBlock->TimeStamp=MemHdr->TimeStamp; + SetBlockName(SplitBlock,GetBlockName(MemHdr)); + + /* And add to the list of used blocks */ + + AttachHdrToList(&M->Used,SplitBlock); + + /* Now Adjust Block that's been split */ + MemHdr->Size=Size; + + /* Return handle of the split block */ + return SplitBlock->Handle; + +} + +/* --------------------------------------------------------------------------- + Function: void GAL_InitModule(void); + + Purpose: Initialise memory manager + --------------------------------------------------------------------------- */ +void GAL_InitModule(void) +{ + int f; + MemInitBlocks=NULL; + FreeBlocks=NULL; + NumOfFreeHdrs=0; + + GAL_SetVerbosity(GAL_SILENT); + + GAL_SetTimeStamp(0); + + LastError=ERR_GAL_NO_ERROR; + + GAL_SetErrorChecking(FALSE); + GAL_ReturnOnError(); + + for (f=0;f to structure describing new mem type + + Returns: FALSE if there was an error + + --------------------------------------------------------------------------- */ +BOOL GAL_AddMemType(MEM_INIT_INFO *M) +{ + MEM_INIT_INFO * P; + MEM_HDR * FreeMemHdr; + U32 Addr1; + U32 Addr2; + + /* Scan through any memory regions GAL has in it's charge to + see if there's any conflict with the block we want to add */ + + P=MemInitBlocks; + + while (P) + { + /* See if this block overlaps any others */ + + Addr1=(U32) M->Mem; + Addr2=(U32) P->Mem; + + if (Addr1 < (Addr2+P->Size)) + { + if (Addr1+M->Size >= Addr2) + { + GSetError(ERR_GAL_MEM_TYPE_OVERLAP); + return FALSE; + } + } + + /* Check to see if there's a memory region already designated of + this type */ + if (P->Type == M->Type) + return ERR_GAL_MEM_TYPE_EXISTS; + + /* Next Block */ + P=P->NextInitBlock; + } + + /* Add to the list of memory regions */ + + M->NextInitBlock=MemInitBlocks; + MemInitBlocks=M; + + /* Initialise Info Block */ + + M->Flags=0; + M->Used=NULL; + M->Empty=NULL; + + /* Get A Mem HDR for the free region if we can */ + + FreeMemHdr=GetFreeMemHdrBlock(); + + if (!FreeMemHdr) + { + GSetError(ERR_RUN_OUT_OF_MEM_HDRS); + return FALSE; + } + + /* Set empty region to be this mem header */ + + AttachHdrToList(&M->Empty,FreeMemHdr); + + /* Init this mem hdr as the empty region */ + + FreeMemHdr->Mem=AlignPtr(M->Mem,M->Alignment); + Addr1=(U32)FreeMemHdr->Mem-(U32)M->Mem; + FreeMemHdr->Size=M->Size-Addr1; + + /* Return without an error */ + + return TRUE; +} + +/* --------------------------------------------------------------------------- + Function: MHANDLE GAL_Alloc(U32 MemNeeded,U32 Type,const char *Name) + + Purpose: Alloc some memory + + Parms: MemNeeded = Amount Wanted + Type = Type of mem wanted + Name = Name of mem block (NULL if no name wanted) + + Returns: A handle to memory if succesful + NULL_HANDLE if failed + --------------------------------------------------------------------------- */ +MHANDLE GAL_Alloc(U32 Size,U32 Type,const char *Name) +{ + MEM_HDR * Block; + MEM_INIT_INFO * M; + U32 FullSize; + FIND_ROUTINE FRoute; + BOOL High; + + + High = (Type&GAL_HIGH) == GAL_HIGH; + + Type&=~GAL_FLAGS; + + if (High) + FRoute=FindHighestMemBlock; + else + FRoute=FindLowestMemBlock; + + LastAttemptedAlloc=Size; + LastTypeAlloced=Type; + + /* Find out the full mem aligned size of this chunk */ + + /* If this is a valid memory type get info about this mem type */ + + if (!(M=GetMemInitInfoBlockFromType(Type))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return NULL_HANDLE; + } + + + FullSize=AlignSize(Size,M->Alignment); + + if (AllocFilter) + AllocFilter(Type,FullSize,Name); + + /* Find a smallest block in memory pool that is big enough in size and error if not*/ + + if (!(Block=FRoute(M->Empty,FullSize))) + { + /* Oh dear couldn't alloc, is it worth trying a defrag? */ + + if (FullSize<=GAL_GetFreeMem(Type)) + { + /* Yes, it could be worth it so lets try */ + + if (!GAL_DefragMem(Type)) + return NULL_HANDLE; + else + { + /* Sucessfully defraged, now try again */ + + if (!(Block=FRoute(M->Empty,FullSize))) + { + GSetError(ERR_GAL_NOT_ENOUGH_MEM); + return NULL_HANDLE; + } + } + } + else + { + GSetError(ERR_GAL_NOT_ENOUGH_MEM); + return NULL_HANDLE; + } + } + + /* Take it out of the empty list */ + DetachHdrFromList(&M->Empty,Block); + + /* No Do the grunt work */ + + if (High) + { + u8 * BaseAddr; + + BaseAddr=(void*)((u32) Block->Mem + Block->Size-FullSize); + + return LoAlloc(M,Block,BaseAddr,Size,Name); + } + else + return LoAlloc(M,Block,Block->Mem,Size,Name); +} + + +/* --------------------------------------------------------------------------- + Function: UINT GAL_GetMemSize(MHANDLE Handle) + + Purpose: Find out how much memory is attached to this handle + + Parms: MHANDLE Handle = handle of allocated memory + + Returns: Size of block (not neccesarily the allocated size, but the rounded + up according to the memory region it was allocated in size ) + --------------------------------------------------------------------------- */ +UINT GAL_GetMemSize(MHANDLE Handle) +{ + MEM_HDR *MemHdr; + + if (!IsActiveValidHandle(Handle)) + { + GSetError(ERR_GAL_INVALID_MEM_HANDLE); + return 0; + } + + MemHdr=&MemHdrBlocks[Handle]; + + return(MemHdr->Size); +} + +/* --------------------------------------------------------------------------- + Function: void *GAL_Lock(MHANDLE Handle) + + Purpose: Lock some alloced memory + + Parms: MHANDLE Handle = handle to alloced memory + + Returns: A handle to memory if succesful + NULL_HANDLE if failed + --------------------------------------------------------------------------- */ +void *GAL_Lock(MHANDLE Handle) +{ + MEM_HDR *MemHdr; + + /* check to see if this is a valid handle, error if not */ + + if (!IsActiveValidHandle(Handle)) + { + GSetError(ERR_GAL_INVALID_MEM_HANDLE); + return NULL; + } + + /* get ptr to memory block */ + + MemHdr=&MemHdrBlocks[Handle]; + + /* Everything ok so bump up the amount of owners */ + + MemHdr->Owners++; + + /* return pointer to blocks memory */ + + return MemHdr->Mem; +} + +/* --------------------------------------------------------------------------- + Function: void *GAL_Unlock(MHANDLE Handle) + + Purpose: Unlock previously locked memory + + Parms: MHANDLE Handle to mem block + + Returns: TRUE if succesfully unlocked + FALSE if not + --------------------------------------------------------------------------- */ +BOOL GAL_Unlock(MHANDLE Handle) +{ + MEM_HDR *MemHdr; + + /* check to see if this is a valid handle, error if not */ + + if (!IsActiveValidHandle(Handle)) + { + GSetError(ERR_GAL_INVALID_MEM_HANDLE); + return FALSE; + } + + /* get ptr to memory block */ + MemHdr=&MemHdrBlocks[Handle]; + + /* If this mem block has no owners error */ + + if (!MemHdr->Owners) + { + GSetError(ERR_GAL_MEM_ALREADY_UNLOCKED); + return FALSE; + } + + + /* Decrease amount of owners */ + + MemHdr->Owners--; + + /* Complete Succesfully */ + + return TRUE; + +} + +/* --------------------------------------------------------------------------- + Function: BOOL GAL_Free(MHANDLE Handle); + + Purpose: Free up previously allocated memory + + Parms: MEMINFO *Mem -> Linked list of memory information blocks + + Returns: How succesful it all was + --------------------------------------------------------------------------- */ +BOOL GAL_Free(MHANDLE Handle) +{ + MEM_INIT_INFO * M; + MEM_HDR * MemHdr; + + /* check to see if this is a valid handle, error if not */ + + if (!IsActiveValidHandle(Handle)) + { + GSetError(ERR_GAL_INVALID_MEM_HANDLE); + return FALSE; + } + + + /* get ptr to memory block */ + + MemHdr=&MemHdrBlocks[Handle]; + + /* Get -> to memory region structure this block belongs to and error if there wasn't one */ + + if (!(M=GetMemInitInfoBlockFromType(MemHdr->Type))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return FALSE; + } + + + /* Report what amount of mem this was being freed */ + + LastDeallocedBlock=MemHdr->Size; + + /* Take mem block from used list */ + + DetachHdrFromList(&M->Used,MemHdr); + + /* Merge into the empty blocks list */ + + MergeToEmptyList(M,MemHdr); + + return TRUE; +} + +/* --------------------------------------------------------------------------- + Function: U32 GAL_GetFreeMem(U32 Type) + + Purpose: Return how much free mem this mem type has + + Parms: U32 Type = memory type to do + + Returns: Total size of free mem + + --------------------------------------------------------------------------- */ +U32 GAL_GetFreeMem(U32 Type) +{ + U32 FreeMem; + MEM_INIT_INFO * M; + + Type&=~GAL_FLAGS; + + FreeMem=0; + + if ((M=GetMemInitInfoBlockFromType(Type))) + { + MEM_HDR * Block; + + Block=M->Empty; + + while (Block) + { + FreeMem+=Block->Size; + Block=Block->Next; + } + } + else + GSetError(ERR_GAL_INVALID_MEM_TYPE); + + return(FreeMem); +} + + +/* --------------------------------------------------------------------------- + Function: U32 GAL_GetFreeMem(U32 Type) + + Purpose: Return how much free mem this mem type has + + Parms: U32 Type = memory type to do + + Returns: Total size of free mem + + --------------------------------------------------------------------------- */ +U32 GAL_GetUsedMem(U32 Type) +{ + U32 FreeMem; + MEM_INIT_INFO * M; + + Type&=~GAL_FLAGS; + + FreeMem=0; + + if ((M=GetMemInitInfoBlockFromType(Type))) + { + MEM_HDR * Block; + + Block=M->Used; + + while (Block) + { + FreeMem+=Block->Size; + Block=Block->Next; + } + } + else + GSetError(ERR_GAL_INVALID_MEM_TYPE); + + return(FreeMem); +} + + + +/* --------------------------------------------------------------------------- + Function: U32 GAL_LargestFreeBlock(U32 Type) + + Purpose: Return the largest block of mem available + + Parms: U32 Type = memory type to do + + Returns: Size of largest mem + + --------------------------------------------------------------------------- */ +U32 GAL_LargestFreeBlock(U32 Type) +{ + U32 Largest; + MEM_HDR * Index; + MEM_INIT_INFO * MI; + + Type&=~GAL_FLAGS; + + if (!(MI=GetMemInitInfoBlockFromType(Type))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return 0; + } + + Largest = 0; + + Index=MI->Empty; + + while (Index) + { + if (Index->Size > Largest) + Largest=Index->Size; + + Index=Index->Next; + } + + return Largest; +} + +/* --------------------------------------------------------------------------- + Function: static void AttachHdrToList(MEM_HDR **Head,MEM_HDR *Block) + + Purpose: Attach this block to a linked list + + Parms: MEM_HDR **Head -> Head of list holder + MEM_HDR *Block -> Block to add + + --------------------------------------------------------------------------- */ +static void AttachHdrToList(MEM_HDR **Head,MEM_HDR *Block) +{ + Block->Prev=NULL; + + if ((Block->Next=*Head)) + Block->Next->Prev=Block; + + *Head=Block; +} + + +/* --------------------------------------------------------------------------- + Function: static void DetachHdrFromList(MEM_HDR **Head,MEM_HDR *Block) + + Purpose: Detach this block from a linked list + + Parms: MEM_HDR **Head -> Head of list holder + MEM_HDR *Block -> Block to detach + + --------------------------------------------------------------------------- */ +static void DetachHdrFromList(MEM_HDR **Head,MEM_HDR *Block) +{ + if (Block->Prev) + Block->Prev->Next=Block->Next; + else + *Head=Block->Next; + + if (Block->Next) + Block->Next->Prev=Block->Prev; +} + + +/* --------------------------------------------------------------------------- + Function: BOOL IsActiveValidHandle(MHANDLE Handle) + + Purpose: Is this an active valid handle + + Parms: MHANDLE Handle to mem block + + Returns: TRUE if is + FALSE if not + --------------------------------------------------------------------------- */ +static BOOL IsActiveValidHandle(MHANDLE Handle) +{ + MEM_HDR *MemHdr; + + if (Handle >= MAX_MEM_BLOCKS || Handle < 0) + return FALSE; + + MemHdr=&MemHdrBlocks[Handle]; + + if (!(MemHdr->Mem)) + return FALSE; + + + return TRUE; +} + + +/* --------------------------------------------------------------------------- + Function: static void *AlignPtr(void *P,U32 Align) + + Purpose: Align this PTR to next aligned memory + + Parms: P = Ptr to align + Align = aligment + + Returns: Aligned ptr + --------------------------------------------------------------------------- */ +static void *AlignPtr(void *P,U32 Align) +{ + U32 Addr,Temp; + + Addr= (U32) P; + + Temp=Align-(Addr%Align); + + Addr+=(Temp == Align ? 0 : Temp); + + return (void *) Addr; +} + +/* --------------------------------------------------------------------------- + Function: static U32 AlignSize(U32 Size,U32 Align) + + Purpose: Pad out a size to this aligment + + Parms: Size = Size to pad out + Align = aligmnet to pad if out to + + Returns: Aligned Size + --------------------------------------------------------------------------- */ +static U32 AlignSize(U32 Size,U32 Align) +{ + Size+= Size%Align ? (Align-(Size%Align)) : 0; + return Size; +} + +/* --------------------------------------------------------------------------- + Function: MEM_HDR *FindClosestSizedBlock(MEM_HDR *Head, U32 Size) + + Purpose: Get the mem info structure for this type of memory + + Parms: U32 Type = Type of mem MEM_INIT_INFO needed for + + Returns: -> MEM_INIT_INFO if succesful else NULL + --------------------------------------------------------------------------- */ +MEM_HDR *FindClosestSizedBlock(MEM_HDR *Head, U32 Size) +{ + MEM_HDR * Closest; + MEM_HDR * Index; + + Closest=NULL; + + Index=Head; + + while (Index) + { + if (Index->Size >= Size) + { + if (Closest) + { + if ((Index->Size - Size) < (Closest->Size - Size)) + Closest=Index; + } + else + Closest=Index; + } + + Index=Index->Next; + } + + return Closest; +} + +/* --------------------------------------------------------------------------- + Function: MEM_HDR *FindClosestSizedBlock(MEM_HDR *Head, U32 Size) + + Purpose: Get the mem info structure for this type of memory + + Parms: U32 Type = Type of mem MEM_INIT_INFO needed for + + Returns: -> MEM_INIT_INFO if succesful else NULL + --------------------------------------------------------------------------- */ +MEM_HDR *FindHighestMemBlock(MEM_HDR *Head, U32 Size) +{ + MEM_HDR * Closest; + MEM_HDR * Index; + void * Highest; + + Closest=NULL; + Highest=NULL; + + Index=Head; + + while (Index) + { + if (Index->Size >= Size) + { + if (Closest) + { + if ((u32) Index->Mem > (u32) Highest) + { + Closest=Index; + Highest=Index->Mem; + } + } + else + { + Closest=Index; + Highest=Index->Mem; + } + } + + Index=Index->Next; + } + + return Closest; +} +/* --------------------------------------------------------------------------- + Function: static MEM_HDR *FindLowestMemBlock(MEM_HDR *Head, U32 Size) + + Purpose: Get the mem info structure for this type of memory + + Parms: U32 Type = Type of mem MEM_INIT_INFO needed for + + Returns: -> MEM_INIT_INFO if succesful else NULL + --------------------------------------------------------------------------- */ +MEM_HDR *FindLowestMemBlock(MEM_HDR *Head, U32 Size) +{ + MEM_HDR * Closest; + MEM_HDR * Index; + u32 Lowest; + + Closest=NULL; + + Index=Head; + + Lowest=0xffffffff; + Closest=NULL; + + while (Index) + { + if (Index->Size >= Size) + { + if (Closest) + { + if ((u32) Index->Mem < Lowest) + { + Closest=Index; + Lowest=(u32)Index->Mem; + } + } + else + { + Closest=Index; + Lowest=(u32)Index->Mem; + } + } + + Index=Index->Next; + } + + return Closest; +} + +/* --------------------------------------------------------------------------- + Function: static MEM_INIT_INFO *GetMemInitInfoBlockFromType(U32 Type) + + Purpose: Get the mem info structure for this type of memory + + Parms: U32 Type = Type of mem MEM_INIT_INFO needed for + + Returns: -> MEM_INIT_INFO if succesful else NULL + --------------------------------------------------------------------------- */ +static MEM_INIT_INFO *GetMemInitInfoBlockFromType(U32 Type) +{ + MEM_INIT_INFO * P; + MEM_INIT_INFO * RetBlock; + + RetBlock=NULL; + + P=MemInitBlocks; + + while (P) + { + /* Is the type we are looking for? */ + if (P->Type==Type) + return P; + + /* No, so go onto next block */ + + P=P->NextInitBlock; + + } + + return RetBlock; +} + +/* --------------------------------------------------------------------------- + Function: void MergeFromUsedtoFreeList(MEM_HDR *M,MEM_INIT_INFO *MI) + + Purpose: Merge this block into this mem info's empty list. Merge + it with any adjacent blocks + + Parms: U32 Type = Type of mem MEM_INIT_INFO needed for + + Returns: -> MEM_INIT_INFO if succesful else NULL + --------------------------------------------------------------------------- */ +static void MergeToEmptyList(MEM_INIT_INFO *MI,MEM_HDR *M) +{ + MEM_HDR * Index; + MEM_HDR * NextIndex; + + void * Start; + void * End; + + + /* Work out end address of this block */ + + Start=M->Mem; + End=(void *)(((U32) M->Mem) + M->Size); + + /* Init Loop Vars */ + Index=MI->Empty; + + /* Tumble through empty list grabbing empty blocks adjacent to our block we're freeing */ + + while (Index) + { + void * ThisStart; + void * ThisEnd; + + /* Work out end of address of current header */ + ThisStart=Index->Mem; + ThisEnd=(void *)(((U32) Index->Mem)+Index->Size); + + /* Work out next block in list */ + NextIndex=Index->Next; + + /* If we're adjacent then merge and remove from empty list */ + + if (Start==ThisEnd || End==ThisStart) + { + /* Merge found block to me */ + + M->Size+=Index->Size; + + if (Start==ThisEnd) + M->Mem=Index->Mem; + + /* Detach from empty and chuck into free */ + + DetachHdrFromList(&MI->Empty,Index); + + ReleaseMemHdrBlock(Index); + + } + + Index=NextIndex; + } + + AttachHdrToList(&MI->Empty,M); +} + + + +/* --------------------------------------------------------------------------- + Function: MHANDLE GAL_AllocAt(U32 MemNeeded,void *Addr,U32 Type,const char *Name) + + Purpose: Alloc some memory at a specefic address + WARNING *** + if Addr is not aligned to this memory types alignment then + block allocated will be at next aligned address, size will + still be correct. + + Parms: MemNeeded = Amount Wanted + Addr = Address needed at + Type = Type of mem wanted + Name = Name of mem block (NULL if no name wanted) + + Returns: A handle to memory if succesful + NULL_HANDLE if failed + --------------------------------------------------------------------------- */ +MHANDLE GAL_AllocAt(U32 Size,void *Addr,U32 Type,const char *Name) +{ + MEM_HDR * Block; + MEM_INIT_INFO * M; + U32 PhysSize; + + /* If this is a valid memory type get info about this mem type */ + + Type&=~GAL_FLAGS; + + if (!(M=GetMemInitInfoBlockFromType(Type))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return NULL_HANDLE; + } + + /* Align address asked for and align size */ + + Addr=AlignPtr(Addr,M->Alignment); + PhysSize=AlignSize(Size,M->Alignment); + + /* Find block we can fit into else error out if one not found */ + + if (!(Block=FindBlockInTheseBounds(M->Empty,Addr,PhysSize))) + return NULL_HANDLE; + + /* Take it out of the empty list */ + DetachHdrFromList(&M->Empty,Block); + + /* Now do the business */ + return LoAlloc(M,Block,Addr,Size,Name); +} + + +/* --------------------------------------------------------------------------- + Function: static MHANDLE LoAlloc(MEM_INIT_INFO *M,MEM_HDR *Block,void *Addr,U32 Size,const char *Name) + + Purpose: Lo level alloc used by GAL_AllocAt && GAL_Alloc + + Alloc some memory at a specefic address + WARNING *** + if Addr is not aligned to this memory types alignment then + block allocated will be at next aligned address, size will + still be correct. + + Parms: M = Mem region to alloc from + Block = Free block in mem region to alloc into + Size = Amount Wanted + Addr = Address needed at + Name = Name of block + + Returns: A handle to memory if succesful + NULL_HANDLE if failed + --------------------------------------------------------------------------- */ +static MHANDLE LoAlloc(MEM_INIT_INFO *M,MEM_HDR *Block,void *Addr,U32 Size,const char *Name) +{ + MEM_HDR * SplitBlock; + U32 PhysSize; + + + PhysSize=AlignSize(Size,M->Alignment); + + /* if block base isn't the as address we asked for ... */ + + if (Block->Mem != Addr) + { + /* Pare of begining go block and chuck back onto free list*/ + + if (!(SplitBlock=GetFreeMemHdrBlock())) + { + GSetError(ERR_RUN_OUT_OF_MEM_HDRS); + return NULL_HANDLE; + } + + SplitBlock->Mem=Block->Mem; + SplitBlock->Size=(U32) Addr-(U32)(Block->Mem); + SplitBlock->Type=(u16) M->Type; + + /* And put it into empty list in MEM_INIT_INFO */ + + MergeToEmptyList(M,SplitBlock); + + /* And adjust this block */ + Block->Mem=Addr; + Block->Size-=SplitBlock->Size; + } + + + /* if block found is bigger than memory wanted ... */ + if ((Block->Size > PhysSize)) + { + /* if can't find an unsused MEM_HDR in global pool then error */ + + if (!(SplitBlock=GetFreeMemHdrBlock())) + { + GSetError(ERR_RUN_OUT_OF_MEM_HDRS); + return NULL_HANDLE; + } + + /* Make new block point @ spare mem of alloced block */ + + SplitBlock->Mem=(void *) ((U32)(Block->Mem)+PhysSize); + SplitBlock->Size=Block->Size-PhysSize; + SplitBlock->Type=(u16) M->Type; + + /* And put it into empty list in MEM_INIT_INFO */ + + MergeToEmptyList(M,SplitBlock); + + /* Adjust alloced Block so that it is same size as memory wanted */ + + Block->Size=PhysSize; + } + + /* Detach block from memory pool from Empty list & add to Used List*/ + + AttachHdrToList(&M->Used,Block); + + /* Initialise the block */ + Block->Owners = 0; /* Say no one owns this block */ + + + Block->Type= (u16)M->Type; + Block->TimeStamp=TimeStamp; + + /* Do Some reporting */ + + if (VerbLev >= GAL_NOISY) + { + DBG_SendMessage("Succesfully alloced block of %d",(int)Size); + GAL_MemDump(M->Type); + } + + + /* Set the blocks name */ + + SetBlockName(Block,Name); + + + /* return Block's handle */ + + return Block->Handle; +} + + +/* --------------------------------------------------------------------------- + Function: static MEM_HDR *FindBlockInTheseBounds(MEM_INIT_INFO *MI,void *Addr,U32 Size) + + Purpose: Find a free block that fully covers from Addr to Addr[Size-1] + + Parms: Head = First block in link list to search + Addr = Address needed at + Size = Size of block + + Returns: A -> to mem block if found else + NULL + --------------------------------------------------------------------------- */ +static MEM_HDR *FindBlockInTheseBounds(MEM_HDR *Head,void *Addr,U32 Size) +{ + MEM_HDR * Index; + BOOL Done; + U32 ThisStart,ThisEnd; + U32 Start,End; + + Start=(U32) Addr; + End=Start+Size;; + + Done=FALSE; + Index=Head; + + while (Index && !Done) + { + ThisStart=(U32) (Index->Mem); + ThisEnd=ThisStart+Index->Size; + + if ((Start >= ThisStart) && (End <= ThisEnd)) + Done=TRUE; + else + Index=Index->Next; + } + + if (Done) + return Index; + else + return NULL; +} + + +/* --------------------------------------------------------------------------- + Function: static MEM_HDR *GetFreeMemHdrBlock(void) + + Purpose: Get a mem hdr block from the list of free ones + + Returns: A -> to mem block if found else + NULL + --------------------------------------------------------------------------- */ +static MEM_HDR *GetFreeMemHdrBlock(void) +{ + MEM_HDR *RetBlock; + + RetBlock=NULL; + + if (FreeBlocks) + { + NumOfFreeHdrs--; + + if (VerbLev >= GAL_AVERAGE && NumOfFreeHdrs == 9) + DBG_SendMessage("GAL: Warning Number of free headers < 10"); + + RetBlock=FreeBlocks; + DetachHdrFromList(&FreeBlocks,FreeBlocks); + } + + return RetBlock; +} + + +/* --------------------------------------------------------------------------- + Function: static void ReleaseMemHdrBlock(MEM_HDR * Index) + + Purpose: Puts a block back onto the list and does some + auditing + + Params: Index -> block to be put back on unused blocks list + + --------------------------------------------------------------------------- */ +static void ReleaseMemHdrBlock(MEM_HDR * Index) +{ + NumOfFreeHdrs++; + AttachHdrToList(&FreeBlocks,Index); +} + +/* --------------------------------------------------------------------------- + Function: void GAL_IterateEmptyMem(U32 MemType,void (*Func)(void *Addr,U32 Size,const char *Name)) + + Purpose: Iterate Through empry mem blocks of a mem type with a callback + + Params: MemType = Type of mem to iterate through + + --------------------------------------------------------------------------- */ +void GAL_IterateEmptyMem(U32 MemType,void (*Func)(void *Addr,U32 Size,const char *Name)) +{ + MEM_INIT_INFO * M; + + MemType&=~GAL_FLAGS; + + if ((M=GetMemInitInfoBlockFromType(MemType))) + { + MEM_HDR * Block; + + Block=M->Empty; + + while (Block) + { + Func(Block->Mem,Block->Size,GetBlockName(Block)); + Block=Block->Next; + } + } + else + GSetError(ERR_GAL_INVALID_MEM_TYPE); + +} +/* --------------------------------------------------------------------------- + Function: void GAL_IterateUsedMem(U32 MemType,void (*Func)(MHANDLE hnd,void *Addr,U32 Size,const char *Name,int Users,int TimeStamp)) + + Purpose: Iterate Through empry mem blocks of a mem type with a callback + + Params: MemType = Type of mem to iterate through + + --------------------------------------------------------------------------- */ +void GAL_IterateUsedMem(U32 MemType,void (*Func)(MHANDLE hnd,void *Addr,U32 Size,const char *Name,int Users,int TimeStamp)) +{ + MEM_INIT_INFO * M; + + MemType&=~GAL_FLAGS; + + if ((M=GetMemInitInfoBlockFromType(MemType))) + { + MEM_HDR * Block; + + Block = M->Used; + + while (Block) + { + Func(Block->Handle,Block->Mem, Block->Size, GetBlockName(Block), Block->Owners,Block->TimeStamp); + Block=Block->Next; + } + } + else + GSetError(ERR_GAL_INVALID_MEM_TYPE); + +} +/* --------------------------------------------------------------------------- + Function: BOOL GAL_SetMemName(MHANDLE Hnd,const char *Text); + + Purpose: Set this mem's name + + Params: Hnd = handle of block to set name of + Text -> text to set it to + + --------------------------------------------------------------------------- */ +BOOL GAL_SetMemName(MHANDLE Hnd,const char *Text) +{ + if (!IsActiveValidHandle(Hnd)) + { + GSetError(ERR_GAL_INVALID_MEM_HANDLE); + return(FALSE); + } + + SetBlockName(&MemHdrBlocks[Hnd],Text); + + return(TRUE); +} + + +/* --------------------------------------------------------------------------- + Function: U32 GAL_TotalMem(U32 Type) + + Purpose: Tells how much mem in total this mem block has + + Params: Type = Type of mem you want to know about + + Returns: Amount of mem, 0 if an erroneous type is passed + + --------------------------------------------------------------------------- */ +U32 GAL_TotalMem(U32 Type) +{ + U32 TotalMem; + MEM_INIT_INFO * M; + + Type&=~GAL_FLAGS; + + TotalMem=0; + + if ((M=GetMemInitInfoBlockFromType(Type))) + TotalMem=M->Size; + else + GSetError(ERR_GAL_INVALID_MEM_TYPE); + + return(TotalMem); +} + + +/* --------------------------------------------------------------------------- + Function: void *GAL_MemBase(U32 Type) + + Purpose: Says where the base of this mem type is + + Params: Type = Type of mem you want to know about + + Returns: Amount of mem, 0 if an erroneous type is passed + + --------------------------------------------------------------------------- */ +void *GAL_MemBase(U32 Type) +{ + void * Ret; + MEM_INIT_INFO * M; + + Ret=NULL; + + Type&=~GAL_FLAGS; + + + if ((M=GetMemInitInfoBlockFromType(Type))) + Ret=M->Mem; + else + GSetError(ERR_GAL_INVALID_MEM_TYPE); + + + return(Ret); +} + + +/* --------------------------------------------------------------------------- + Function: BOOL GAL_DefragMem(U32 type) + + Purpose: Defrag a mem type + + Params: Type = Type of mem you want to defrag + + Returns: TRUE if AOK + + --------------------------------------------------------------------------- */ +BOOL GAL_DefragMem(U32 type) +{ + BOOL GalRet; + + if (VerbLev >= GAL_AVERAGE) + DBG_SendMessage("GAL: Attempting defrag"); + + GalRet=GazDefragMem(type); + + if (FullErrorChecking) + { + if (!GAL_CheckMem(type)) + return FALSE; + } + + NumOfFreeHdrs=CountFreeBlocks(); + + return(TRUE); +} + + +/* --------------------------------------------------------------------------- + Function: static BOOL GSetError(GAL_ERROR_CODE Err) + + Purpose: Set's the internal error code to a vale + + Params: Error code to set internal var to + + Returns: FALSE + --------------------------------------------------------------------------- */ +static BOOL GSetError(GAL_ERROR_CODE Err) +{ + if (VerbLev >= GAL_AVERAGE) + DBG_SendMessage("GAL Error: %s ",GalErrors[Err]); + + if (HaltOnError) + DBG_Halt(); + + LastError=Err; + return(FALSE); +} + + +/* --------------------------------------------------------------------------- + Function: BOOL GAL_CheckMem(U32 Type) + + Purpose: Checks a memory types internal records for consistency + + Params: Type = type of mem to check + + Returns: False if there's some internal inconsistency + + --------------------------------------------------------------------------- */ +BOOL GAL_CheckMem(U32 Type) +{ + MEM_INIT_INFO * M; + MEM_HDR * MemHdr; + u32 TotalMem; + + Type&=~GAL_FLAGS; + + TotalMem=0; + + if (!(M=GetMemInitInfoBlockFromType(Type))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return FALSE; + } + + + /* Check all of the empty blocks for collisions with any others */ + + MemHdr=M->Empty; + + while (MemHdr) + { + if (CheckCollisions(M,MemHdr)) + return(GSetError(ERR_GAL_MEM_BLOCK_COLLISION)); + + TotalMem+=MemHdr->Size; + + MemHdr=MemHdr->Next; + } + + /* Check all of the used blocks for collisions with any others */ + + MemHdr=M->Used; + + while (MemHdr) + { + if (CheckCollisions(M,MemHdr)) + return(GSetError(ERR_GAL_MEM_BLOCK_COLLISION)); + + TotalMem+=MemHdr->Size; + + MemHdr=MemHdr->Next; + } + + + /* Check that the total mem is exactly the same as the mem info mem */ + + if (M->Size != TotalMem) + return(GSetError(ERR_GAL_MEM_AREA_NOT_COVERED)); + + + return(TRUE); +} + + +/* --------------------------------------------------------------------------- + Function: static void CheckCollisions(MEM_INIT_INFO * M,MEM_HDR *MemHdr) + + Purpose: Checks if this mem block overlaps with any other in this mem + types empty or used list + + Params: M -> Header for type of mem + MemHdr -> Block to check + + Returns: TRUE if it does + --------------------------------------------------------------------------- */ +static BOOL CheckCollisions(MEM_INIT_INFO * M,MEM_HDR *MemHdr) +{ + MEM_HDR * CheckHdr; + + CheckHdr=M->Used; + + while (CheckHdr) + { + if ((CheckHdr != MemHdr) && AreBlocksColliding(MemHdr,CheckHdr)) + return(TRUE); + + CheckHdr=CheckHdr->Next; + } + + + CheckHdr=M->Empty; + + while (CheckHdr) + { + if ((CheckHdr != MemHdr) && AreBlocksColliding(MemHdr,CheckHdr)) + return(TRUE); + + CheckHdr=CheckHdr->Next; + } + + return(FALSE); +} + +/* --------------------------------------------------------------------------- + Function: static BOOL AreBlocksColliding(MEM_HDR *Hdr1,MEM_HDR *Hdr1) + + Purpose: Do this two blocks overlap + + Params: Hdr1 -> First block + Hdr2 -> Second block + + Returns: TRUE if it does + --------------------------------------------------------------------------- */ +static BOOL AreBlocksColliding(MEM_HDR *Hdr1,MEM_HDR *Hdr2) +{ + U32 Addr1; + U32 Addr2; + + Addr1=(U32) Hdr1->Mem; + Addr2=(U32) Hdr2->Mem; + + if ((Addr1< Addr2+Hdr2->Size) && (Addr1 >= Addr2)) + return(TRUE); + + if ((Addr2< Addr1+Hdr1->Size) && (Addr2 >= Addr1)) + return(TRUE); + + return(FALSE); + +} + +/* --------------------------------------------------------------------------- + Function: const char *GAL_GetErrorText(GAL_ERROR_CODE Err) + + Purpose: Converts a GAL error to + + Params: Err = Error to get code for + + Returns: NULL if no error || -> error text + --------------------------------------------------------------------------- */ +char *GAL_GetErrorText(GAL_ERROR_CODE Err) +{ + if (Err>= NUM_OF_ERROR_MESSAGES) + return("Invalid error code"); + else + return(GalErrors[Err]); +} + +/* --------------------------------------------------------------------------- + Function: GAL_ERROR_CODE GAL_GetLastErrorCode(void) + + Purpose: Get's last error gal had + + Returns: Last error code + --------------------------------------------------------------------------- */ +GAL_ERROR_CODE GAL_GetLastErrorCode(void) +{ + return(LastError); +} + +/* --------------------------------------------------------------------------- + Function: char *GAL_GetLastErrorText(void) + + Purpose: Get's text for last error gal had + + Returns: NULL if no error || -> error text + --------------------------------------------------------------------------- */ +char *GAL_GetLastErrorText(void) +{ + return(GAL_GetErrorText(LastError)); +} + + +/* --------------------------------------------------------------------------- + Function: int GAL_HowManyFreeBlocksUsed(U32 Type) + + Purpose: Find's out how many free blocks are used by this mem type + + Returns: Blocks used + --------------------------------------------------------------------------- */ +int GAL_HowManyEmptyRegions(U32 Type) +{ + MEM_INIT_INFO * m; + int Count; + + Type&=~GAL_FLAGS; + + if ((m = GetMemInitInfoBlockFromType(Type))) + { + MEM_HDR * mh; + + Count=0; + + mh=m->Empty; + + while (mh) + { + Count++; + mh=mh->Next; + } + } + else + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + Count=-1; + } + + return Count; +} + +/* --------------------------------------------------------------------------- + Function: int GAL_HowManyFreeBlocksUsed(U32 Type) + + Purpose: Find's out how many free blocks are used by this mem type + + Returns: Blocks used + --------------------------------------------------------------------------- */ +int GAL_HowManyUsedRegions(U32 Type) +{ + MEM_INIT_INFO * m; + int Count; + + Type&=~GAL_FLAGS; + + if ((m = GetMemInitInfoBlockFromType(Type))) + { + MEM_HDR * mh; + mh=m->Used; + Count=0; + + while (mh) + { + Count++; + mh=mh->Next; + } + } + else + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + Count=-1; + } + + return Count; +} + + +/* --------------------------------------------------------------------------- + Function: void GAL_SetTimeStamp(int Time) + + Purpose: All blocks alloced will be of this time stamp + --------------------------------------------------------------------------- */ +void GAL_SetTimeStamp(int Time) +{ + TimeStamp=Time; +} + + +/* --------------------------------------------------------------------------- + Function: void GAL_SetTimeStamp(int Time) + + Purpose: All blocks alloced will be of this time stamp + --------------------------------------------------------------------------- */ +void GAL_IncTimeStamp(void) +{ + TimeStamp++; +} + +/* --------------------------------------------------------------------------- + Function: void GAL_SetTimeStamp(int Time) + + Purpose: All blocks alloced will be of this time stamp + --------------------------------------------------------------------------- */ +int GAL_GetTimeStamp(void) +{ + return TimeStamp; +} + + +/* --------------------------------------------------------------------------- + Function: S32 GAL_AlignSizeToType(U32 Size,U32 MemType) + + Purpose: Align a size to the alignment specified by a mem type + + Params: Size = Size to align + MemType = Mem type + + Returns: Aligned size or -1 if an error + + --------------------------------------------------------------------------- */ +S32 GAL_AlignSizeToType(U32 Size,U32 MemType) +{ + MEM_INIT_INFO * Mi; + + MemType&=~GAL_FLAGS; + + Mi=GetMemInitInfoBlockFromType(MemType); + + if (Mi) + return(AlignSize(Size,Mi->Alignment)); + else + return(-1); +} + + +/* --------------------------------------------------------------------------- + Function: MHANDLE GAL_AllocMultiStruct(GAL_STRUCT * G,U32 Type,const char *Name) + + Purpose: Alloc a load of structures back to back all aligned to correct + boundaries for the mem type. Then store the offset into the mem + mem alloced for each struct back into the GAL_STRUC_ALLOC array. + The array should be terminated by an element with an original size + entry of -1. See TASKER.C TSK_AddTask for an example + + Params: G -> Array of GAL_STRUC_ALLOC + Type = Mem to put it into + Name = Name of this block + + Returns: HND to mem the mem if ok || NULL_HANDLE if not + + --------------------------------------------------------------------------- */ +MHANDLE GAL_AllocMultiStruct(GAL_STRUCT * G,U32 Type,const char *Name) +{ + int TotalMem; + TotalMem=GAL_ProcessMultiStruct(G,Type&~GAL_FLAGS); + + return(GAL_Alloc(TotalMem,Type,Name)); +} + +/* --------------------------------------------------------------------------- + Function: UINT GAL_ProcessMultiStruct(GAL_STRUCT * G,U32 Type) + + Purpose: Works out the offsets for this multi struc in a particular mem + type and stores em back into the Offset stuff + + Params: G -> Array of GAL_STRUC_ALLOC + Type = Mem to put it into + + Returns: How big the alloced block would be + + --------------------------------------------------------------------------- */ +UINT GAL_ProcessMultiStruct(GAL_STRUCT * G,U32 Type) +{ + UINT TotalMem; + int f; + + Type&=~GAL_FLAGS; + + TotalMem=0; + + for (f=0;G[f].OriginalSize != -1;f++) + { + G[f].Offset=TotalMem; + TotalMem+=GAL_AlignSizeToType((UINT) G[f].OriginalSize,Type); + } + + G[f].Offset=TotalMem; + + return(TotalMem); +} + +/* --------------------------------------------------------------------------- + Function: static BOOL GazDefragMem(U32 MemType) + + Purpose: Defrag the memory of this mem type + + Returns: If there was a problem or not + + --------------------------------------------------------------------------- */ +s32 GAL_GetSize(MHANDLE hnd) +{ + MEM_HDR * MemHdr; + + if (!IsActiveValidHandle(hnd)) + { + GSetError(ERR_GAL_INVALID_MEM_HANDLE); + return -1; + } + + MemHdr=&MemHdrBlocks[hnd]; + return(MemHdr->Size); +} + + + +/* --------------------------------------------------------------------------- + Function: static BOOL GazDefragMem(U32 MemType) + + Purpose: Defrag the memory of this mem type + + Returns: If there was a problem or not + + --------------------------------------------------------------------------- */ + +static BOOL GazDefragMem(U32 MemType) +{ + MEM_HDR * LockedBlocks; + MEM_INIT_INFO * M; + MEM_REG Reg; + + MemType&=~GAL_FLAGS; + + if (!(M=GetMemInitInfoBlockFromType(MemType))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return (FALSE); + } + + /* Only defrag if a memove defined in memory profile */ + + if (!M->MemMove) + { + GSetError(ERR_GAL_NO_MEM_MOVE); + return (FALSE); + } + + + /* Take all the locked blocks off the used list and chuck then onto our local list */ + + LockedBlocks=NULL; + PutAllLockedBlocksOntoList(&LockedBlocks,&M->Used); + + /* Sort our brand new list by size */ + + /* Sort remaining used but unlocked blocks into addr order */ + + SortMemHdrListByAddr(&LockedBlocks); + + + /* Get Rid of all Empty blocks in MemType */ + + DeleteEmptyBlocks(M); + + /* Now loop through sorted empty blocks */ + + Reg.Mem=NULL; + + while (GetRegion(&Reg,LockedBlocks,M)) + { + + /* If there was a region and it has a size the do the business */ + + if (Reg.Size) + { + MEM_HDR * NewEmptyBlock; + MEM_HDR * ListOfBlocksInRegion; + U32 ShuffledSize; + int GapSize; + + ListOfBlocksInRegion=NULL; + + PutBlocksInRegionIntoList(&Reg,&ListOfBlocksInRegion,&M->Used); + + SortMemHdrListByAddr(&ListOfBlocksInRegion); + ShuffledSize=ShuffleBlocks(ListOfBlocksInRegion,&Reg,M); + + /* Create the empty block for the rest of the region */ + + GapSize=Reg.Size-ShuffledSize; + + if (GapSize) + { + if (!(NewEmptyBlock=GetFreeMemHdrBlock())) + { + GSetError(ERR_RUN_OUT_OF_MEM_HDRS); + return FALSE; + } + + NewEmptyBlock->Mem=(void *)((U32)Reg.Mem+ShuffledSize); + NewEmptyBlock->Size= GapSize; + NewEmptyBlock->Type=(u16)MemType; + + /* And put it into the list */ + + MergeToEmptyList(M,NewEmptyBlock); + } + + GraftMemHdrList(&M->Used,&ListOfBlocksInRegion); + } + } + + /* Now add locked blocks back onto list */ + + PutAllLockedBlocksOntoList(&M->Used,&LockedBlocks); + + + return(TRUE); +} + +/* --------------------------------------------------------------------------- + Function: void PutBlocksInRegionIntoList(MEM_REG *Reg,MEM_HDR **ToList,MEM_HDR **FromList) + + Purpose: Go through From list and take out all the blocks in the + region and put it into ToList + + Params: Reg -> Struct describing region + ToList -> Head of list to put them into + FromList -> Head of list to take them from + --------------------------------------------------------------------------- */ +static void PutBlocksInRegionIntoList(MEM_REG *Reg,MEM_HDR **ToList,MEM_HDR **FromList) +{ + MEM_HDR * ThisBlock; + + + ThisBlock=*FromList; + + while (ThisBlock) + { + MEM_HDR * NextBlock; + MEM_REG MemReg; + + NextBlock=ThisBlock->Next; + + MemReg.Mem=ThisBlock->Mem; + MemReg.Size=ThisBlock->Size; + + if (CollideRegions(Reg,&MemReg)) + { + DetachHdrFromList(FromList,ThisBlock); + AttachHdrToList(ToList,ThisBlock); + } + + ThisBlock=NextBlock; + } +} + +/* --------------------------------------------------------------------------- + Function: BOOL CollideRegions(MEM_REG *Reg1,MEM_REG *Reg2) + + Purpose: Do these regions collide + + Params: Reg1 -> Region descriptor 1 + Reg2 -> Region descriptor 2 + + Return: TRUE if so + --------------------------------------------------------------------------- */ +static BOOL CollideRegions(MEM_REG *Reg1,MEM_REG *Reg2) +{ + + if (((U32) Reg1->Mem + Reg1->Size) <= (U32) Reg2->Mem) + return(FALSE); + + if ((U32) Reg1->Mem >= ((U32) Reg2->Mem + Reg2->Size)) + return(FALSE); + + return(TRUE); +} + +/* --------------------------------------------------------------------------- + Function: static void DeleteEmptyBlocks(MEM_INIT_INFO *M) + + Purpose: Go through this mem type and delete all empty blocks + + Params: M -> Information about this mem type + --------------------------------------------------------------------------- */ +static void DeleteEmptyBlocks(MEM_INIT_INFO *M) +{ + while (M->Empty) + { + MEM_HDR * ThisBlock; + + ThisBlock=M->Empty; + + DetachHdrFromList(&M->Empty,ThisBlock); + AttachHdrToList(&FreeBlocks,ThisBlock); + } +} + +/* --------------------------------------------------------------------------- + Function: void BOOL GetRegion(MEM_REG *Reg,MEM_HDR * LockedBlocks,MEM_INIT_INFO *M) + + Purpose: Find the next region of memory after Region that has no locked blocks + a new list + + Params: Reg -> Description of previous region (If base is NULL will + from beggining + LockedBlocks -> List of locked blocks sorted by addr + M -> Information about this mem type + + Returns: TRUE if there was another region and info about it in Reg + FALSE if not another region + + --------------------------------------------------------------------------- */ +static BOOL GetRegion(MEM_REG *Reg,MEM_HDR * LockedBlocks,MEM_INIT_INFO *M) +{ + MEM_HDR * FirstBlock; + MEM_HDR * SecondBlock; + MEM_REG NewReg; + U32 FirstSize; + + FirstBlock=FindNextBlock(Reg->Mem,LockedBlocks); + + FirstSize=0; + + if (FirstBlock) + NewReg.Mem=(void *)((U32)FirstBlock->Mem+FirstBlock->Size); + else + { + if (Reg->Mem) + return(FALSE); + else + NewReg.Mem=M->Mem; + } + + SecondBlock=FindNextBlock(NewReg.Mem,LockedBlocks); + + if (SecondBlock) + NewReg.Size=(U32) SecondBlock->Mem - (U32) NewReg.Mem; + else + NewReg.Size=(U32) M->Mem +M->Size - (U32) NewReg.Mem; + + if (CollideRegions(Reg,&NewReg) && Reg->Mem) + return(FALSE); + else + { + Reg->Mem=NewReg.Mem; + Reg->Size=NewReg.Size; + return(TRUE); + } +} + +/* --------------------------------------------------------------------------- + Function: static MEM_HDR *FindStartBlock(void *Addr,MEM_HDR * Blocks) + + Purpose: Find the first block in this list that Sits addres this addr + + Params: Addr = addr to look from + Blocks -> List of blocks to look in + + Returns: -> Block || Null if none + --------------------------------------------------------------------------- */ + +/* +static MEM_HDR *FindStartBlock(void *Addr,MEM_HDR * Blocks) +{ + while (Blocks) + { + if (Blocks->Mem==Addr) + return(Blocks); + else + Blocks=Blocks->Next; + } + + return(NULL); +} +*/ + +/* --------------------------------------------------------------------------- + Function: static MEM_HDR *FindNextBlock(void *Addr,MEM_HDR * Blocks) + + Purpose: Find the first block that is at >= Addr + + Params: Addr = addr to look from + Blocks -> List of blocks to look in + + Returns: -> Block || Null if none + --------------------------------------------------------------------------- */ +static MEM_HDR *FindNextBlock(void *Addr,MEM_HDR * Blocks) +{ + if (Addr) + { + while (Blocks) + { + U32 BlockAddr; + U32 AddrU32; + + AddrU32=(U32) Addr; + BlockAddr=(U32) Blocks->Mem; + + if (BlockAddr >= AddrU32) + return(Blocks); + else + Blocks=Blocks->Next; + } + } + + return(NULL); +} + +/* --------------------------------------------------------------------------- + Function: static U32 ShuffleBlocks(MEM_HDR *Blocks,MEM_REG *Reg,MEM_INIT_INFO *M) + + Purpose: Compact a linked list of sorted by addr blocks in mem + + Params: Blocks -> First in list of blocks + Reg -> Region the blocks should compact themselves in + M -> Description of mem region + + Returns: Size all blocks take up in memory + --------------------------------------------------------------------------- */ +static U32 ShuffleBlocks(MEM_HDR *Blocks,MEM_REG *Reg,MEM_INIT_INFO *M) +{ + U32 NewSize; + void * MemBase; + MEM_HDR * ThisBlock; + + NewSize=0; + + MemBase=Reg->Mem; + + ThisBlock=Blocks; + + while (ThisBlock) + { + NewSize+=ThisBlock->Size; + + if (MemBase != ThisBlock->Mem) + { + M->MemMove(MemBase,ThisBlock->Mem,ThisBlock->Size); + ThisBlock->Mem=MemBase; + } + + MemBase=(void *)((U32) ThisBlock->Mem+ThisBlock->Size); + + ThisBlock=ThisBlock->Next; + } + + return(NewSize); + + +} + +/* --------------------------------------------------------------------------- + Function: void PutAllLockedBlocksOntoList(MEM_HDR **ToHead,MEM_HDR **FromHead) + + Purpose: Take all the locked blocks from a list and chuck them onto + a new list + + Params: ToHead -> Head of list to put stuff onto + FromHead -> Head of list to take blocks off + --------------------------------------------------------------------------- */ +static void PutAllLockedBlocksOntoList(MEM_HDR **ToHead,MEM_HDR **FromHead) +{ + MEM_HDR * CurHdr; + + CurHdr = *FromHead; + + while (CurHdr) + { + MEM_HDR * NextCurHdr; + + NextCurHdr=CurHdr->Next; + + if (CurHdr->Owners) + { + DetachHdrFromList(FromHead,CurHdr); + AttachHdrToList(ToHead,CurHdr); + } + + CurHdr=NextCurHdr; + } +} + + +/* --------------------------------------------------------------------------- + Function: void SortMemHdrListByAddr(MEM_HDR **Head) + + Purpose: Sort all the memhdr blocks in a list in descending + order by base Address and yes, it's a bubble sort + + Params: Head -> Head of list to sort + --------------------------------------------------------------------------- */ + +void SortMemHdrListByAddr(MEM_HDR **Head) +{ + BOOL DidASwap; + MEM_HDR * CurHdr; + MEM_HDR * NextHdr; + + if (*Head && (*Head)->Next) + { + do + { + + DidASwap=FALSE; + + CurHdr=*Head; + + do + { + + NextHdr=CurHdr->Next; + + if ((U32) CurHdr->Mem > (U32) NextHdr->Mem) + { + MEM_HDR * OldPrev; + + OldPrev=CurHdr->Prev; + + /* Swap them in the link list */ + + CurHdr->Next = NextHdr->Next; + CurHdr->Prev = NextHdr; + NextHdr->Next= CurHdr; + if (CurHdr->Next) + CurHdr->Next->Prev=CurHdr; + + NextHdr->Next=CurHdr; + NextHdr->Prev=OldPrev; + if (NextHdr->Prev) + NextHdr->Prev->Next=NextHdr; + else + *Head=NextHdr; + + DidASwap=TRUE; + } + else + CurHdr=CurHdr->Next; + } + while(CurHdr->Next); + + } + while(DidASwap); + } +} + +/* --------------------------------------------------------------------------- + Function: void GraftMemHdrList(MEM_HDR ** ToList,MEM_HDR ** FromList) + + Purpose: Graft one list to another + + Params: ToList -> Head of list to graft to + FromList -> Head of list to graft + --------------------------------------------------------------------------- */ +static void GraftMemHdrList(MEM_HDR ** ToList,MEM_HDR ** FromList) +{ + MEM_HDR * OldFirst; + + OldFirst=*ToList; + + if (*FromList) + { + MEM_HDR * LastHdr; + + *ToList=*FromList; + + LastHdr=*FromList; + + while (LastHdr->Next) + LastHdr=LastHdr->Next; + + LastHdr->Next=OldFirst; + + if (OldFirst) + OldFirst->Prev=LastHdr; + + *FromList=NULL; + } +} + +/* --------------------------------------------------------------------------- + Function: void GAL_MemDump(U32 Type) + + Purpose: Do a dump of this type to the debug messager + + Params: Type = type to dump + + --------------------------------------------------------------------------- */ +void GAL_MemDump(U32 Type) +{ + Type&=~GAL_FLAGS; + + DBG_SendMessage("%d : mem left",(int)GAL_GetFreeMem(Type)); + DBG_SendMessage("%d : largest block",(int)GAL_GetFreeMem(Type)); + DBG_SendMessage("%d : Last attempted alloc",(int)LastAttemptedAlloc); +} + + +/* --------------------------------------------------------------------------- + + Function: void GAL_SetVerbosity(GAL_VERB_LEV G) + + Purpose: Say how noisy gal should be + + Params: Type = type to dump + + --------------------------------------------------------------------------- */ +void GAL_SetVerbosity(GAL_VERB_LEV G) +{ + VerbLev=G; +} + + +/* --------------------------------------------------------------------------- + Function: void GAL_SetVerbosity(GAL_VERB_LEV G) + + Purpose: Say how noisy gal should be + + Params: Type = type to dump + + --------------------------------------------------------------------------- */ +int CountFreeBlocks(void) +{ + MEM_HDR * RetBlock; + int Count; + + RetBlock=FreeBlocks; + Count=0; + + while (RetBlock) + { + Count++; + RetBlock=RetBlock->Next; + } + + return(Count); +} + +/* --------------------------------------------------------------------------- + Function: void SetBlockName(MEM_HDR * MemHdr,const char * NewName) + + Purpose: Set the name of this block + + Params: MemHdr - > Block to set name of + NewName -> New name of block + + --------------------------------------------------------------------------- */ +void SetBlockName(MEM_HDR * MemHdr,const char * NewName) +{ +#ifdef __GL_DEBUG__ + + int IndexSoFar; + + IndexSoFar=0; + + if (NewName) + { + while ((IndexSoFar != MAX_NAME_SIZE-1) && *NewName) + MemHdr->Name[IndexSoFar++]=*NewName++; + } + + MemHdr->Name[IndexSoFar]=0; +#endif +} + +/* --------------------------------------------------------------------------- + Function: void SetBlockName(MEM_HDR * MemHdr,const char * NewName) + + Purpose: Set the name of this block + + Params: MemHdr - > Block to set name of + NewName -> New name of block + + --------------------------------------------------------------------------- */ +char const * GetBlockName(MEM_HDR * MemHdr) +{ +#ifdef __GL_DEBUG__ + return(MemHdr->Name); +#else + return("NO NAME"); +#endif +} + + +/* --------------------------------------------------------------------------- + Function: int GAL_GetNumFreeHeaders(void) + Purpose: Find out how many more free hdrs there are + Returns: Info we want + --------------------------------------------------------------------------- */ +int GAL_GetNumFreeHeaders(void) +{ + return(NumOfFreeHdrs); +} + +u32 GAL_GetLastTypeAlloced(void) +{ + return(LastTypeAlloced); +} + +/* --------------------------------------------------------------------------- + Function: int GAL_GetAlignment(u32 MemType) + Purpose: Get the allocation boundary alignment for this memory type + Returns: Info we want or -1 if an error + --------------------------------------------------------------------------- */ +u32 GAL_GetAlignment(u32 MemType) +{ + MEM_INIT_INFO * M; + + if (!(M=GetMemInitInfoBlockFromType(MemType))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return -1; + } + else + return(M->Alignment); +} + + +/* --------------------------------------------------------------------------- + Function: void GAL_HaltOnError(void) + Purpose: Say that gal will halt on an error + --------------------------------------------------------------------------- */ +void GAL_HaltOnError(void) +{ + HaltOnError=TRUE; +} + +/* --------------------------------------------------------------------------- + Function: int GAL_GetAlignment(u32 MemType) + Purpose: Say that gal WONT will halt on an error (Default behaviour) + --------------------------------------------------------------------------- */ +void GAL_ReturnOnError(void) +{ + HaltOnError=FALSE; +} + +/* --------------------------------------------------------------------------- + Function: GAL_FILTER GAL_SetAllocFilter(GAL_FILTER NewFilter) + Purpose: Set a callback to be executed b4 every alloc + Params: NewFilter -> Filter func (NULL means no filtering) + Returns: Old filter + --------------------------------------------------------------------------- */ +GAL_FILTER GAL_SetAllocFilter(GAL_FILTER NewFilter) +{ + GAL_FILTER OldFilter; + + OldFilter=AllocFilter; + AllocFilter=NewFilter; + + return(OldFilter); +} + + +/* --------------------------------------------------------------------------- + Function: void GAL_SortUsedRegionsBySize(U32 Type) + Purpose: Sort the order of the used regions by size (descending) + Params: MemType=type of mem to sort + --------------------------------------------------------------------------- */ +GLIB_API BOOL GAL_SortUsedRegionsBySize(U32 MemType) +{ + MEM_INIT_INFO * M; + + MemType&=~GAL_FLAGS; + + if (!(M=GetMemInitInfoBlockFromType(MemType))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return (FALSE); + } + SortMemHdrList(&M->Used,SortSize); + return(TRUE); +} + +BOOL SortSize(MEM_HDR *B1,MEM_HDR * B2) +{ + if (B1->SizeSize) + return(TRUE); + else + return(FALSE); +} + +/* --------------------------------------------------------------------------- + Function: void GAL_SortUsedRegionsBySize(U32 Type) + Purpose: Sort the order of the used regions by size (descending) + Params: MemType=type of mem to sort + --------------------------------------------------------------------------- */ +GLIB_API BOOL GAL_SortUsedRegionsByAddress(U32 MemType) +{ + MEM_INIT_INFO * M; + + MemType&=~GAL_FLAGS; + + if (!(M=GetMemInitInfoBlockFromType(MemType))) + { + GSetError(ERR_GAL_INVALID_MEM_TYPE); + return (FALSE); + } + SortMemHdrList(&M->Used,SortAddr); + return(TRUE); +} + +BOOL SortAddr(MEM_HDR *B1,MEM_HDR * B2) +{ + if ((u32)B1->Mem<(u32)B2->Mem) + return(TRUE); + else + return(FALSE); +} + + +/* --------------------------------------------------------------------------- + Function: void SortMemHdrList(MEM_HDR **Head,bool (*CompFunc)(MEM_HDR *B1,MEM_HDR * B2)) + Purpose: Sort a list of memhdr blocks + Params: Head->-> Head of list to be sorted + CompFunc Should I swap routine + --------------------------------------------------------------------------- */ +void SortMemHdrList(MEM_HDR **Head,BOOL (*CompFunc)(MEM_HDR *B1,MEM_HDR * B2)) +{ + BOOL DidASwap; + MEM_HDR * CurHdr; + MEM_HDR * NextHdr; + + if (*Head && (*Head)->Next) + { + do + { + + DidASwap=FALSE; + + CurHdr=*Head; + + do + { + NextHdr=CurHdr->Next; + + if (CompFunc(CurHdr,NextHdr)) + { + MEM_HDR * OldPrev; + + OldPrev=CurHdr->Prev; + + /* Swap them in the link list */ + + CurHdr->Next = NextHdr->Next; + CurHdr->Prev = NextHdr; + NextHdr->Next= CurHdr; + if (CurHdr->Next) + CurHdr->Next->Prev=CurHdr; + + NextHdr->Next=CurHdr; + NextHdr->Prev=OldPrev; + if (NextHdr->Prev) + NextHdr->Prev->Next=NextHdr; + else + *Head=NextHdr; + + DidASwap=TRUE; + } + else + CurHdr=CurHdr->Next; + } + while(CurHdr->Next); + + } + while(DidASwap); + } +} + +/* --------------------------------------------------------------------------- + ends + ---- */ diff --git a/Utils/Libs/GLib/gal.h b/Utils/Libs/GLib/gal.h new file mode 100644 index 000000000..18d450a85 --- /dev/null +++ b/Utils/Libs/GLib/gal.h @@ -0,0 +1,210 @@ +/* ========================================================================== + File: GAL.H + + Notes: Memory allocation \ deallocation module header file + + Author: Gary Liddon + + Copyright (C) 1995 - 1997 Gary Liddon + All rights reserved. + =========================================================================== */ + +#ifndef __GAL_H__ +#define __GAL_H__ + +/* --------------------------------------------------------------------------- + Includes + -------- */ +#include "gtypes.h" + +/* --------------------------------------------------------------------------- + Defines + ------- */ +#define NULL_HANDLE -1 +#define PHANTOM_MEM -1 + +/* --------------------------------------------------------------------------- + Typedefs + -------- */ +typedef S32 MHANDLE; +typedef int MTYPE; +typedef void (*GAL_FILTER)(U32 MemType,U32 Size,const char *Name); + +/* --------------------------------------------------------------------------- + Enums + ----- */ +enum +{ + GAL_PHANTOM_MEM=0, + GAL_FIRST_FREE_MEM_TYPE, + GAL_HIGH = 1<<15, + GAL_FLAGS = GAL_HIGH +}; + +typedef enum GAL_ERROR_CODE +{ + ERR_GAL_NO_ERROR = 0, + ERR_RUN_OUT_OF_MEM_HDRS, + ERR_GAL_MEM_TYPE_EXISTS, + ERR_GAL_MEM_TYPE_OVERLAP, + ERR_GAL_INVALID_MEM_TYPE, + ERR_GAL_INVALID_MEM_HANDLE, + ERR_GAL_MEM_ALREADY_UNLOCKED, + ERR_GAL_MEM_BLOCK_COLLISION, + ERR_GAL_MEM_AREA_NOT_COVERED, + ERR_GAL_NO_MEM_MOVE, + ERR_GAL_NOT_ENOUGH_MEM, + NUM_OF_ERROR_MESSAGES + +}GAL_ERROR_CODE; + +typedef enum GAL_VERB_LEV +{ + GAL_SILENT=0, + GAL_AVERAGE, + GAL_NOISY + +} GAL_VERB_LEV; + +/* --------------------------------------------------------------------------- + Structures + ---------- */ + +/* Pre declaration of Header each block of free / used memory has + -------------------------------------------------------------- */ +struct MEM_HDR; + +/* Memory Initialisation module Structure + Must be placed in RAM + -------------------------------------- */ +typedef struct MEM_INIT_INFO +{ + void * Mem; /* Start of memory region */ + U32 Size; /* Size of memory region */ + U32 Type; /* Type of memory region */ + char * TypeString; /* Debugging string */ + U16 Alignment; /* Alignment required */ + void (*MemMove)(void *Dest,void *Source,U32 size); /* Memory move vector */ + + /* Used by System, Leave uninitialised */ + + struct MEM_INIT_INFO * NextInitBlock; + + U16 Flags; + + struct MEM_HDR * Empty; /* Pointer to free MEM_HDRs of this mem type */ + struct MEM_HDR * Used; /* Pointer to used MEM_HDRs of this mem type */ + +} MEM_INIT_INFO; + + +/* Structure used by GAL_AllocMultiStruct + -------------------------------------- */ +typedef struct GAL_STRUCT +{ + int OriginalSize; + UINT Offset; + +} GAL_STRUCT; + +/* --------------------------------------------------------------------------- + Supported Functions + ------------------- */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialisation Functions + ------------------------ */ +void GAL_InitModule(void); + + +/* Mem allocing / deallocing functions + ----------------------------------- */ +GLIB_API MHANDLE GAL_Alloc(U32 Size,U32 Type,const char *Name); +GLIB_API MHANDLE GAL_AllocAt(U32 Size,void *Addr,U32 Type,const char *Name); +GLIB_API BOOL GAL_Free(MHANDLE Handle); +GLIB_API void * GAL_Lock(MHANDLE Handle); +GLIB_API BOOL GAL_Unlock(MHANDLE Handle); + + + +/* + Purpose: Split a Block into two. If Split point outside + block to be split then this will return an error. + Split is rounded rounded up to be on mem type alignment + boundary + Returns: Handle of second block else + NULL_HANDLE if unsucessful +*/ + +GLIB_API MHANDLE GAL_SplitBlock(MHANDLE CurBlock,U32 Split); + + + + +GLIB_API BOOL GAL_SetMemName(MHANDLE Hnd,const char *Text); +GLIB_API MHANDLE GAL_AllocMultiStruct(GAL_STRUCT * G,U32 Type,const char *Name); +GLIB_API UINT GAL_ProcessMultiStruct(GAL_STRUCT * G,U32 Type); +GLIB_API UINT GAL_GetMemSize(MHANDLE hnd); + +/* Mem type Functions + ------------------ */ +GLIB_API BOOL GAL_DefragMem(U32 type); +GLIB_API BOOL GAL_AddMemType(MEM_INIT_INFO *M); +GLIB_API U32 GAL_TotalMem(U32 Type); +GLIB_API void * GAL_MemBase(U32 Type); +GLIB_API U32 GAL_GetFreeMem(U32 Type); +GLIB_API U32 GAL_GetUsedMem(U32 Type); +GLIB_API U32 GAL_LargestFreeBlock(U32 Type); +GLIB_API S32 GAL_AlignSizeToType(U32 Size,U32 MemType); +GLIB_API u32 GAL_GetAlignment(u32 MemType); + + +/* Error Functions + --------------- */ +GLIB_API GAL_ERROR_CODE GAL_GetLastErrorCode(void); +GLIB_API char * GAL_GetLastErrorText(void); +GLIB_API char * GAL_GetErrorText(GAL_ERROR_CODE Err); +GLIB_API void GAL_MemDump(U32 Type); +GLIB_API void GAL_HaltOnError(void); +GLIB_API void GAL_ReturnOnError(void); + +/* Debug amd diagnostic functions + ------------------------------ */ +GLIB_API s32 GAL_GetSize(MHANDLE hnd); + +/* Debug amd diagnostic functions + ------------------------------ */ +GLIB_API BOOL GAL_CheckMem(U32 Type); +GLIB_API GAL_FILTER GAL_SetAllocFilter(GAL_FILTER NewFilter); +GLIB_API void GAL_IterateUsedMem(U32 MemType,void (*Func)(MHANDLE hnd,void *Addr,U32 Size,const char *Name,int Users,int TimeStamp)); +GLIB_API void GAL_IterateEmptyMem(U32 MemType,void (*Func)(void *Addr,U32 Size,const char *Name)); +GLIB_API int GAL_HowManyUsedRegions(U32 Type); +GLIB_API int GAL_HowManyEmptyRegions(U32 Type); + +GLIB_API int GAL_GetTimeStamp(void); +GLIB_API void GAL_IncTimeStamp(void); +GLIB_API void GAL_SetTimeStamp(int Time); +GLIB_API void GAL_SetErrorChecking(BOOL OnOff); +GLIB_API void GAL_SetVerbosity(GAL_VERB_LEV G); +GLIB_API int GAL_GetNumFreeHeaders(void); +GLIB_API u32 GAL_GetLastTypeAlloced(void); + +GLIB_API BOOL GAL_SortUsedRegionsBySize(U32 Type); +GLIB_API BOOL GAL_SortUsedRegionsByAddress(U32 MemType); + +/* --------------------------------------------------------------------------- + API'd but As Yet Unsupported + ---------------------------- */ +GLIB_API BOOL GAL_Resize(MHANDLE Handle); + + +#ifdef __cplusplus +}; +#endif + +/* --------------------------------------------------------------------------- */ +#endif +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/gdebug.h b/Utils/Libs/GLib/gdebug.h new file mode 100644 index 000000000..ac486c13c --- /dev/null +++ b/Utils/Libs/GLib/gdebug.h @@ -0,0 +1,112 @@ +/* =========================================================================== + File: GDEBUG.H + + Notes: API for machine independant debugging + + Author: Gary Liddon @ 73b + + Created: Wednesday 27th March 1996 + + Copyright (C) 1996 - 1997 Gary Liddon + All rights reserved. + + ============================================================================ */ + +#ifndef __GDEBUG_H__ +#define __GDEBUG_H__ + +/* --------------------------------------------------------------------------- + Includes + -------- */ + +/* Standard Lib + ------------ */ +#include "stdarg.h" + + +/* Glib Includes + ------------- */ +#include "gtypes.h" + + +/* --------------------------------------------------------------------------- + Defines + ------- */ + +/* --------------------------------------------------------------------------- + Typedefs + -------- */ +#ifdef __GL_DEBUG__ +#define ASSERT(p) ( (p) ? (void)0 : (void) DBG_Error(#p,__FILE__,__LINE__) ) +#else +#define ASSERT(p) ( (p) ? (void)0 : (void) DBG_Error(NULL,__FILE__,__LINE__) ) +#endif + +#ifdef __GL_DEBUG__ + +#define DBG_MSG0(a) DBG_SendMessage(a) +#define DBG_MSG1(a,b) DBG_SendMessage(a,b) +#define DBG_MSG2(a,b,c) DBG_SendMessage(a,b,c) +#define DBG_MSG3(a,b,c,d) DBG_SendMessage(a,b,c,d) +#define DBG_MSG4(a,b,c,d,e) DBG_SendMessage(a,b,c,d,e) + +#else + +#define DBG_MSG0(a) +#define DBG_MSG1(a,b) +#define DBG_MSG2(a,b,c) +#define DBG_MSG3(a,b,c,d) +#define DBG_MSG4(a,b,c,d,e) + +#endif + + +/* --------------------------------------------------------------------------- + Enums + ----- */ + +/* --------------------------------------------------------------------------- + Id for each file + ---------------- */ + +/* --------------------------------------------------------------------------- + Externs + ------- */ + +/* --------------------------------------------------------------------------- + Structures + ---------- */ + +/* --------------------------------------------------------------------------- + Supported Functions + ------------------- */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +GLIB_API BOOL DBG_OpenModule(void); +GLIB_API void DBG_Halt(void); +GLIB_API void DBG_PollHost(void); + +GLIB_API void DBG_SendMessage(char *e,...); +GLIB_API void DBG_SendErrorMessage(char *e,...); + +GLIB_API void DBG_SetMessageHandler(void (*Func)(char *e,va_list argptr)); +GLIB_API void DBG_SetErrorMessageHandler(void (*Func)(char *e,va_list argptr)); + +GLIB_API void DBG_Error(char *Text,char *File,int Line); + +GLIB_API void DBG_SetErrorFunc(void (*EFunc)(char *Text,char *File,int Line)); +GLIB_API void DBG_SetPollRoutine(void (*Func)(void)); + +#ifdef __cplusplus +}; +#endif + +/* --------------------------------------------------------------------------- */ +#endif +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/gmain.c b/Utils/Libs/GLib/gmain.c new file mode 100644 index 000000000..adb6480a2 --- /dev/null +++ b/Utils/Libs/GLib/gmain.c @@ -0,0 +1,46 @@ +/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + File: GMAIN.C + + Notes: Main! + + Author: G Robert Liddon @ 73b + + Created: Wednesday 27th March 1996 + + Copyright (C) 1996 DCI Ltd All rights reserved. + ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */ + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Glib Includes + ÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +#include "gmain.h" +#include "gal.h" +#include "gsys.h" +#include "tick.h" +#include "gutils.h" + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function Prototypes + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Main + ÄÄÄÄ */ +void main(void) +{ + GSYS_InitMachine(); + GAL_InitModule(); + TICK_InitModule(); + GU_InitModule(); + + AppMain(); + + while (1) + { + } +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Ends + ÄÄÄÄ */ diff --git a/Utils/Libs/GLib/gmain.h b/Utils/Libs/GLib/gmain.h new file mode 100644 index 000000000..cb8649372 --- /dev/null +++ b/Utils/Libs/GLib/gmain.h @@ -0,0 +1,59 @@ +/* =========================================================================== + File: GMAIN.C + + Notes: Glib Main + + Author: Gary Liddon @ 73b + + Created: Wednesday 27th March 1996 + + Copyright (C) 1996 - 1997 Gary Liddon + All rights reserved. + ============================================================================ */ + +#ifndef __GMAIN_H__ +#define __GMAIN_H__ + +/* --------------------------------------------------------------------------- + Includes + -------- */ +#include "gtypes.h" + +/* --------------------------------------------------------------------------- + Defines + ------- */ + +/* --------------------------------------------------------------------------- + Typedefs + -------- */ + +/* --------------------------------------------------------------------------- + Enums + ----- */ + +/* --------------------------------------------------------------------------- + Externs + ------- */ + +/* --------------------------------------------------------------------------- + Structures + ---------- */ + +/* --------------------------------------------------------------------------- + Supported Functions + ------------------- */ +#ifdef __cplusplus +extern "C" { +#endif + +void AppMain(void); + +#ifdef __cplusplus +}; +#endif + + +/* --------------------------------------------------------------------------- */ +#endif +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/gsys.h b/Utils/Libs/GLib/gsys.h new file mode 100644 index 000000000..9dee96587 --- /dev/null +++ b/Utils/Libs/GLib/gsys.h @@ -0,0 +1,83 @@ +/* ========================================================================== + File: GSYS.H + + Notes: Machine Independant API to target for low level system info + and manipulation + + Author: Gary Liddon + + Copyright (C) 1995 - 1997 Gary Liddon + All rights reserved. + =========================================================================== */ + +#ifndef __GSYS_H__ +#define __GSYS_H__ + +/* --------------------------------------------------------------------------- + Includes + -------- */ +#include "gtypes.h" + +/* --------------------------------------------------------------------------- + Defines + ------- */ + +/* --------------------------------------------------------------------------- + Typedefs + -------- */ + +/* --------------------------------------------------------------------------- + Enums + ----- */ + +/* --------------------------------------------------------------------------- + Externs + ------- */ + +/* --------------------------------------------------------------------------- + Structures + ---------- */ +typedef struct MEM_INFO +{ + void * Addr; + U32 Size; + +} MEM_INFO; + + +/* --------------------------------------------------------------------------- + Supported Functions + ------------------- */ +#ifdef __cplusplus +extern "C" { +#endif + +/* System Initialisation stuff + --------------------------- */ +GLIB_API BOOL GSYS_InitMachine(void); + +/* Stack handling functions + ------------------------ */ +GLIB_API void GSYS_SetStackAndJump(void *Stack,void(*Func)(void *),void *Param); +GLIB_API void GSYS_MarkStack(void * Stack, U32 StackSize); +GLIB_API BOOL GSYS_IsStackCorrupted(void * Stack, U32 StackSize); +GLIB_API BOOL GSYS_CheckPtr(void *Ptr); +GLIB_API BOOL GSYS_IsStackOutOfBounds(void* Stack, U32 StackSize); + +/* Machine Info Functions + ---------------------- */ +GLIB_API const MEM_INFO * GSYS_GetWorkMemInfo(void); + +#ifdef __cplusplus +}; +#endif + +/* Global Vars + ----------- */ +GLIB_API extern UINT GSYS_MemStart; +GLIB_API extern UINT GSYS_MemEnd; + +/* --------------------------------------------------------------------------- */ +#endif +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/gtimer.c b/Utils/Libs/GLib/gtimer.c new file mode 100644 index 000000000..a6357c8c9 --- /dev/null +++ b/Utils/Libs/GLib/gtimer.c @@ -0,0 +1,225 @@ +/* =========================================================================== + File: GTIMER.C + + Notes: Timing Stuff + + Author: G Robert Liddon @ 73b + + Copyright (C) 1996 DCI Ltd All rights reserved. + ============================================================================ */ + +/* --------------------------------------------------------------------------- + Standard Lib Includes + --------------------- */ + +/* Standard Lib + ------------ */ + +/* Glib + ---- */ +#include "gal.h" + +/* Headers + ------- */ +#include "gtimer.h" +#include "gtimsys.h" + +/* --------------------------------------------------------------------------- + Defines and Enums + ----------------- */ + +/* --------------------------------------------------------------------------- + Structs + ------- */ + +/* --------------------------------------------------------------------------- + Vars + ---- */ +MHANDLE hndClocks; +CLOCK * Clocks; +int ClocksOut; +int ClocksInAll; +U32 TimeForAFrame; +U32 ReadTime; + +/* --------------------------------------------------------------------------- + Function Prototypes + ------------------- */ +static void StartClock(CLOCK * C,U32 Id); +static void StopClock(CLOCK * C); +static void ReadTestFunc(void); + +static U32 GetTimer(void) +{ + return((GTIMSYS_GetTimer()*0x10000)/TimeForAFrame); +} + +/* --------------------------------------------------------------------------- + Function: BOOL GTIM_Open(int MaxClocks,U32 RamId); + + Purpose: Creates the timer moduler + + Params: MaxClocks = Max amount of clocks you can have + + Returns: FALSE if an error + + --------------------------------------------------------------------------- */ +BOOL GTIM_Open(int MaxClocks,U32 RamId) +{ + hndClocks=GAL_Alloc(sizeof(CLOCK)*MaxClocks,RamId,NULL); + + if (hndClocks != NULL_HANDLE) + { + Clocks=GAL_Lock(hndClocks); + + if (Clocks) + { + ClocksInAll=MaxClocks; + TimeForAFrame=GTIMSYS_InitTimer(); + GTIM_ClearClocks(); + + ReadTime=GTIM_TimeFunction(ReadTestFunc,5000); + + return(TRUE); + } + } + + return(FALSE); +} + +/* --------------------------------------------------------------------------- + Function: void GTIM_ClearClocks(void) + + Purpose: Reset all clocks + + --------------------------------------------------------------------------- */ +void GTIM_ClearClocks(void) +{ + ClocksOut=0; + GTIMSYS_ResetTimer(); +} + +/* --------------------------------------------------------------------------- + Function: GTHANDLE GTIM_StartClock(U32 Id) + + Purpose: Start a clock + + Params: Id for a clock + + Returns: Handle used to stop the clock + + --------------------------------------------------------------------------- */ +GTHANDLE GTIM_StartClock(U32 Id) +{ + S32 RetHandle; + + if (ClocksOut==ClocksInAll) + RetHandle=NULL_GTHANDLE; + else + { + RetHandle=ClocksOut; + + ClocksOut++; + + StartClock(&Clocks[RetHandle],Id); + } + + return(RetHandle); +} + +/* --------------------------------------------------------------------------- + Function: void GTIM_StopClock(GTHANDLE Hnd) + + Purpose: Stop a clock + + Params: Id for a clock to stop + + --------------------------------------------------------------------------- */ +U32 GTIM_StopClock(GTHANDLE Hnd) +{ + CLOCK * C; + + C=&Clocks[Hnd]; + StopClock(C); + return(C->StopTime-C->StartTime); +} + +/* --------------------------------------------------------------------------- + Function: void GTIM_IterateClocks(void (*Func)(U32 Start,U32 End,U32 Id) + + Purpose: Callback through all the current clocks + + Params: Id for a clock to stop + + --------------------------------------------------------------------------- */ +void GTIM_IterateClocks(void (*Func)(U32 Start,U32 End,U32 Id)) +{ + int f; + CLOCK * C; + + for (f=0,C=Clocks;fStartTime,C->StopTime,C->Id); +} + + +/* --------------------------------------------------------------------------- + Function: U32 GTIM_TimeFunction(void (*Func)(void),int Tries) + + Purpose: Test this functions speed + + Params: Func -> Function to test + Tries = Number of tries to average it out against + + Returns: Time taken + + --------------------------------------------------------------------------- */ +U32 GTIM_TimeFunction(void (*Func)(void),int Tries) +{ + U32 Time0; + U32 Time1; + int f; + + Time0=GetTimer(); + + for (f=0;fStartTime=RealTime; + C->Id=Id; + C->Running=TRUE; +} + +static void StopClock(CLOCK * C) +{ + int RealTime; + + RealTime=GetTimer(); + C->StopTime=RealTime; + C->Running=FALSE; +} + +static void ReadTestFunc(void) +{ + GTHANDLE GtHnd0; + + GtHnd0=GTIM_StartClock(0); + GTIM_StopClock(GtHnd0); +} + +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/gtimer.h b/Utils/Libs/GLib/gtimer.h new file mode 100644 index 000000000..6e5871f40 --- /dev/null +++ b/Utils/Libs/GLib/gtimer.h @@ -0,0 +1,76 @@ +/* =========================================================================== + File: GTIMER.C + + Notes: PSX stuff for timing things + + Author: Gary Liddon + + Copyright (C) 1995 - 1997 Gary Liddon + All rights reserved. + + ============================================================================ */ + +#ifndef __GTIMER_H__ +#define __GTIMER_H__ + +/* --------------------------------------------------------------------------- + Includes + -------- */ + +/* Glib + ---- */ +#include "gtypes.h" + + +/* --------------------------------------------------------------------------- + Defines + ------- */ + +/* --------------------------------------------------------------------------- + Typedefs + -------- */ + +/* --------------------------------------------------------------------------- + Enums + ----- */ + +/* --------------------------------------------------------------------------- + Structures + ---------- */ + +typedef S32 GTHANDLE; + +typedef struct CLOCK +{ + U32 StartTime; + U32 StopTime; + U32 Id; + BOOL Running; + +} CLOCK; + +#define NULL_GTHANDLE -1 + +/* --------------------------------------------------------------------------- + Supported Functions + ------------------- */ +#ifdef __cplusplus +extern "C" { +#endif + +GLIB_API BOOL GTIM_Open(int MaxClocks,U32 RamId); +GLIB_API void GTIM_ClearClocks(void); +GLIB_API GTHANDLE GTIM_StartClock(U32 Id); +GLIB_API U32 GTIM_StopClock(GTHANDLE Hnd); +GLIB_API void GTIM_StopAllClocks(void); +GLIB_API void GTIM_IterateClocks(void (*Func)(U32 Start,U32 End,U32 Id)); +GLIB_API U32 GTIM_TimeFunction(void (*Func)(void),int Tries); + +#ifdef __cplusplus +}; +#endif + +/* --------------------------------------------------------------------------- */ +#endif +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/gtimsys.h b/Utils/Libs/GLib/gtimsys.h new file mode 100644 index 000000000..445485a13 --- /dev/null +++ b/Utils/Libs/GLib/gtimsys.h @@ -0,0 +1,57 @@ +/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + File: GTIMSYS.C + + Notes: Timing system stuff needed by gtim + + Author: G Robert Liddon @ 73b + + Copyright (C) 1996 DCI Ltd All rights reserved. + ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */ + +#ifndef __GTIMSYS_H__ +#define __GTIMSYS_H__ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Includes + ÄÄÄÄÄÄÄÄ */ + +/* Glib + ÄÄÄÄ */ +#include "gtypes.h" + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Defines + ÄÄÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Typedefs + ÄÄÄÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Enums + ÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Structures + ÄÄÄÄÄÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Supported Functions + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +#ifdef __cplusplus +extern "C" { +#endif + +GLIB_API U32 GTIMSYS_InitTimer(void); +GLIB_API void GTIMSYS_ResetTimer(void); +GLIB_API U32 GTIMSYS_GetTimer(void); + +#ifdef __cplusplus +} +#endif + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +#endif +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + ends */ + diff --git a/Utils/Libs/GLib/gtypes.h b/Utils/Libs/GLib/gtypes.h new file mode 100644 index 000000000..73e5d5fdd --- /dev/null +++ b/Utils/Libs/GLib/gtypes.h @@ -0,0 +1,154 @@ +/* ========================================================================== + File: GTYPES.H + + Notes: Standard Non Machine dependant data types used by LIBGAZ + + Author: Gary Liddon @ 73b + + Created: Saturday 16th March 1996 + + Copyright (C) 1996 - 1997 Gary Liddon + All rights reserved. + =========================================================================== */ + +#ifndef __GTYPES_H__ +#define __GTYPES_H__ + +/* --------------------------------------------------------------------------- + Includes + -------- */ + +#include "mtypes.h" + +/* --------------------------------------------------------------------------- + Defines + ------- */ +#ifndef NULL + +#ifdef __cplusplus + +#define NULL 0 + +#else + +#define NULL ((void *)0) + +#endif /* __cplusplus */ +#endif /* NULL */ + +#ifndef NOP +#define NOP ((void)0) +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +/* --------------------------------------------------------------------------- + Typedefs + -------- */ + +#ifndef __GLIB_uint__ +#define __GLIB_uint__ +typedef unsigned int uint; +#endif + +#ifndef __GLIB_uchar__ +#define __GLIB_uchar__ +typedef unsigned char uchar; +#endif + +#ifndef __GLIB_ushort__ +#define __GLIB_ushort__ +typedef unsigned short ushort; +#endif + +#ifndef __GLIB_ulong__ +#define __GLIB_ulong__ +typedef unsigned long ulong; +#endif + +#ifndef __GLIB_UBYTE__ +#define __GLIB_UBYTE__ +typedef u8 UBYTE; +#endif + +#ifndef __GLIB_UWORD__ +#define __GLIB_UWORD__ +typedef u16 UWORD; +#endif + + + +typedef uint UINT; +typedef uchar UCHAR; +typedef ushort USHORT; +typedef ulong ULONG; + +#ifdef __GLIB_BOOLAsInt__ +typedef int BOOL; +#else +typedef u8 BOOL; +#endif + + +/* Lower case versions are defined in mtypes.h */ + +typedef s8 S8; /* signed 8-bit */ +typedef s16 S16; /* signed 16-bit */ +typedef s32 S32; /* signed 32-bit */ + +typedef u8 U8; /* unsigned 8-bit */ +typedef u16 U16; /* unsigned 16-bit */ +typedef u32 U32; /* unsigned 32-bit */ + + +/* More alternative names */ + +typedef s8 int8; +typedef u8 uint8; +typedef u8 byte; + +typedef s16 int16; +typedef u16 uint16; +typedef u16 word; + + +typedef s32 int32; +typedef u32 uint32; +typedef u32 dword; + + +/* 64 bit names + ------------ */ +#ifndef __GLIB_No64__ + +typedef s64 S64; +typedef u64 U64; +typedef s64 int64; +typedef u64 uint64; +typedef u64 qword; + +#endif + +/* GLIB Api Defintion + ------------------ */ +#ifndef GLIB_API +#define GLIB_API +#endif + + + +/* --------------------------------------------------------------------------- + Globals + ------- */ + +/* --------------------------------------------------------------------------- */ +#endif /* __GTYPES_H__ */ +/* --------------------------------------------------------------------------- + ends */ + diff --git a/Utils/Libs/GLib/gutils.c b/Utils/Libs/GLib/gutils.c new file mode 100644 index 000000000..6bf796f1c --- /dev/null +++ b/Utils/Libs/GLib/gutils.c @@ -0,0 +1,132 @@ +/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + File: GUTILS.C + + Notes: Machine indepnant util code + + Author: G Robert Liddon @ 73b + + Project: NBA Hang Time PSX + + Copyright (C) 1996 DCI Ltd All rights reserved. + ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */ + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Includes + ÄÄÄÄÄÄÄÄ */ + +/* Glib + ÄÄÄÄ */ +#include "gsys.h" +#include "gdebug.h" + +/* Game + ÄÄÄÄ */ +#include "gutils.h" + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function Prototypes + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Vars + ÄÄÄÄ */ +U32 RndTabs[6]; + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Tables + ÄÄÄÄÄÄ */ +U32 DefaultRnd[6]= +{ + 0xabcd1234, + 0xffde1534, + 0xffde1534, + 0x001a1010, + 0xf61a1890, + 0x00000002, +}; + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Init the general utilities module + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +BOOL GU_InitModule(void) +{ + GU_SetRndSeed(DefaultRnd); + return(TRUE); +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Init the general utilities module + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void GU_SetRndSeed(U32 *Tab) +{ + int f; + + for (f=0;f<6;f++) + RndTabs[f]=Tab[f]; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Get a random 32 bit unsigned number + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +U32 GU_GetRnd(void) +{ + U32 RetVal; + + RetVal=RndTabs[1]+RndTabs[4]; + + if (RetVal< RndTabs[1] && RetVal < RndTabs[4]) + RetVal++; + + RetVal++; + + RndTabs[0]=RetVal; + + RndTabs[5]=RndTabs[4]; + RndTabs[4]=RndTabs[3]; + RndTabs[3]=RndTabs[2]; + RndTabs[2]=RndTabs[1]; + RndTabs[1]=RndTabs[0]; + + return(RndTabs[0]); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: S32 GU_GetSRnd(void) + Notes: Get a signed random number + Returns: Signed Number + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +S32 GU_GetSRnd(void) +{ + return((S32)GU_GetRnd()); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: U32 GU_GetRndRange(UINT Range) + Notes: Returns a number between 0 and Range-1 + Params: Range -> Max number returned -1 + Returns: unsigned number + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +U32 GU_GetRndRange(UINT Range) +{ + return(GU_GetRnd()%Range); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: UINT GU_AlignVal(UINT w,UINT round) + Notes: Align a value to a boundary (7 round to boundaries of 4 = 8) + Params: w = Value to align + round = Boundaries to align it to + Returns: Aligned number + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +UINT GU_AlignVal(UINT w,UINT round) +{ + w += round - 1; + return (w - w % round); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + ends */ diff --git a/Utils/Libs/GLib/gutils.h b/Utils/Libs/GLib/gutils.h new file mode 100644 index 000000000..b5dfea6b9 --- /dev/null +++ b/Utils/Libs/GLib/gutils.h @@ -0,0 +1,63 @@ +/* ========================================================================== + File: GUTILS.C + + Notes: General miscellaneous utilities + + Author: Gary Liddon + + Copyright (C) 1995 - 1997 Gary Liddon + All rights reserved. + =========================================================================== */ + +#ifndef __GUTILS_H +#define __GUTILS_H + +/* --------------------------------------------------------------------------- + Includes + -------- */ + +/* Glib + ---- */ +#include "gtypes.h" + +/* Includes + -------- */ + +/* --------------------------------------------------------------------------- + Defines, enums & Typedefs + ------------------------- */ +#define STRUCT_OFFSET(type,member) ((int)(&(((type *)0)->member))); + +/* --------------------------------------------------------------------------- + Structure Definitions + --------------------- */ + + +/* --------------------------------------------------------------------------- + Globals + ------- */ +#ifdef __cplusplus +extern "C" { +#endif + + +GLIB_API BOOL GU_InitModule(void); + +/* Random number stuff + ------------------- */ +GLIB_API void GU_SetRndSeed(U32 *Tab); +GLIB_API U32 GU_GetRnd(void); +GLIB_API S32 GU_GetSRnd(void); +GLIB_API U32 GU_GetRndRange(UINT Range); /* 0- Range-1 */ +GLIB_API UINT GU_AlignVal(UINT w,UINT round); + + +#ifdef __cplusplus +}; +#endif + + +/* --------------------------------------------------------------------------- */ +#endif +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/internal.h b/Utils/Libs/GLib/internal.h new file mode 100644 index 000000000..713e6c5d0 --- /dev/null +++ b/Utils/Libs/GLib/internal.h @@ -0,0 +1,334 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + + +#define PCRE_VERSION "2.02 14-Jan-1999" + + +/* This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-1999 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. +----------------------------------------------------------------------------- +*/ + +/* This header contains definitions that are shared between the different +modules, but which are not relevant to the outside. */ + +/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), +define a macro for memmove() if USE_BCOPY is defined. */ + +#ifdef USE_BCOPY +#undef memmove /* some systems may have a macro */ +#define memmove(a, b, c) bcopy(b, a, c) +#endif + +/* Standard C headers plus the external interface definition */ + +#include +#include +#include +#include +#include +#include +#include "pcre.h" + +/* In case there is no definition of offsetof() provided - though any proper +Standard C system should have one. */ + +#ifndef offsetof +#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) +#endif + +/* These are the public options that can change during matching. */ + +#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) + +/* Private options flags start at the most significant end of the two bytes. +The public options defined in pcre.h start at the least significant end. Make +sure they don't overlap! */ + +#define PCRE_FIRSTSET 0x8000 /* first_char is set */ +#define PCRE_STARTLINE 0x4000 /* start after \n for multiline */ +#define PCRE_INGROUP 0x2000 /* compiling inside a group */ + +/* Options for the "extra" block produced by pcre_study(). */ + +#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */ + +/* Masks for identifying the public options which are permitted at compile +time, run time or study time, respectively. */ + +#define PUBLIC_OPTIONS \ + (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ + PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY) + +#define PUBLIC_EXEC_OPTIONS (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL) + +#define PUBLIC_STUDY_OPTIONS 0 /* None defined */ + +/* Magic number to provide a small check against being handed junk. */ + +#define MAGIC_NUMBER 0x50435245 /* 'PCRE' */ + +/* Miscellaneous definitions */ + +typedef int BOOL; + +#define FALSE 0 +#define TRUE 1 + +/* These are escaped items that aren't just an encoding of a particular data +value such as \n. They must have non-zero values, as check_escape() returns +their negation. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_z. The final one must be ESC_REF as subsequent +values are used for \1, \2, \3, etc. There is a test in the code for an escape +greater than ESC_b and less than ESC_X to detect the types that may be +repeated. If any new escapes are put in-between that don't consume a character, +that code will have to change. */ + +enum { ESC_A = 1, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, + ESC_Z, ESC_z, ESC_REF }; + +/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets +that extract substrings. Starting from 1 (i.e. after OP_END), the values up to +OP_EOD must correspond in order to the list of escapes immediately above. */ + +enum { + OP_END, /* End of pattern */ + + /* Values corresponding to backslashed metacharacters */ + + OP_SOD, /* Start of data: \A */ + OP_NOT_WORD_BOUNDARY, /* \B */ + OP_WORD_BOUNDARY, /* \b */ + OP_NOT_DIGIT, /* \D */ + OP_DIGIT, /* \d */ + OP_NOT_WHITESPACE, /* \S */ + OP_WHITESPACE, /* \s */ + OP_NOT_WORDCHAR, /* \W */ + OP_WORDCHAR, /* \w */ + OP_EODN, /* End of data or \n at end of data: \Z. */ + OP_EOD, /* End of data: \z */ + + OP_OPT, /* Set runtime options */ + OP_CIRC, /* Start of line - varies with multiline switch */ + OP_DOLL, /* End of line - varies with multiline switch */ + OP_ANY, /* Match any character */ + OP_CHARS, /* Match string of characters */ + OP_NOT, /* Match anything but the following char */ + + OP_STAR, /* The maximizing and minimizing versions of */ + OP_MINSTAR, /* all these opcodes must come in pairs, with */ + OP_PLUS, /* the minimizing one second. */ + OP_MINPLUS, /* This first set applies to single characters */ + OP_QUERY, + OP_MINQUERY, + OP_UPTO, /* From 0 to n matches */ + OP_MINUPTO, + OP_EXACT, /* Exactly n matches */ + + OP_NOTSTAR, /* The maximizing and minimizing versions of */ + OP_NOTMINSTAR, /* all these opcodes must come in pairs, with */ + OP_NOTPLUS, /* the minimizing one second. */ + OP_NOTMINPLUS, /* This first set applies to "not" single characters */ + OP_NOTQUERY, + OP_NOTMINQUERY, + OP_NOTUPTO, /* From 0 to n matches */ + OP_NOTMINUPTO, + OP_NOTEXACT, /* Exactly n matches */ + + OP_TYPESTAR, /* The maximizing and minimizing versions of */ + OP_TYPEMINSTAR, /* all these opcodes must come in pairs, with */ + OP_TYPEPLUS, /* the minimizing one second. These codes must */ + OP_TYPEMINPLUS, /* be in exactly the same order as those above. */ + OP_TYPEQUERY, /* This set applies to character types such as \d */ + OP_TYPEMINQUERY, + OP_TYPEUPTO, /* From 0 to n matches */ + OP_TYPEMINUPTO, + OP_TYPEEXACT, /* Exactly n matches */ + + OP_CRSTAR, /* The maximizing and minimizing versions of */ + OP_CRMINSTAR, /* all these opcodes must come in pairs, with */ + OP_CRPLUS, /* the minimizing one second. These codes must */ + OP_CRMINPLUS, /* be in exactly the same order as those above. */ + OP_CRQUERY, /* These are for character classes and back refs */ + OP_CRMINQUERY, + OP_CRRANGE, /* These are different to the three seta above. */ + OP_CRMINRANGE, + + OP_CLASS, /* Match a character class */ + OP_REF, /* Match a back reference */ + + OP_ALT, /* Start of alternation */ + OP_KET, /* End of group that doesn't have an unbounded repeat */ + OP_KETRMAX, /* These two must remain together and in this */ + OP_KETRMIN, /* order. They are for groups the repeat for ever. */ + + /* The assertions must come before ONCE and COND */ + + OP_ASSERT, /* Positive lookahead */ + OP_ASSERT_NOT, /* Negative lookahead */ + OP_ASSERTBACK, /* Positive lookbehind */ + OP_ASSERTBACK_NOT, /* Negative lookbehind */ + OP_REVERSE, /* Move pointer back - used in lookbehind assertions */ + + /* ONCE and COND must come after the assertions, with ONCE first, as there's + a test for >= ONCE for a subpattern that isn't an assertion. */ + + OP_ONCE, /* Once matched, don't back up into the subpattern */ + OP_COND, /* Conditional group */ + OP_CREF, /* Used to hold an extraction string number */ + + OP_BRAZERO, /* These two must remain together and in this */ + OP_BRAMINZERO, /* order. */ + + OP_BRA /* This and greater values are used for brackets that + extract substrings. */ +}; + +/* The highest extraction number. This is limited by the number of opcodes +left after OP_BRA, i.e. 255 - OP_BRA. We actually set it somewhat lower. */ + +#define EXTRACT_MAX 99 + +/* The texts of compile-time error messages are defined as macros here so that +they can be accessed by the POSIX wrapper and converted into error codes. Yes, +I could have used error codes in the first place, but didn't feel like changing +just to accommodate the POSIX wrapper. */ + +#define ERR1 "\\ at end of pattern" +#define ERR2 "\\c at end of pattern" +#define ERR3 "unrecognized character follows \\" +#define ERR4 "numbers out of order in {} quantifier" +#define ERR5 "number too big in {} quantifier" +#define ERR6 "missing terminating ] for character class" +#define ERR7 "invalid escape sequence in character class" +#define ERR8 "range out of order in character class" +#define ERR9 "nothing to repeat" +#define ERR10 "operand of unlimited repeat could match the empty string" +#define ERR11 "internal error: unexpected repeat" +#define ERR12 "unrecognized character after (?" +#define ERR13 "too many capturing parenthesized sub-patterns" +#define ERR14 "missing )" +#define ERR15 "back reference to non-existent subpattern" +#define ERR16 "erroffset passed as NULL" +#define ERR17 "unknown option bit(s) set" +#define ERR18 "missing ) after comment" +#define ERR19 "too many sets of parentheses" +#define ERR20 "regular expression too large" +#define ERR21 "failed to get memory" +#define ERR22 "unmatched parentheses" +#define ERR23 "internal error: code overflow" +#define ERR24 "unrecognized character after (?<" +#define ERR25 "lookbehind assertion is not fixed length" +#define ERR26 "malformed number after (?(" +#define ERR27 "conditional group contains more than two branches" +#define ERR28 "assertion expected after (?(" + +/* All character handling must be done as unsigned characters. Otherwise there +are problems with top-bit-set characters and functions such as isspace(). +However, we leave the interface to the outside world as char *, because that +should make things easier for callers. We define a short type for unsigned char +to save lots of typing. I tried "uchar", but it causes problems on Digital +Unix, where it is defined in sys/types, so use "uschar" instead. */ + +typedef unsigned char uschar; + +/* The real format of the start of the pcre block; the actual code vector +runs on as long as necessary after the end. */ + +typedef struct real_pcre { + unsigned int magic_number; + const unsigned char *tables; + unsigned short int options; + unsigned char top_bracket; + unsigned char top_backref; + unsigned char first_char; + unsigned char code[1]; +} real_pcre; + +/* The real format of the extra block returned by pcre_study(). */ + +typedef struct real_pcre_extra { + unsigned char options; + unsigned char start_bits[32]; +} real_pcre_extra; + + +/* Structure for passing "static" information around between the functions +doing the compiling, so that they are thread-safe. */ + +typedef struct compile_data { + const uschar *lcc; /* Points to lower casing table */ + const uschar *fcc; /* Points to case-flippint table */ + const uschar *cbits; /* Points to character type table */ + const uschar *ctypes; /* Points to table of type maps */ +} compile_data; + +/* Structure for passing "static" information around between the functions +doing the matching, so that they are thread-safe. */ + +typedef struct match_data { + int errorcode; /* As it says */ + int *offset_vector; /* Offset vector */ + int offset_end; /* One past the end */ + int offset_max; /* The maximum usable for return data */ + const uschar *lcc; /* Points to lower casing table */ + const uschar *ctypes; /* Points to table of type maps */ + BOOL offset_overflow; /* Set if too many extractions */ + BOOL notbol; /* NOTBOL flag */ + BOOL noteol; /* NOTEOL flag */ + BOOL endonly; /* Dollar not before final \n */ + const uschar *start_subject; /* Start of the subject string */ + const uschar *end_subject; /* End of the subject string */ + const uschar *end_match_ptr; /* Subject position at end match */ + int end_offset_top; /* Highwater mark at end of match */ +} match_data; + +/* Bit definitions for entries in the pcre_ctypes table. */ + +#define ctype_space 0x01 +#define ctype_letter 0x02 +#define ctype_digit 0x04 +#define ctype_xdigit 0x08 +#define ctype_word 0x10 /* alphameric or '_' */ +#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ + +/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set +of bits for a class map. */ + +#define cbit_digit 0 /* for \d */ +#define cbit_word 32 /* for \w */ +#define cbit_space 64 /* for \s */ +#define cbit_length 96 /* Length of the cbits table */ + +/* Offsets of the various tables from the base tables pointer, and +total length. */ + +#define lcc_offset 0 +#define fcc_offset 256 +#define cbits_offset 512 +#define ctypes_offset (cbits_offset + cbit_length) +#define tables_length (ctypes_offset + 256) + +/* End of internal.h */ diff --git a/Utils/Libs/GLib/ll.c b/Utils/Libs/GLib/ll.c new file mode 100644 index 000000000..6a9603921 --- /dev/null +++ b/Utils/Libs/GLib/ll.c @@ -0,0 +1,52 @@ +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + File: LL.C + + Notes: General Link List Handling Code + + Author: G Robert Liddon @ Croydon + Created: Wednesday 6th October 1994 + + Copyright (C) 1994 G Robert Liddon + All rights reserved. + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Includes + ÄÄÄÄÄÄÄÄ */ +#include "ll.h" + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Defines + ÄÄÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Detach from a list + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void LL_DetachFromList(LinkList **Head,LinkList *ThisObj) +{ + if (ThisObj->Prev) + ThisObj->Prev->Next=ThisObj->Next; + else + *Head=ThisObj->Next; + + if (ThisObj->Next) + ThisObj->Next->Prev=ThisObj->Prev; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Add To A List + ÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void LL_AddToList(LinkList **Head,LinkList *ThisObj) +{ + ThisObj->Prev=NULL; + + if ((ThisObj->Next=*Head)) + ThisObj->Next->Prev=ThisObj; + + *Head=ThisObj; +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + ends + ÄÄÄÄ */ diff --git a/Utils/Libs/GLib/ll.h b/Utils/Libs/GLib/ll.h new file mode 100644 index 000000000..47668328e --- /dev/null +++ b/Utils/Libs/GLib/ll.h @@ -0,0 +1,49 @@ +/* =========================================================================== + File: LL.C + + Notes: General Link List Handling Code + + Author: G Robert Liddon @ Croydon + Created: Wednesday 6th October 1994 + + Copyright (C) 1995 - 1997 Gary Liddon + All rights reserved. + =========================================================================== */ + +#ifndef __LL_H +#define __LL_H + +/* --------------------------------------------------------------------------- + Includes + -------- */ +#include "gtypes.h" + +/* --------------------------------------------------------------------------- + Structures + ---------- */ +typedef struct LinkList +{ + struct LinkList *Next; + struct LinkList *Prev; + +} LinkList; + + +/* --------------------------------------------------------------------------- + Globals + ------- */ +#ifdef __cplusplus +extern "C" { +#endif + +GLIB_API void LL_DetachFromList(LinkList **Head,LinkList *ThisObj); +GLIB_API void LL_AddToList(LinkList **Head,LinkList *ThisObj); + +#ifdef __cplusplus +}; +#endif + +/* --------------------------------------------------------------------------- */ +#endif +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/maketables.c b/Utils/Libs/GLib/maketables.c new file mode 100644 index 000000000..01943d37c --- /dev/null +++ b/Utils/Libs/GLib/maketables.c @@ -0,0 +1,109 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Written by: Philip Hazel + + Copyright (c) 1997-1999 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. +----------------------------------------------------------------------------- + +See the file Tech.Notes for some information on the internals. +*/ + + +/* This file is compiled on its own as part of the PCRE library. However, +it is also included in the compilation of dftables.c, in which case the macro +DFTABLES is defined. */ + +#ifndef DFTABLES +#include "internal.h" +#endif + + + +/************************************************* +* Create PCRE character tables * +*************************************************/ + +/* This function builds a set of character tables for use by PCRE and returns +a pointer to them. They are build using the ctype functions, and consequently +their contents will depend upon the current locale setting. When compiled as +part of the library, the store is obtained via pcre_malloc(), but when compiled +inside dftables, use malloc(). + +Arguments: none +Returns: pointer to the contiguous block of data +*/ + +unsigned const char * +pcre_maketables(void) +{ +unsigned char *yield, *p; +int i; + +#ifndef DFTABLES +yield = (pcre_malloc)(tables_length); +#else +yield = malloc(tables_length); +#endif + +if (yield == NULL) return NULL; +p = yield; + +/* First comes the lower casing table */ + +for (i = 0; i < 256; i++) *p++ = tolower(i); + +/* Next the case-flipping table */ + +for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i); + +/* Then the character class tables */ + +memset(p, 0, cbit_length); +for (i = 0; i < 256; i++) + { + if (isdigit(i)) p[cbit_digit + i/8] |= 1 << (i&7); + if (isalnum(i) || i == '_') + p[cbit_word + i/8] |= 1 << (i&7); + if (isspace(i)) p[cbit_space + i/8] |= 1 << (i&7); + } +p += cbit_length; + +/* Finally, the character type table */ + +for (i = 0; i < 256; i++) + { + int x = 0; + if (isspace(i)) x += ctype_space; + if (isalpha(i)) x += ctype_letter; + if (isdigit(i)) x += ctype_digit; + if (isxdigit(i)) x += ctype_xdigit; + if (isalnum(i) || i == '_') x += ctype_word; + if (strchr("*+?{^.$|()[", i) != 0) x += ctype_meta; + *p++ = x; + } + +return yield; +} + +/* End of maketables.c */ diff --git a/Utils/Libs/GLib/objs.c b/Utils/Libs/GLib/objs.c new file mode 100644 index 000000000..051ebdcb8 --- /dev/null +++ b/Utils/Libs/GLib/objs.c @@ -0,0 +1,800 @@ +/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + File: OBJS.C + + Notes: Genric Object Server + + Author: G Robert Liddon @ The Park + + Created: Wednesday 5th April, 1995 + + Copyright (C) 1995 G Robert Liddon + All rights reserved. + ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Includes + ÄÄÄÄÄÄÄÄ */ + +/* Glib + ÄÄÄÄ */ +#include "objs.h" +#include "gal.h" +#include "gdebug.h" + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function Prototypes + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_DetachOLFromList(OBJ_LIST **Head,OBJ_LIST *ThisObj); + +static void OBJS_AddOLToList(OBJ_LIST **Head,OBJ_LIST *ThisObj); +static void OBJS_DetachFromList(OBJ_STRUCT **Head,OBJ_STRUCT *ThisObj); +static void OBJS_AddToList(OBJ_STRUCT **Head,OBJ_STRUCT *ThisObj); +static void SortList(OBJ_LIST *OL); + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Vars + ÄÄÄÄ */ +int ObjsAlloced; /* Number of currently active objs */ +OBJ_LIST * ListOfObjLists; /* Head of the list of print Qs */ +BOOL AddVels; /* Should vels be auto added or not */ +U32 MemId; +void (*ProcessEpilog)(void); /* Called after processobj */ +void (*ProcessProlog)(void); /* Called after processobj */ +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_OpenModule(U32 MemType) + Purpose: Initialise the object module for use. + Params: MyMaxObjs = Max objs to alloc for + MemType = GAL memtype to alloc in + Returns: TRUE if opened succesfuly + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +BOOL OBJS_OpenModule(U32 MemType) +{ + MemId=MemType; + ObjsAlloced=0; + ListOfObjLists=NULL; + AddVels=TRUE; + + ProcessEpilog=NULL; + ProcessProlog=NULL; + + return TRUE; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: BOOL OBJS_CloseModule(void) + Purpose: Closes the active obj module + Returns: TRUE if closed succesfuly + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +BOOL OBJS_CloseModule(void) +{ + OBJS_DestroyAllObjs(); + return TRUE; +} + + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_AddDisplayList(OBJ_LIST *OL) + Purpose: Add a display list + Params: OL -> Object List to add + + Returns: -> Created object if succesfull + NULL if not + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_AddDisplayList(OBJ_LIST *OL) +{ + OBJS_AddOLToList(&ListOfObjLists,OL); + OL->Head=NULL; + OL->Visible=TRUE; + OL->SortCompare=NULL; +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: OBJ_STRUCT *OBJS_MakeObj(const OBJ_TYPE_INFO *OTI,OBJ_LIST *OL,void *Ptr) + Purpose: Make an object + Params: OTI -> Obj type info + OL -> Object List to add to + Ptr -> Instance specefic info + + Returns: -> Created object if succesfull + NULL if not + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +OBJ_STRUCT *OBJS_CreateObjMulti(const OBJ_TYPE_INFO *OTI,OBJ_LIST *OL,GAL_STRUCT * G,void *Ptr) +{ + UINT TotalMem; + + TotalMem=GAL_ProcessMultiStruct(G,MemId); + + return(OBJS_CreateObj(OTI,OL,(int)TotalMem,Ptr)); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: OBJ_STRUCT *OBJS_MakeObj(const OBJ_TYPE_INFO *OTI,OBJ_LIST *OL,void *Ptr) + Purpose: Make an object + Params: OTI -> Obj type info + OL -> Object List to add to + Ptr -> Instance specefic info + + Returns: -> Created object if succesfull + NULL if not + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +OBJ_STRUCT *OBJS_CreateObj(const OBJ_TYPE_INFO *OTI,OBJ_LIST *OL,int DataSize,void *Ptr) +{ + MHANDLE ObjHandle; + + int SizeOfObjstruct; + int SizeOfData; + + OBJ_STRUCT * ThisObj; + + + SizeOfObjstruct=GAL_AlignSizeToType(sizeof(OBJ_STRUCT),MemId); + if (SizeOfObjstruct==-1) + return(NULL); + + SizeOfData=GAL_AlignSizeToType((U32)DataSize,MemId); + if (SizeOfData==-1) + return(NULL); + + ObjHandle=GAL_Alloc((U32)(SizeOfObjstruct+SizeOfData),MemId,"OBJ"); + if (ObjHandle==NULL_HANDLE) + return(NULL); + + + ThisObj=GAL_Lock(ObjHandle); + + if (ThisObj) + { + OBJS_AddToList(&OL->Head,ThisObj); + + ThisObj->MemHandle=ObjHandle; + ThisObj->Data=(void *)((U32)ThisObj + SizeOfObjstruct); + + ThisObj->OTI=OTI; + ThisObj->OL=OL; + + ThisObj->ID=0; + ThisObj->XVel=0; + ThisObj->YVel=0; + ThisObj->ZVel=0; + + if (ThisObj->OTI->Constructor) + ThisObj->OTI->Constructor(ThisObj,Ptr); + + ObjsAlloced++; + } + + return ThisObj; +} + + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_ProcessObjsEpilogue(void (*Func)(void)) + + Purpose: Add a call back that's executed after OBJS_ProcessObjs + + Params: Func -> Function + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_ProcessObjsEpilogue(void (*Func)(void)) +{ + ProcessEpilog=Func; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_ProcessObjsPrologue(void (*Func)(void)) + + Purpose: Add a call back that's executed before OBJS_ProcessObjs + + Params: Func -> Function + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_ProcessObjsPrologue(void (*Func)(void)) +{ + ProcessProlog=Func; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_DetachFromList(OBJ_STRUCT **Head,OBJ_STRUCT *ThisObj) + Purpose: Take an object from an obj list + Params: Head -> Head ptr of list + ThisObj -> Obj to add + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +static void OBJS_DetachFromList(OBJ_STRUCT **Head,OBJ_STRUCT *ThisObj) +{ + if (ThisObj->Prev) + ThisObj->Prev->Next=ThisObj->Next; + else + *Head=ThisObj->Next; + + if (ThisObj->Next) + ThisObj->Next->Prev=ThisObj->Prev; +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_AddToList(OBJ_STRUCT **Head,OBJ_STRUCT *ThisObj) + Purpose: Add an object to a obj list + Params: Head -> Head ptr of list + ThisObj -> Obj to add + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +static void OBJS_AddToList(OBJ_STRUCT **Head,OBJ_STRUCT *ThisObj) +{ + ThisObj->Prev=NULL; + + if ((ThisObj->Next=*Head)) + ThisObj->Next->Prev=ThisObj; + + *Head=ThisObj; +} + + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_DetachFromList(OBJ_STRUCT **Head,OBJ_STRUCT *ThisObj) + Purpose: Take an object from an obj list + Params: Head -> Head ptr of list + ThisObj -> Obj to add + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_DetachOLFromList(OBJ_LIST **Head,OBJ_LIST *ThisObj) +{ + if (ThisObj->Prev) + ThisObj->Prev->Next=ThisObj->Next; + else + *Head=ThisObj->Next; + + if (ThisObj->Next) + ThisObj->Next->Prev=ThisObj->Prev; +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_AddToList(OBJ_STRUCT **Head,OBJ_STRUCT *ThisObj) + Purpose: Add an object to a obj list + Params: Head -> Head ptr of list + ThisObj -> Obj to add + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +static void OBJS_AddOLToList(OBJ_LIST **Head,OBJ_LIST *ThisObj) +{ + ThisObj->Prev=NULL; + + if ((ThisObj->Next=*Head)) + ThisObj->Next->Prev=ThisObj; + + *Head=ThisObj; +} + + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Change this Object's Print List + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_ChangeDisplayList(OBJ_STRUCT *O,OBJ_LIST *NewList) +{ + if (NewList != O->OL) + { + OBJS_DetachFromList(&O->OL->Head,O); + OBJS_AddToList(&NewList->Head,O); + O->OL=NewList; + } +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_ProcessObjs(void) + Purpose: Print the display lists + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_ProcessObjs(void) +{ + OBJ_LIST *ThisList; + + if (ProcessProlog) + ProcessProlog(); + + ThisList=ListOfObjLists; + + while (ThisList) + { + + if (ThisList->Visible) + { + { + OBJ_STRUCT *ObjList; + + SortList(ThisList); + + ObjList=ThisList->Head; + + while (ObjList) + { + if (AddVels) + { + ObjList->XPos+=ObjList->XVel; + ObjList->YPos+=ObjList->YVel; + ObjList->ZPos+=ObjList->ZVel; + } + + if (ObjList->OTI->Printer) + ObjList->OTI->Printer(ObjList,ThisList); + + ObjList=ObjList->Next; + } + } + } + + ThisList=ThisList->Next; + } + + if (ProcessEpilog) + ProcessEpilog(); +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_ProcessObjs(void) + Purpose: Print the display lists + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_ObjsVelAddOnly(void) +{ + OBJ_LIST *ThisList; + + ThisList=ListOfObjLists; + + while (ThisList) + { + + if (ThisList->Visible) + { + { + OBJ_STRUCT *ObjList; + + SortList(ThisList); + + ObjList=ThisList->Head; + + while (ObjList) + { + if (AddVels) + { + ObjList->XPos+=ObjList->XVel; + ObjList->YPos+=ObjList->YVel; + ObjList->ZPos+=ObjList->ZVel; + } + + ObjList=ObjList->Next; + } + } + } + + ThisList=ThisList->Next; + } +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_DestroyAllObjs(void) + Purpose: Delete all objects in all lists + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_DestroyAllObjs(void) +{ + OBJ_LIST *ThisList; + ThisList=ListOfObjLists; + + while (ThisList) + { + OBJS_DestroyListObjs(ThisList); + ThisList=ThisList->Next; + } +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_DestroyListObjs(OBJ_LIST *OL) + Purpose: Destroy all the objs in a list if list is killable + Params: OL -> List + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_DestroyListObjs(OBJ_LIST *OL) +{ + + if (OL->Killable) + { + OBJ_STRUCT *O; + O=OL->Head; + + while (O) + O=OBJS_DestroyObj(O); + } +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_DestroyAllObjsOfAType(U32 Type,U32 AndMask) + Purpose: Go through all display lists and destroy objs + of a certain type. + if (O->ID&AndMask)==Type then obj is destroyed + Params: Type = Type of objs to destroy + AndMask = And maks + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_DestroyAllObjsOfAType(U32 Type,U32 AndMask) +{ + OBJ_LIST *ThisList; + + ThisList=ListOfObjLists; + + while (ThisList) + { + OBJS_DestroyListObjsOfAType(ThisList,Type,AndMask); + ThisList=ThisList->Next; + } +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_DestroyListObjsOfAType(OBJ_LIST *OL,U32 Type,U32 AndMask) + Purpose: Go through a single display list and destroy objs + of a certain type. + if (O->ID&AndMask)==Type then obj is destroyed + Params: Type = Type of objs to destroy + AndMask = And maks + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_DestroyListObjsOfAType(OBJ_LIST *OL,U32 Type,U32 AndMask) +{ + OBJ_STRUCT *O; + + O=OL->Head; + + while (O) + { + OBJ_STRUCT * NextO; + + NextO=O->Next; + + if (((O->ID)&AndMask)==Type) + OBJS_DestroyObj(O); + + O=NextO; + } +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: OBJ_STRUCT *OBJS_DestroyObj(OBJ_STRUCT *O) + Purpose: Destroy an object + Params: O -> Obj to destroy + Returns: -> to what was next in list + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +OBJ_STRUCT *OBJS_DestroyObj(OBJ_STRUCT *O) +{ + MHANDLE ObjHandle; + OBJ_STRUCT * RetObj; + BOOL GalRet; + + ObjHandle=O->MemHandle; + + RetObj=O->Next; + + if (O->OTI->Destructor) + O->OTI->Destructor(O); + + OBJS_DetachFromList(&O->OL->Head,O); + + O->Next=NULL; /* Handy for debugging */ + O->Prev=NULL; + + GalRet=GAL_Free(ObjHandle); + ASSERT(GalRet); + + ObjsAlloced--; + + return RetObj; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_IterateAllObjs(U32 Type,U32 AndMask,void (*CallBack)(OBJ_STRUCT *O)) + Purpose: Go through all display lists and do a callback for objs + of a certain type. + if (O->ID&AndMask)==Type then obj is callbacked + Params: Type = Type of objs to callback + AndMask = And maks + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_IterateAllObjs(U32 Type,U32 AndMask,void (*CallBack)(OBJ_STRUCT *O)) +{ + OBJ_LIST *ThisList; + + ThisList=ListOfObjLists; + + while (ThisList) + { + OBJS_IterateListObjs(ThisList,Type,AndMask,CallBack); + ThisList=ThisList->Next; + } +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_IterateListObjs(OBJ_LIST *OL,U32 Type,U32 AndMask,void (*CallBack)(OBJ_STRUCT *O)) + Purpose: Go through a single display list and do a callback for objs + of a certain type. + if (O->ID&AndMask)==Type then obj is callbacked + Params: Type = Type of objs to callback + AndMask = And maks + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_IterateListObjs(OBJ_LIST *OL,U32 Type,U32 AndMask,void (*CallBack)(OBJ_STRUCT *O)) +{ + OBJ_STRUCT *O; + + O=OL->Head; + + while (O) + { + OBJ_STRUCT * NextO; + + NextO=O->Next; + + if (((O->ID)&AndMask)==Type) + CallBack(O); + + O=NextO; + } +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Set the World's XY + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_SetListXY(OBJ_LIST *OL,S32 X,S32 Y) +{ + OBJS_SetListX(OL,X); + OBJS_SetListY(OL,Y); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Set the World's X + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_SetListX(OBJ_LIST *OL,S32 X) +{ + OL->X=X; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Set the World's X + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_SetListY(OBJ_LIST *OL,S32 Y) +{ + OL->Y=Y; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Make a Priority level of sprites undestroyable + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_MakeListImmortal(OBJ_LIST *OL) +{ + OL->Killable=FALSE; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Make a Priority level of sprites Destroyable + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_MakeListMortal(OBJ_LIST *OL) +{ + OL->Killable=TRUE; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void MKOBJS_NoVels(void) + Purpose: Say no vels to be addes + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +BOOL OBJS_NoAddVels(void) +{ + BOOL OldVels; + + OldVels=AddVels; + AddVels=FALSE; + return(OldVels); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void MKOBJS_AddVels(void) + Purpose: Say vels to be addes + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +BOOL OBJS_AddVels(void) +{ + BOOL OldVels; + + OldVels=AddVels; + AddVels=TRUE; + return(OldVels); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_MakeListInvisible(OBJ_LIST *OL) + Purpose: Mark this list as invisble + Params: OL - > List to mark + Returns: Previous visible / invisible state of list + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +BOOL OBJS_MakeListInvisible(OBJ_LIST *OL) +{ + BOOL OldState; + OldState=OL->Visible; + OL->Visible=FALSE; + return(OldState); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_MakeListInvisible(OBJ_LIST *OL) + Purpose: Mark this list as visble + Params: OL - > List to mark + Returns: Previous visible / invisible state of list + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +BOOL OBJS_MakeListVisible(OBJ_LIST *OL) +{ + BOOL OldState; + OldState=OL->Visible; + OL->Visible=TRUE; + return(OldState); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Make a Priority level of sprites Invisible + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ*/ +BOOL OBJS_GetVelState(void) +{ + return AddVels; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: BOOL OBJS_SetVelState(BOOL NewState) + Purpose: Set wether to add vels + Params: Newstate = + Returns: Previous addvel state of list + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +BOOL OBJS_SetVelState(BOOL NewState) +{ + BOOL OldState; + + OldState=AddVels; + AddVels=NewState; + return(OldState); +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: void OBJS_SetSortCompare(OBJ_LIST *OL,void (*CompRout)(OBJ_STRUCT *O1,OBJ_STRUCT *O1)) + Purpose: Set the call back used in the sort routine + Params: OL -> List to set sort routine for + CompRout -> Function to do the compare + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void OBJS_SetSortCompare(OBJ_LIST *OL,BOOL (*CompRout)(OBJ_STRUCT *O1,OBJ_STRUCT *O2)) +{ + OL->SortCompare=CompRout; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: int OBJS_GetWidth(OBJ_STRUCT *O) + Purpose: Get an objs width + Params: O -> Obj 2 get width off + Returns: Width, zero if no width routine + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +int OBJS_GetWidth(OBJ_STRUCT *O) +{ + if (O->OTI->GetWidth) + return(O->OTI->GetWidth(O)); + else + return(0); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: int OBJS_GetWidth(OBJ_STRUCT *O) + Purpose: Get an objs width + Params: O -> Obj 2 get width off + Returns: Width, zero if no width routine + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +int OBJS_GetHeight(OBJ_STRUCT *O) +{ + if (O->OTI->GetHeight) + return(O->OTI->GetHeight(O)); + else + return(0); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: int OBJS_GetXOff(OBJ_STRUCT *O) + Purpose: Get an objs X offset + Params: O -> Obj 2 get width off + Returns: Width, zero if no width routine + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +int OBJS_GetXOff(OBJ_STRUCT *O) +{ + if (O->OTI->GetXOff) + return(O->OTI->GetXOff(O)); + else + return(0); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: int OBJS_GetYOff(OBJ_STRUCT *O) + Purpose: Get an objs X offset + Params: O -> Obj 2 get width off + Returns: Width, zero if no width routine + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +int OBJS_GetYOff(OBJ_STRUCT *O) +{ + if (O->OTI->GetYOff) + return(O->OTI->GetYOff(O)); + else + return(0); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: int OBJS_GetYOff(OBJ_STRUCT *O) + Purpose: Get an objs X offset + Params: O -> Obj 2 get width off + Returns: Width, zero if no width routine + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +int OBJS_GetPal(OBJ_STRUCT *O) +{ + if (O->OTI->GetPal) + return(O->OTI->GetPal(O)); + else + return(0); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: static void SortList(OBJ_LIST *OL) + Purpose: Sort this list + Params: OL -> List to sort + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +static void SortList(OBJ_LIST *OL) +{ + if (OL->SortCompare && OL->Head && OL->Head->Next) + { + BOOL DidSwap; + + do + { + OBJ_STRUCT * O; + OBJ_STRUCT * NO; + + O=OL->Head; + + DidSwap=FALSE; + + do + { + NO=O->Next; + + if (OL->SortCompare(O,NO)) + { + O->Next=NO->Next; + NO->Next=O; + + NO->Prev=O->Prev; + O->Prev=NO; + + if (NO->Prev) + NO->Prev->Next=NO; + else + OL->Head=NO; + + if (O->Next) + O->Next->Prev=O; + + DidSwap=TRUE; + } + else + O=O->Next; + } + while (O->Next); + } + while(DidSwap); + + } +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: int OBJS_GetObjsAlloced(void) + Purpose: Tell us how many objects are out + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +int OBJS_GetObjsAlloced(void) +{ + return(ObjsAlloced); +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + ends */ diff --git a/Utils/Libs/GLib/objs.h b/Utils/Libs/GLib/objs.h new file mode 100644 index 000000000..ff51a9910 --- /dev/null +++ b/Utils/Libs/GLib/objs.h @@ -0,0 +1,157 @@ +/* =========================================================================== + File: OBJS.H + + Notes: Genric Object Server + + Author: G Robert Liddon @ The Park + + Created: Wednesday 5th April, 1995 + + Copyright (C) 1995 - 1997 Gary Liddon + All rights reserved. + ============================================================================ */ + +#ifndef __GAZ_OBJS_H +#define __GAZ_OBJS_H + +/* --------------------------------------------------------------------------- + Includes + -------- */ + +/* Glib + ---- */ +#include "gtypes.h" +#include "gal.h" + +/* --------------------------------------------------------------------------- + Typedefs, Defines & Structs + --------------------------- */ + +struct OBJ_LIST; +struct OBJ_STRUCT; + +/* Information about a type of object + ---------------------------------- */ +typedef struct OBJ_TYPE_INFO +{ + void (*Constructor)(struct OBJ_STRUCT *O,void *Ptr); /* Object constructor routine */ + void (*Destructor)(struct OBJ_STRUCT *O); /* Object destructor routine */ + void (*Printer)(struct OBJ_STRUCT *O,struct OBJ_LIST *OL); /* Individual object printer */ + int (*GetWidth)(struct OBJ_STRUCT *O); /* Get this obj width */ + int (*GetHeight)(struct OBJ_STRUCT *O); /* Get this obj height */ + int (*GetXOff)(struct OBJ_STRUCT *O); /* Get this obj x */ + int (*GetYOff)(struct OBJ_STRUCT *O); /* Get this obj y */ + int (*GetPal)(struct OBJ_STRUCT *O); /* Get this obj pal */ + +} OBJ_TYPE_INFO; + +/* A List of objects + ----------------- */ +typedef struct OBJ_LIST +{ + U32 PrintDepth; + BOOL Visible; + BOOL Killable; + + char * ListName; + + struct OBJ_LIST *Prev; + struct OBJ_LIST *Next; + + S32 X,Y,Z; + struct OBJ_STRUCT *Head; + + BOOL (*SortCompare)(struct OBJ_STRUCT *O1,struct OBJ_STRUCT *O2); + +} OBJ_LIST; + + +/* An Object itself + ---------------- */ +typedef struct OBJ_STRUCT +{ + struct OBJ_STRUCT *Next; + struct OBJ_STRUCT *Prev; + + U32 ID; + + S32 XPos,YPos,ZPos; + S32 XVel,YVel,ZVel; + + const OBJ_TYPE_INFO * OTI; /* Object type info */ + OBJ_LIST * OL; /* Obj list we're currently in */ + + void * Data; + + MHANDLE MemHandle; + +} OBJ_STRUCT; + + +/* --------------------------------------------------------------------------- + Globals Functions + ----------------- */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Module Management + ----------------- */ +GLIB_API BOOL OBJS_OpenModule(U32 MemType); +GLIB_API BOOL OBJS_CloseModule(void); + +GLIB_API void OBJS_ProcessObjs(void); + +GLIB_API void OBJS_ProcessObjsPrologue(void (*Func)(void)); +GLIB_API void OBJS_ProcessObjsEpilogue(void (*Func)(void)); + +GLIB_API void OBJS_AddDisplayList(OBJ_LIST *OL); + +GLIB_API BOOL OBJS_AddVels(void); +GLIB_API BOOL OBJS_NoAddVels(void); +GLIB_API BOOL OBJS_GetVelState(void); +GLIB_API BOOL OBJS_SetVelState(BOOL NewState); + +/* Object Management + ----------------- */ +GLIB_API OBJ_STRUCT * OBJS_CreateObj(const OBJ_TYPE_INFO *OTI,OBJ_LIST *OL,int DataSize,void *Ptr); +GLIB_API OBJ_STRUCT * OBJS_CreateObjMulti(const OBJ_TYPE_INFO *OTI,OBJ_LIST *OL,GAL_STRUCT * G,void *Ptr); +GLIB_API void OBJS_ChangeDisplayList(OBJ_STRUCT *O,OBJ_LIST *NewList); +GLIB_API OBJ_STRUCT * OBJS_DestroyObj(OBJ_STRUCT *O); + +GLIB_API void OBJS_DestroyAllObjs(void); +GLIB_API void OBJS_DestroyListObjs(OBJ_LIST *OL); +GLIB_API void OBJS_DestroyAllObjsOfAType(U32 Type,U32 AndMask); +GLIB_API void OBJS_DestroyListObjsOfAType(OBJ_LIST *OL,U32 Type,U32 AndMask); + +GLIB_API void OBJS_IterateAllObjs(U32 Type,U32 AndMask,void (*CallBack)(OBJ_STRUCT *O)); +GLIB_API void OBJS_IterateListObjs(OBJ_LIST *OL,U32 Type,U32 AndMask,void (*CallBack)(OBJ_STRUCT *O)); + +/* List Management + --------------- */ +GLIB_API void OBJS_SetListXY(OBJ_LIST *OL,S32 X,S32 Y); +GLIB_API void OBJS_SetListX(OBJ_LIST *OL,S32 X); +GLIB_API void OBJS_SetListY(OBJ_LIST *OL,S32 Y); +GLIB_API void OBJS_SetListZ(OBJ_LIST *OL,S32 Z); +GLIB_API void OBJS_MakeListImmortal(OBJ_LIST *OL); +GLIB_API void OBJS_MakeListMortal(OBJ_LIST *OL); +GLIB_API BOOL OBJS_MakeListInvisible(OBJ_LIST *OL); +GLIB_API BOOL OBJS_MakeListVisible(OBJ_LIST *OL); +GLIB_API void OBJS_SetSortCompare(OBJ_LIST *OL,BOOL (*CompRout)(OBJ_STRUCT *O1,OBJ_STRUCT *O2)); + +GLIB_API int OBJS_GetHeight(OBJ_STRUCT *O); +GLIB_API int OBJS_GetWidth(OBJ_STRUCT *O); +GLIB_API int OBJS_GetPal(OBJ_STRUCT *O); +GLIB_API int OBJS_GetYOff(OBJ_STRUCT *O); +GLIB_API int OBJS_GetXOff(OBJ_STRUCT *O); + +GLIB_API int OBJS_GetObjsAlloced(void); +GLIB_API void OBJS_ObjsVelAddOnly(void); + +#ifdef __cplusplus +}; +#endif + + +/* --------------------------------------------------------------------------- */ +#endif diff --git a/Utils/Libs/GLib/pcre.c b/Utils/Libs/GLib/pcre.c new file mode 100644 index 000000000..320b8e228 --- /dev/null +++ b/Utils/Libs/GLib/pcre.c @@ -0,0 +1,4175 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-1999 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. +----------------------------------------------------------------------------- +*/ + + +/* Define DEBUG to get debugging output on stdout. */ + +/* #define DEBUG */ + +/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef +inline, and there are *still* stupid compilers about that don't like indented +pre-processor statements. I suppose it's only been 10 years... */ + +#ifdef DEBUG +#define DPRINTF(p) printf p +#else +#define DPRINTF(p) /*nothing*/ +#endif + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + +#include "internal.h" + + +/* Allow compilation as C++ source code, should anybody want to do that. */ + +#ifdef __cplusplus +#define class pcre_class +#endif + + +/* Number of items on the nested bracket stacks at compile time. This should +not be set greater than 200. */ + +#define BRASTACK_SIZE 200 + + +/* Min and max values for the common repeats; for the maxima, 0 => infinity */ + +static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; +static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; + +/* Text forms of OP_ values and things, for debugging (not all used) */ + +#ifdef DEBUG +static const char *OP_names[] = { + "End", "\\A", "\\B", "\\b", "\\D", "\\d", + "\\S", "\\s", "\\W", "\\w", "\\Z", "\\z", + "Opt", "^", "$", "Any", "chars", "not", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", + "class", "Ref", + "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", + "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cref", + "Brazero", "Braminzero", "Bra" +}; +#endif + +/* Table for handling escaped characters in the range '0'-'z'. Positive returns +are simple data values; negative values are for special things like \d and so +on. Zero means further processing is needed (for things like \x), or the escape +is invalid. */ + +static const short int escapes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ + 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ + '@', -ESC_A, -ESC_B, 0, -ESC_D, 0, 0, 0, /* @ - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ + 0, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ + '`', 7, -ESC_b, 0, -ESC_d, 27, '\f', 0, /* ` - g */ + 0, 0, 0, 0, 0, 0, '\n', 0, /* h - o */ + 0, 0, '\r', -ESC_s, '\t', 0, 0, -ESC_w, /* p - w */ + 0, 0, -ESC_z /* x - z */ +}; + +/* Definition to allow mutual recursion */ + +static BOOL + compile_regex(int, int, int *, uschar **, const uschar **, const char **, + BOOL, int, compile_data *); + + + +/************************************************* +* Global variables * +*************************************************/ + +/* PCRE is thread-clean and doesn't use any global variables in the normal +sense. However, it calls memory allocation and free functions via the two +indirections below, which are can be changed by the caller, but are shared +between all threads. */ + +void *(*pcre_malloc)(size_t) = malloc; +void (*pcre_free)(void *) = free; + + + + +/************************************************* +* Default character tables * +*************************************************/ + +/* A default set of character tables is included in the PCRE binary. Its source +is built by the maketables auxiliary program, which uses the default C ctypes +functions, and put in the file chartables.c. These tables are used by PCRE +whenever the caller of pcre_compile() does not provide an alternate set of +tables. */ + +#include "chartables.c" + + + +/************************************************* +* Return version string * +*************************************************/ + +const char * +pcre_version(void) +{ +return PCRE_VERSION; +} + + + + +/************************************************* +* Return info about a compiled pattern * +*************************************************/ + +/* This function picks potentially useful data out of the private +structure. + +Arguments: + external_re points to compiled code + optptr where to pass back the options + first_char where to pass back the first character, + or -1 if multiline and all branches start ^, + or -2 otherwise + +Returns: number of identifying extraction brackets + or negative values on error +*/ + +int +pcre_info(const pcre *external_re, int *optptr, int *first_char) +{ +const real_pcre *re = (const real_pcre *)external_re; +if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; +if (optptr != NULL) *optptr = (re->options & PUBLIC_OPTIONS); +if (first_char != NULL) + *first_char = ((re->options & PCRE_FIRSTSET) != 0)? re->first_char : + ((re->options & PCRE_STARTLINE) != 0)? -1 : -2; +return re->top_bracket; +} + + + + +#ifdef DEBUG +/************************************************* +* Debugging function to print chars * +*************************************************/ + +/* Print a sequence of chars in printable format, stopping at the end of the +subject if the requested. + +Arguments: + p points to characters + length number to print + is_subject TRUE if printing from within md->start_subject + md pointer to matching data block, if is_subject is TRUE + +Returns: nothing +*/ + +static void +pchars(const uschar *p, int length, BOOL is_subject, match_data *md) +{ +int c; +if (is_subject && length > md->end_subject - p) length = md->end_subject - p; +while (length-- > 0) + if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c); +} +#endif + + + + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \n, or a negative value which +encodes one of the more complicated things such as \d. On entry, ptr is +pointing at the \. On exit, it is on the final character of the escape +sequence. + +Arguments: + ptrptr points to the pattern position pointer + errorptr points to the pointer to the error message + bracount number of previous extracting brackets + options the options bits + isclass TRUE if inside a character class + cd pointer to char tables block + +Returns: zero or positive => a data character + negative => a special escape sequence + on error, errorptr is set +*/ + +static int +check_escape(const uschar **ptrptr, const char **errorptr, int bracount, + int options, BOOL isclass, compile_data *cd) +{ +const uschar *ptr = *ptrptr; +int c = *(++ptr) & 255; /* Ensure > 0 on signed-char systems */ +int i; + +if (c == 0) *errorptr = ERR1; + +/* Digits or letters may have special meaning; all others are literals. */ + +else if (c < '0' || c > 'z') {} + +/* Do an initial lookup in a table. A non-zero result is something that can be +returned immediately. Otherwise further processing may be required. */ + +else if ((i = escapes[c - '0']) != 0) c = i; + +/* Escapes that need further processing, or are illegal. */ + +else + { + const uschar *oldptr; + switch (c) + { + /* The handling of escape sequences consisting of a string of digits + starting with one that is not zero is not straightforward. By experiment, + the way Perl works seems to be as follows: + + Outside a character class, the digits are read as a decimal number. If the + number is less than 10, or if there are that many previous extracting + left brackets, then it is a back reference. Otherwise, up to three octal + digits are read to form an escaped byte. Thus \123 is likely to be octal + 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal + value is greater than 377, the least significant 8 bits are taken. Inside a + character class, \ followed by a digit is always an octal number. */ + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + + if (!isclass) + { + oldptr = ptr; + c -= '0'; + while ((cd->ctypes[ptr[1]] & ctype_digit) != 0) + c = c * 10 + *(++ptr) - '0'; + if (c < 10 || c <= bracount) + { + c = -(ESC_REF + c); + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle an octal number following \. If the first digit is 8 or 9, Perl + generates a binary zero byte and treats the digit as a following literal. + Thus we have to pull back the pointer by one. */ + + if ((c = *ptr) >= '8') + { + ptr--; + c = 0; + break; + } + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit */ + + case '0': + c -= '0'; + while(i++ < 2 && (cd->ctypes[ptr[1]] & ctype_digit) != 0 && + ptr[1] != '8' && ptr[1] != '9') + c = c * 8 + *(++ptr) - '0'; + break; + + /* Special escapes not starting with a digit are straightforward */ + + case 'x': + c = 0; + while (i++ < 2 && (cd->ctypes[ptr[1]] & ctype_xdigit) != 0) + { + ptr++; + c = c * 16 + cd->lcc[*ptr] - + (((cd->ctypes[*ptr] & ctype_digit) != 0)? '0' : 'W'); + } + break; + + case 'c': + c = *(++ptr); + if (c == 0) + { + *errorptr = ERR2; + return 0; + } + + /* A letter is upper-cased; then the 0x40 bit is flipped */ + + if (c >= 'a' && c <= 'z') c = cd->fcc[c]; + c ^= 0x40; + break; + + /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any + other alphameric following \ is an error if PCRE_EXTRA was set; otherwise, + for Perl compatibility, it is a literal. This code looks a bit odd, but + there used to be some cases other than the default, and there may be again + in future, so I haven't "optimized" it. */ + + default: + if ((options & PCRE_EXTRA) != 0) switch(c) + { + default: + *errorptr = ERR3; + break; + } + break; + } + } + +*ptrptr = ptr; +return c; +} + + + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + cd pointer to char tables block + +Returns: TRUE or FALSE +*/ + +static BOOL +is_counted_repeat(const uschar *p, compile_data *cd) +{ +if ((cd->ctypes[*p++] & ctype_digit) == 0) return FALSE; +while ((cd->ctypes[*p] & ctype_digit) != 0) p++; +if (*p == '}') return TRUE; + +if (*p++ != ',') return FALSE; +if (*p == '}') return TRUE; + +if ((cd->ctypes[*p++] & ctype_digit) == 0) return FALSE; +while ((cd->ctypes[*p] & ctype_digit) != 0) p++; +return (*p == '}'); +} + + + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values. This is called only +after is_counted_repeat() has confirmed that a repeat-count quantifier exists, +so the syntax is guaranteed to be correct, but we need to check the values. + +Arguments: + p pointer to first char after '{' + minp pointer to int for min + maxp pointer to int for max + returned as -1 if no max + errorptr points to pointer to error message + cd pointer to character tables clock + +Returns: pointer to '}' on success; + current ptr on error, with errorptr set +*/ + +static const uschar * +read_repeat_counts(const uschar *p, int *minp, int *maxp, + const char **errorptr, compile_data *cd) +{ +int min = 0; +int max = -1; + +while ((cd->ctypes[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0'; + +if (*p == '}') max = min; else + { + if (*(++p) != '}') + { + max = 0; + while((cd->ctypes[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0'; + if (max < min) + { + *errorptr = ERR4; + return p; + } + } + } + +/* Do paranoid checks, then fill in the required variables, and pass back the +pointer to the terminating '}'. */ + +if (min > 65535 || max > 65535) + *errorptr = ERR5; +else + { + *minp = min; + *maxp = max; + } +return p; +} + + + +/************************************************* +* Find the fixed length of a pattern * +*************************************************/ + +/* Scan a pattern and compute the fixed length of subject that will match it, +if the length is fixed. This is needed for dealing with backward assertions. + +Arguments: + code points to the start of the pattern (the bracket) + +Returns: the fixed length, or -1 if there is no fixed length +*/ + +static int +find_fixedlength(uschar *code) +{ +int length = -1; + +register int branchlength = 0; +register uschar *cc = code + 3; + +/* Scan along the opcodes for this branch. If we get to the end of the +branch, check the length against that of the other branches. */ + +for (;;) + { + int d; + register int op = *cc; + if (op >= OP_BRA) op = OP_BRA; + + switch (op) + { + case OP_BRA: + case OP_ONCE: + case OP_COND: + d = find_fixedlength(cc); + if (d < 0) return -1; + branchlength += d; + do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); + cc += 3; + break; + + /* Reached end of a branch; if it's a ket it is the end of a nested + call. If it's ALT it is an alternation in a nested call. If it is + END it's the end of the outer call. All can be handled by the same code. */ + + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_END: + if (length < 0) length = branchlength; + else if (length != branchlength) return -1; + if (*cc != OP_ALT) return length; + cc += 3; + branchlength = 0; + break; + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); + cc += 3; + break; + + /* Skip over things that don't match chars */ + + case OP_REVERSE: + cc++; + + case OP_CREF: + case OP_OPT: + cc++; + /* Fall through */ + + case OP_SOD: + case OP_EOD: + case OP_EODN: + case OP_CIRC: + case OP_DOLL: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + cc++; + break; + + /* Handle char strings */ + + case OP_CHARS: + branchlength += *(++cc); + cc += *cc + 1; + break; + + /* Handle exact repetitions */ + + case OP_EXACT: + case OP_TYPEEXACT: + branchlength += (cc[1] << 8) + cc[2]; + cc += 4; + break; + + /* Handle single-char matchers */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + branchlength++; + cc++; + break; + + + /* Check a class for variable quantification */ + + case OP_CLASS: + cc += (*cc == OP_REF)? 2 : 33; + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + return -1; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if ((cc[1] << 8) + cc[2] != (cc[3] << 8) + cc[4]) return -1; + branchlength += (cc[1] << 8) + cc[2]; + cc += 5; + break; + + default: + branchlength++; + } + break; + + /* Anything else is variable length */ + + default: + return -1; + } + } +/* Control never gets here */ +} + + + + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the pattern, compiling it into the code vector. + +Arguments: + options the option bits + brackets points to number of brackets used + code points to the pointer to the current code point + ptrptr points to the current pattern pointer + errorptr points to pointer to error message + optchanged set to the value of the last OP_OPT item compiled + cd contains pointers to tables + +Returns: TRUE on success + FALSE, with *errorptr set on error +*/ + +static BOOL +compile_branch(int options, int *brackets, uschar **codeptr, + const uschar **ptrptr, const char **errorptr, int *optchanged, + compile_data *cd) +{ +int repeat_type, op_type; +int repeat_min, repeat_max; +int bravalue, length; +int greedy_default, greedy_non_default; +register int c; +register uschar *code = *codeptr; +uschar *tempcode; +const uschar *ptr = *ptrptr; +const uschar *tempptr; +uschar *previous = NULL; +uschar class[32]; + +/* Set up the default and non-default settings for greediness */ + +greedy_default = ((options & PCRE_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; + +/* Switch on next character until the end of the branch */ + +for (;; ptr++) + { + BOOL negate_class; + int class_charcount; + int class_lastchar; + int newoptions; + int condref; + + c = *ptr; + if ((options & PCRE_EXTENDED) != 0) + { + if ((cd->ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + } + + switch(c) + { + /* The branch terminates at end of string, |, or ). */ + + case 0: + case '|': + case ')': + *codeptr = code; + *ptrptr = ptr; + return TRUE; + + /* Handle single-character metacharacters */ + + case '^': + previous = NULL; + *code++ = OP_CIRC; + break; + + case '$': + previous = NULL; + *code++ = OP_DOLL; + break; + + case '.': + previous = code; + *code++ = OP_ANY; + break; + + /* Character classes. These always build a 32-byte bitmap of the permitted + characters, except in the special case where there is only one character. + For negated classes, we build the map as usual, then invert it at the end. + */ + + case '[': + previous = code; + *code++ = OP_CLASS; + + /* If the first character is '^', set the negation flag and skip it. */ + + if ((c = *(++ptr)) == '^') + { + negate_class = TRUE; + c = *(++ptr); + } + else negate_class = FALSE; + + /* Keep a count of chars so that we can optimize the case of just a single + character. */ + + class_charcount = 0; + class_lastchar = -1; + + /* Initialize the 32-char bit map to all zeros. We have to build the + map in a temporary bit of store, in case the class contains only 1 + character, because in that case the compiled code doesn't use the + bit map. */ + + memset(class, 0, 32 * sizeof(uschar)); + + /* Process characters until ] is reached. By writing this as a "do" it + means that an initial ] is taken as a data character. */ + + do + { + if (c == 0) + { + *errorptr = ERR6; + goto FAILED; + } + + /* Backslash may introduce a single character, or it may introduce one + of the specials, which just set a flag. Escaped items are checked for + validity in the pre-compiling pass. The sequence \b is a special case. + Inside a class (and only there) it is treated as backspace. Elsewhere + it marks a word boundary. Other escapes have preset maps ready to + or into the one we are building. We assume they have more than one + character in them, so set class_count bigger than one. */ + + if (c == '\\') + { + c = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); + if (-c == ESC_b) c = '\b'; + else if (c < 0) + { + register const uschar *cbits = cd->cbits; + class_charcount = 10; + switch (-c) + { + case ESC_d: + for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_digit]; + continue; + + case ESC_D: + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_digit]; + continue; + + case ESC_w: + for (c = 0; c < 32; c++) + class[c] |= (cbits[c+cbit_digit] | cbits[c+cbit_word]); + continue; + + case ESC_W: + for (c = 0; c < 32; c++) + class[c] |= ~(cbits[c+cbit_digit] | cbits[c+cbit_word]); + continue; + + case ESC_s: + for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_space]; + continue; + + case ESC_S: + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_space]; + continue; + + default: + *errorptr = ERR7; + goto FAILED; + } + } + /* Fall through if single character */ + } + + /* A single character may be followed by '-' to form a range. However, + Perl does not permit ']' to be the end of the range. A '-' character + here is treated as a literal. */ + + if (ptr[1] == '-' && ptr[2] != ']') + { + int d; + ptr += 2; + d = *ptr; + + if (d == 0) + { + *errorptr = ERR6; + goto FAILED; + } + + /* The second part of a range can be a single-character escape, but + not any of the other escapes. */ + + if (d == '\\') + { + d = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); + if (d < 0) + { + if (d == -ESC_b) d = '\b'; else + { + *errorptr = ERR7; + goto FAILED; + } + } + } + + if (d < c) + { + *errorptr = ERR8; + goto FAILED; + } + + for (; c <= d; c++) + { + class[c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + int uc = cd->fcc[c]; /* flip case */ + class[uc/8] |= (1 << (uc&7)); + } + class_charcount++; /* in case a one-char range */ + class_lastchar = c; + } + continue; /* Go get the next char in the class */ + } + + /* Handle a lone single character - we can get here for a normal + non-escape char, or after \ that introduces a single character. */ + + class [c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + c = cd->fcc[c]; /* flip case */ + class[c/8] |= (1 << (c&7)); + } + class_charcount++; + class_lastchar = c; + } + + /* Loop until ']' reached; the check for end of string happens inside the + loop. This "while" is the end of the "do" above. */ + + while ((c = *(++ptr)) != ']'); + + /* If class_charcount is 1 and class_lastchar is not negative, we saw + precisely one character. This doesn't need the whole 32-byte bit map. + We turn it into a 1-character OP_CHAR if it's positive, or OP_NOT if + it's negative. */ + + if (class_charcount == 1 && class_lastchar >= 0) + { + if (negate_class) + { + code[-1] = OP_NOT; + } + else + { + code[-1] = OP_CHARS; + *code++ = 1; + } + *code++ = class_lastchar; + } + + /* Otherwise, negate the 32-byte map if necessary, and copy it into + the code vector. */ + + else + { + if (negate_class) + for (c = 0; c < 32; c++) code[c] = ~class[c]; + else + memcpy(code, class, 32); + code += 32; + } + break; + + /* Various kinds of repeat */ + + case '{': + if (!is_counted_repeat(ptr+1, cd)) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr, cd); + if (*errorptr != NULL) goto FAILED; + goto REPEAT; + + case '*': + repeat_min = 0; + repeat_max = -1; + goto REPEAT; + + case '+': + repeat_min = 1; + repeat_max = -1; + goto REPEAT; + + case '?': + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (previous == NULL) + { + *errorptr = ERR9; + goto FAILED; + } + + /* If the next character is '?' this is a minimizing repeat, by default, + but if PCRE_UNGREEDY is set, it works the other way round. Advance to the + next character. */ + + if (ptr[1] == '?') + { repeat_type = greedy_non_default; ptr++; } + else repeat_type = greedy_default; + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) code = previous; + + /* If previous was a string of characters, chop off the last one and use it + as the subject of the repeat. If there was only one character, we can + abolish the previous item altogether. */ + + else if (*previous == OP_CHARS) + { + int len = previous[1]; + if (len == 1) + { + c = previous[2]; + code = previous; + } + else + { + c = previous[len+1]; + previous[1]--; + code--; + } + op_type = 0; /* Use single-char op codes */ + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + } + + /* If previous was a single negated character ([^a] or similar), we use + one of the special opcodes, replacing it. The code is shared with single- + character repeats by adding a suitable offset into repeat_type. */ + + else if ((int)*previous == OP_NOT) + { + op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ + c = previous[1]; + code = previous; + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by adding a suitable offset into repeat_type. */ + + else if ((int)*previous < OP_EODN || *previous == OP_ANY) + { + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + c = *previous; + code = previous; + + OUTPUT_SINGLE_REPEAT: + repeat_type += op_type; /* Combine both values for many cases */ + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) + { + if (repeat_max == -1) *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; + else + { + *code++ = OP_UPTO + repeat_type; + *code++ = repeat_max >> 8; + *code++ = (repeat_max & 255); + } + } + + /* The case {1,} is handled as the special case + */ + + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_PLUS + repeat_type; + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO. An EXACT of 1 is optimized. */ + + else + { + if (repeat_min != 1) + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + *code++ = repeat_min >> 8; + *code++ = (repeat_min & 255); + } + + /* If the mininum is 1 and the previous item was a character string, + we either have to put back the item that got cancelled if the string + length was 1, or add the character back onto the end of a longer + string. For a character type nothing need be done; it will just get + put back naturally. Note that the final character is always going to + get added below. */ + + else if (*previous == OP_CHARS) + { + if (code == previous) code += 2; else previous[1]++; + } + + /* For a single negated character we also have to put back the + item that got cancelled. */ + + else if (*previous == OP_NOT) code++; + + /* If the maximum is unlimited, insert an OP_STAR. */ + + if (repeat_max < 0) + { + *code++ = c; + *code++ = OP_STAR + repeat_type; + } + + /* Else insert an UPTO if the max is greater than the min. */ + + else if (repeat_max != repeat_min) + { + *code++ = c; + repeat_max -= repeat_min; + *code++ = OP_UPTO + repeat_type; + *code++ = repeat_max >> 8; + *code++ = (repeat_max & 255); + } + } + + /* The character or character type itself comes last in all cases. */ + + *code++ = c; + } + + /* If previous was a character class or a back reference, we put the repeat + stuff after it. */ + + else if (*previous == OP_CLASS || *previous == OP_REF) + { + if (repeat_min == 0 && repeat_max == -1) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else + { + *code++ = OP_CRRANGE + repeat_type; + *code++ = repeat_min >> 8; + *code++ = repeat_min & 255; + if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ + *code++ = repeat_max >> 8; + *code++ = repeat_max & 255; + } + } + + /* If previous was a bracket group, we may have to replicate it in certain + cases. */ + + else if ((int)*previous >= OP_BRA || (int)*previous == OP_ONCE || + (int)*previous == OP_COND) + { + int i, ketoffset = 0; + int len = code - previous; + + /* If the maximum repeat count is unlimited, find the end of the bracket + by scanning through from the start, and compute the offset back to it + from the current code pointer. There may be an OP_OPT setting following + the final KET, so we can't find the end just by going back from the code + pointer. */ + + if (repeat_max == -1) + { + register uschar *ket = previous; + do ket += (ket[1] << 8) + ket[2]; while (*ket != OP_KET); + ketoffset = code - ket; + } + + /* If the minimum is greater than zero, and the maximum is unlimited or + equal to the minimum, the first copy remains where it is, and is + replicated up to the minimum number of times. This case includes the + + repeat, but of course no replication is needed in that case. */ + + if (repeat_min > 0 && (repeat_max == -1 || repeat_max == repeat_min)) + { + for (i = 1; i < repeat_min; i++) + { + memcpy(code, previous, len); + code += len; + } + } + + /* If the minimum is zero, stick BRAZERO in front of the first copy. + Then, if there is a fixed upper limit, replicated up to that many times, + sticking BRAZERO in front of all the optional ones. */ + + else + { + if (repeat_min == 0) + { + memmove(previous+1, previous, len); + code++; + *previous++ = OP_BRAZERO + repeat_type; + } + + for (i = 1; i < repeat_min; i++) + { + memcpy(code, previous, len); + code += len; + } + + for (i = (repeat_min > 0)? repeat_min : 1; i < repeat_max; i++) + { + *code++ = OP_BRAZERO + repeat_type; + memcpy(code, previous, len); + code += len; + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. We + can't just offset backwards from the current code point, because we + don't know if there's been an options resetting after the ket. The + correct offset was computed above. */ + + if (repeat_max == -1) code[-ketoffset] = OP_KETRMAX + repeat_type; + } + + /* Else there's some kind of shambles */ + + else + { + *errorptr = ERR11; + goto FAILED; + } + + /* In all case we no longer have a previous item. */ + + previous = NULL; + break; + + + /* Start of nested bracket sub-expression, or comment or lookahead or + lookbehind or option setting or condition. First deal with special things + that can come after a bracket; all are introduced by ?, and the appearance + of any of them means that this is not a referencing group. They were + checked for validity in the first pass over the string, so we don't have to + check for syntax errors here. */ + + case '(': + newoptions = options; + condref = -1; + + if (*(++ptr) == '?') + { + int set, unset; + int *optset; + + switch (*(++ptr)) + { + case '#': /* Comment; skip to ket */ + ptr++; + while (*ptr != ')') ptr++; + continue; + + case ':': /* Non-extracting bracket */ + bravalue = OP_BRA; + ptr++; + break; + + case '(': + bravalue = OP_COND; /* Conditional group */ + if ((cd->ctypes[*(++ptr)] & ctype_digit) != 0) + { + condref = *ptr - '0'; + while (*(++ptr) != ')') condref = condref*10 + *ptr - '0'; + ptr++; + } + else ptr--; + break; + + case '=': /* Positive lookahead */ + bravalue = OP_ASSERT; + ptr++; + break; + + case '!': /* Negative lookahead */ + bravalue = OP_ASSERT_NOT; + ptr++; + break; + + case '<': /* Lookbehinds */ + switch (*(++ptr)) + { + case '=': /* Positive lookbehind */ + bravalue = OP_ASSERTBACK; + ptr++; + break; + + case '!': /* Negative lookbehind */ + bravalue = OP_ASSERTBACK_NOT; + ptr++; + break; + + default: /* Syntax error */ + *errorptr = ERR24; + goto FAILED; + } + break; + + case '>': /* One-time brackets */ + bravalue = OP_ONCE; + ptr++; + break; + + default: /* Option setting */ + set = unset = 0; + optset = &set; + + while (*ptr != ')' && *ptr != ':') + { + switch (*ptr++) + { + case '-': optset = &unset; break; + + case 'i': *optset |= PCRE_CASELESS; break; + case 'm': *optset |= PCRE_MULTILINE; break; + case 's': *optset |= PCRE_DOTALL; break; + case 'x': *optset |= PCRE_EXTENDED; break; + case 'U': *optset |= PCRE_UNGREEDY; break; + case 'X': *optset |= PCRE_EXTRA; break; + + default: + *errorptr = ERR12; + goto FAILED; + } + } + + /* Set up the changed option bits, but don't change anything yet. */ + + newoptions = (options | set) & (~unset); + + /* If the options ended with ')' this is not the start of a nested + group with option changes, so the options change at this level. At top + level there is nothing else to be done (the options will in fact have + been set from the start of compiling as a result of the first pass) but + at an inner level we must compile code to change the ims options if + necessary, and pass the new setting back so that it can be put at the + start of any following branches, and when this group ends, a resetting + item can be compiled. */ + + if (*ptr == ')') + { + if ((options & PCRE_INGROUP) != 0 && + (options & PCRE_IMS) != (newoptions & PCRE_IMS)) + { + *code++ = OP_OPT; + *code++ = *optchanged = newoptions & PCRE_IMS; + } + options = newoptions; /* Change options at this level */ + previous = NULL; /* This item can't be repeated */ + continue; /* It is complete */ + } + + /* If the options ended with ':' we are heading into a nested group + with possible change of options. Such groups are non-capturing and are + not assertions of any kind. All we need to do is skip over the ':'; + the newoptions value is handled below. */ + + bravalue = OP_BRA; + ptr++; + } + } + + /* Else we have a referencing group; adjust the opcode. */ + + else + { + if (++(*brackets) > EXTRACT_MAX) + { + *errorptr = ERR13; + goto FAILED; + } + bravalue = OP_BRA + *brackets; + } + + /* Process nested bracketed re. Assertions may not be repeated, but other + kinds can be. We copy code into a non-register variable in order to be able + to pass its address because some compilers complain otherwise. Pass in a + new setting for the ims options if they have changed. */ + + previous = (bravalue >= OP_ONCE)? code : NULL; + *code = bravalue; + tempcode = code; + + if (!compile_regex( + options | PCRE_INGROUP, /* Set for all nested groups */ + ((options & PCRE_IMS) != (newoptions & PCRE_IMS))? + newoptions & PCRE_IMS : -1, /* Pass ims options if changed */ + brackets, /* Bracket level */ + &tempcode, /* Where to put code (updated) */ + &ptr, /* Input pointer (updated) */ + errorptr, /* Where to put an error message */ + (bravalue == OP_ASSERTBACK || + bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ + condref, /* Condition reference number */ + cd)) /* Tables block */ + goto FAILED; + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group + and any option resetting that may follow it. The pattern pointer (ptr) + is on the bracket. */ + + /* If this is a conditional bracket, check that there are no more than + two branches in the group. */ + + if (bravalue == OP_COND) + { + int branchcount = 0; + uschar *tc = code; + + do { + branchcount++; + tc += (tc[1] << 8) | tc[2]; + } + while (*tc != OP_KET); + + if (branchcount > 2) + { + *errorptr = ERR27; + goto FAILED; + } + } + + /* Now update the main code pointer to the end of the group. */ + + code = tempcode; + + /* Error if hit end of pattern */ + + if (*ptr != ')') + { + *errorptr = ERR14; + goto FAILED; + } + break; + + /* Check \ for being a real metacharacter; if not, fall through and handle + it as a data character at the start of a string. Escape items are checked + for validity in the pre-compiling pass. */ + + case '\\': + tempptr = ptr; + c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); + + /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values + are arranged to be the negation of the corresponding OP_values. For the + back references, the values are ESC_REF plus the reference number. Only + back references and those types that consume a character may be repeated. + We can test for values between ESC_b and ESC_Z for the latter; this may + have to change if any new ones are ever created. */ + + if (c < 0) + { + if (-c >= ESC_REF) + { + previous = code; + *code++ = OP_REF; + *code++ = -c - ESC_REF; + } + else + { + previous = (-c > ESC_b && -c < ESC_Z)? code : NULL; + *code++ = -c; + } + continue; + } + + /* Data character: reset and fall through */ + + ptr = tempptr; + c = '\\'; + + /* Handle a run of data characters until a metacharacter is encountered. + The first character is guaranteed not to be whitespace or # when the + extended flag is set. */ + + NORMAL_CHAR: + default: + previous = code; + *code = OP_CHARS; + code += 2; + length = 0; + + do + { + if ((options & PCRE_EXTENDED) != 0) + { + if ((cd->ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + if (c == 0) break; + continue; + } + } + + /* Backslash may introduce a data char or a metacharacter. Escaped items + are checked for validity in the pre-compiling pass. Stop the string + before a metaitem. */ + + if (c == '\\') + { + tempptr = ptr; + c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); + if (c < 0) { ptr = tempptr; break; } + } + + /* Ordinary character or single-char escape */ + + *code++ = c; + length++; + } + + /* This "while" is the end of the "do" above. */ + + while (length < 255 && (cd->ctypes[c = *(++ptr)] & ctype_meta) == 0); + + /* Compute the length and set it in the data vector, and advance to + the next state. */ + + previous[1] = length; + if (length < 255) ptr--; + break; + } + } /* end of big loop */ + +/* Control never reaches here by falling through, only by a goto for all the +error states. Pass back the position in the pattern so that it can be displayed +to the user for diagnosing the error. */ + +FAILED: +*ptrptr = ptr; +return FALSE; +} + + + + +/************************************************* +* Compile sequence of alternatives * +*************************************************/ + +/* On entry, ptr is pointing past the bracket character, but on return +it points to the closing bracket, or vertical bar, or end of string. +The code variable is pointing at the byte into which the BRA operator has been +stored. If the ims options are changed at the start (for a (?ims: group) or +during any branch, we need to insert an OP_OPT item at the start of every +following branch to ensure they get set correctly at run time, and also pass +the new options into every subsequent branch compile. + +Argument: + options the option bits + optchanged new ims options to set as if (?ims) were at the start, or -1 + for no change + brackets -> int containing the number of extracting brackets used + codeptr -> the address of the current code pointer + ptrptr -> the address of the current pattern pointer + errorptr -> pointer to error message + lookbehind TRUE if this is a lookbehind assertion + condref > 0 for OPT_CREF setting at start of conditional group + cd points to the data block with tables pointers + +Returns: TRUE on success +*/ + +static BOOL +compile_regex(int options, int optchanged, int *brackets, uschar **codeptr, + const uschar **ptrptr, const char **errorptr, BOOL lookbehind, int condref, + compile_data *cd) +{ +const uschar *ptr = *ptrptr; +uschar *code = *codeptr; +uschar *last_branch = code; +uschar *start_bracket = code; +uschar *reverse_count = NULL; +int oldoptions = options & PCRE_IMS; + +code += 3; + +/* At the start of a reference-based conditional group, insert the reference +number as an OP_CREF item. */ + +if (condref > 0) + { + *code++ = OP_CREF; + *code++ = condref; + } + +/* Loop for each alternative branch */ + +for (;;) + { + int length; + + /* Handle change of options */ + + if (optchanged >= 0) + { + *code++ = OP_OPT; + *code++ = optchanged; + options = (options & ~PCRE_IMS) | optchanged; + } + + /* Set up dummy OP_REVERSE if lookbehind assertion */ + + if (lookbehind) + { + *code++ = OP_REVERSE; + reverse_count = code; + *code++ = 0; + *code++ = 0; + } + + /* Now compile the branch */ + + if (!compile_branch(options,brackets,&code,&ptr,errorptr,&optchanged,cd)) + { + *ptrptr = ptr; + return FALSE; + } + + /* Fill in the length of the last branch */ + + length = code - last_branch; + last_branch[1] = length >> 8; + last_branch[2] = length & 255; + + /* If lookbehind, check that this branch matches a fixed-length string, + and put the length into the OP_REVERSE item. Temporarily mark the end of + the branch with OP_END. */ + + if (lookbehind) + { + *code = OP_END; + length = find_fixedlength(last_branch); + DPRINTF(("fixed length = %d\n", length)); + if (length < 0) + { + *errorptr = ERR25; + *ptrptr = ptr; + return FALSE; + } + reverse_count[0] = (length >> 8); + reverse_count[1] = length & 255; + } + + /* Reached end of expression, either ')' or end of pattern. Insert a + terminating ket and the length of the whole bracketed item, and return, + leaving the pointer at the terminating char. If any of the ims options + were changed inside the group, compile a resetting op-code following. */ + + if (*ptr != '|') + { + length = code - start_bracket; + *code++ = OP_KET; + *code++ = length >> 8; + *code++ = length & 255; + if (optchanged >= 0) + { + *code++ = OP_OPT; + *code++ = oldoptions; + } + *codeptr = code; + *ptrptr = ptr; + return TRUE; + } + + /* Another branch follows; insert an "or" node and advance the pointer. */ + + *code = OP_ALT; + last_branch = code; + code += 3; + ptr++; + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Find first significant op code * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring op code etc. It skips over things +that do not influence this. For one application, a change of caseless option is +important. + +Arguments: + code pointer to the start of the group + options pointer to external options + optbit the option bit whose changing is significant, or + zero if none are + optstop TRUE to return on option change, otherwise change the options + value and continue + +Returns: pointer to the first significant opcode +*/ + +static const uschar* +first_significant_code(const uschar *code, int *options, int optbit, + BOOL optstop) +{ +for (;;) + { + switch ((int)*code) + { + case OP_OPT: + if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit)) + { + if (optstop) return code; + *options = (int)code[1]; + } + code += 2; + break; + + case OP_CREF: + code += 2; + break; + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do code += (code[1] << 8) + code[2]; while (*code == OP_ALT); + code += 3; + break; + + default: + return code; + } + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Check for anchored expression * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket +all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then +it's anchored. However, if this is a multiline pattern, then only OP_SOD +counts, since OP_CIRC can match in the middle. + +A branch is also implicitly anchored if it starts with .* because that will try +the rest of the pattern at all possible matching points, so there is no point +trying them again. + +Arguments: + code points to start of expression (the bracket) + options points to the options setting + +Returns: TRUE or FALSE +*/ + +static BOOL +is_anchored(register const uschar *code, int *options) +{ +do { + const uschar *scode = first_significant_code(code + 3, options, + PCRE_MULTILINE, FALSE); + register int op = *scode; + if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + { if (!is_anchored(scode, options)) return FALSE; } + else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) + { if (scode[1] != OP_ANY) return FALSE; } + else if (op != OP_SOD && + ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC)) + return FALSE; + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Check for start with \n line expression * +*************************************************/ + +/* This is called for multiline expressions to try to find out if every branch +starts with ^ so that "first char" processing can be done to speed things up. + +Argument: points to start of expression (the bracket) +Returns: TRUE or FALSE +*/ + +static BOOL +is_startline(const uschar *code) +{ +do { + const uschar *scode = first_significant_code(code + 3, NULL, 0, FALSE); + register int op = *scode; + if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + { if (!is_startline(scode)) return FALSE; } + else if (op != OP_CIRC) return FALSE; + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Check for fixed first char * +*************************************************/ + +/* Try to find out if there is a fixed first character. This is called for +unanchored expressions, as it speeds up their processing quite considerably. +Consider each alternative branch. If they all start with the same char, or with +a bracket all of whose alternatives start with the same char (recurse ad lib), +then we return that char, otherwise -1. + +Arguments: + code points to start of expression (the bracket) + options pointer to the options (used to check casing changes) + +Returns: -1 or the fixed first char +*/ + +static int +find_firstchar(const uschar *code, int *options) +{ +register int c = -1; +do { + int d; + const uschar *scode = first_significant_code(code + 3, options, + PCRE_CASELESS, TRUE); + register int op = *scode; + + if (op >= OP_BRA) op = OP_BRA; + + switch(op) + { + default: + return -1; + + case OP_BRA: + case OP_ASSERT: + case OP_ONCE: + case OP_COND: + if ((d = find_firstchar(scode, options)) < 0) return -1; + if (c < 0) c = d; else if (c != d) return -1; + break; + + case OP_EXACT: /* Fall through */ + scode++; + + case OP_CHARS: /* Fall through */ + scode++; + + case OP_PLUS: + case OP_MINPLUS: + if (c < 0) c = scode[1]; else if (c != scode[1]) return -1; + break; + } + + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return c; +} + + + + + +/************************************************* +* Compile a Regular Expression * +*************************************************/ + +/* This function takes a string and returns a pointer to a block of store +holding a compiled version of the expression. + +Arguments: + pattern the regular expression + options various option bits + errorptr pointer to pointer to error text + erroroffset ptr offset in pattern where error was detected + tables pointer to character tables or NULL + +Returns: pointer to compiled data block, or NULL on error, + with errorptr and erroroffset set +*/ + +pcre * +pcre_compile(const char *pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +{ +real_pcre *re; +int length = 3; /* For initial BRA plus length */ +int runlength; +int c, size; +int bracount = 0; +int top_backref = 0; +int branch_extra = 0; +int branch_newextra; +unsigned int brastackptr = 0; +uschar *code; +const uschar *ptr; +compile_data compile_block; +int brastack[BRASTACK_SIZE]; +uschar bralenstack[BRASTACK_SIZE]; + +#ifdef DEBUG +uschar *code_base, *code_end; +#endif + +/* We can't pass back an error message if errorptr is NULL; I guess the best we +can do is just return NULL. */ + +if (errorptr == NULL) return NULL; +*errorptr = NULL; + +/* However, we can give a message for this error */ + +if (erroroffset == NULL) + { + *errorptr = ERR16; + return NULL; + } +*erroroffset = 0; + +if ((options & ~PUBLIC_OPTIONS) != 0) + { + *errorptr = ERR17; + return NULL; + } + +/* Set up pointers to the individual character tables */ + +if (tables == NULL) tables = pcre_default_tables; +compile_block.lcc = tables + lcc_offset; +compile_block.fcc = tables + fcc_offset; +compile_block.cbits = tables + cbits_offset; +compile_block.ctypes = tables + ctypes_offset; + +/* Reflect pattern for debugging output */ + +DPRINTF(("------------------------------------------------------------------\n")); +DPRINTF(("%s\n", pattern)); + +/* The first thing to do is to make a pass over the pattern to compute the +amount of store required to hold the compiled code. This does not have to be +perfect as long as errors are overestimates. At the same time we can detect any +internal flag settings. Make an attempt to correct for any counted white space +if an "extended" flag setting appears late in the pattern. We can't be so +clever for #-comments. */ + +ptr = (const uschar *)(pattern - 1); +while ((c = *(++ptr)) != 0) + { + int min, max; + int class_charcount; + + if ((options & PCRE_EXTENDED) != 0) + { + if ((compile_block.ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + } + + switch(c) + { + /* A backslashed item may be an escaped "normal" character or a + character type. For a "normal" character, put the pointers and + character back so that tests for whitespace etc. in the input + are done correctly. */ + + case '\\': + { + const uschar *save_ptr = ptr; + c = check_escape(&ptr, errorptr, bracount, options, FALSE, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (c >= 0) + { + ptr = save_ptr; + c = '\\'; + goto NORMAL_CHAR; + } + } + length++; + + /* A back reference needs an additional char, plus either one or 5 + bytes for a repeat. We also need to keep the value of the highest + back reference. */ + + if (c <= -ESC_REF) + { + int refnum = -c - ESC_REF; + if (refnum > top_backref) top_backref = refnum; + length++; /* For single back reference */ + if (ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else length += 5; + if (ptr[1] == '?') ptr++; + } + } + continue; + + case '^': + case '.': + case '$': + case '*': /* These repeats won't be after brackets; */ + case '+': /* those are handled separately */ + case '?': + length++; + continue; + + /* This covers the cases of repeats after a single char, metachar, class, + or back reference. */ + + case '{': + if (!is_counted_repeat(ptr+1, &compile_block)) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else + { + length--; /* Uncount the original char or metachar */ + if (min == 1) length++; else if (min > 0) length += 4; + if (max > 0) length += 4; else length += 2; + } + if (ptr[1] == '?') ptr++; + continue; + + /* An alternation contains an offset to the next branch or ket. If any ims + options changed in the previous branch(es), and/or if we are in a + lookbehind assertion, extra space will be needed at the start of the + branch. This is handled by branch_extra. */ + + case '|': + length += 3 + branch_extra; + continue; + + /* A character class uses 33 characters. Don't worry about character types + that aren't allowed in classes - they'll get picked up during the compile. + A character class that contains only one character uses 2 or 3 bytes, + depending on whether it is negated or not. Notice this where we can. */ + + case '[': + class_charcount = 0; + if (*(++ptr) == '^') ptr++; + do + { + if (*ptr == '\\') + { + int ch = check_escape(&ptr, errorptr, bracount, options, TRUE, + &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (-ch == ESC_b) class_charcount++; else class_charcount = 10; + } + else class_charcount++; + ptr++; + } + while (*ptr != 0 && *ptr != ']'); + + /* Repeats for negated single chars are handled by the general code */ + + if (class_charcount == 1) length += 3; else + { + length += 33; + + /* A repeat needs either 1 or 5 bytes. */ + + if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else length += 5; + if (ptr[1] == '?') ptr++; + } + } + continue; + + /* Brackets may be genuine groups or special things */ + + case '(': + branch_newextra = 0; + + /* Handle special forms of bracket, which all start (? */ + + if (ptr[1] == '?') + { + int set, unset; + int *optset; + + switch (c = ptr[2]) + { + /* Skip over comments entirely */ + case '#': + ptr += 3; + while (*ptr != 0 && *ptr != ')') ptr++; + if (*ptr == 0) + { + *errorptr = ERR18; + goto PCRE_ERROR_RETURN; + } + continue; + + /* Non-referencing groups and lookaheads just move the pointer on, and + then behave like a non-special bracket, except that they don't increment + the count of extracting brackets. Ditto for the "once only" bracket, + which is in Perl from version 5.005. */ + + case ':': + case '=': + case '!': + case '>': + ptr += 2; + break; + + /* Lookbehinds are in Perl from version 5.005 */ + + case '<': + if (ptr[3] == '=' || ptr[3] == '!') + { + ptr += 3; + branch_newextra = 3; + length += 3; /* For the first branch */ + break; + } + *errorptr = ERR24; + goto PCRE_ERROR_RETURN; + + /* Conditionals are in Perl from version 5.005. The bracket must either + be followed by a number (for bracket reference) or by an assertion + group. */ + + case '(': + if ((compile_block.ctypes[ptr[3]] & ctype_digit) != 0) + { + ptr += 4; + length += 2; + while ((compile_block.ctypes[*ptr] & ctype_digit) != 0) ptr++; + if (*ptr != ')') + { + *errorptr = ERR26; + goto PCRE_ERROR_RETURN; + } + } + else /* An assertion must follow */ + { + ptr++; /* Can treat like ':' as far as spacing is concerned */ + + if (ptr[2] != '?' || strchr("=!<", ptr[3]) == NULL) + { + ptr += 2; /* To get right offset in message */ + *errorptr = ERR28; + goto PCRE_ERROR_RETURN; + } + } + break; + + /* Else loop checking valid options until ) is met. Anything else is an + error. If we are without any brackets, i.e. at top level, the settings + act as if specified in the options, so massage the options immediately. + This is for backward compatibility with Perl 5.004. */ + + default: + set = unset = 0; + optset = &set; + ptr += 2; + + for (;; ptr++) + { + c = *ptr; + switch (c) + { + case 'i': + *optset |= PCRE_CASELESS; + continue; + + case 'm': + *optset |= PCRE_MULTILINE; + continue; + + case 's': + *optset |= PCRE_DOTALL; + continue; + + case 'x': + *optset |= PCRE_EXTENDED; + continue; + + case 'X': + *optset |= PCRE_EXTRA; + continue; + + case 'U': + *optset |= PCRE_UNGREEDY; + continue; + + case '-': + optset = &unset; + continue; + + /* A termination by ')' indicates an options-setting-only item; + this is global at top level; otherwise nothing is done here and + it is handled during the compiling process on a per-bracket-group + basis. */ + + case ')': + if (brastackptr == 0) + { + options = (options | set) & (~unset); + set = unset = 0; /* To save length */ + } + /* Fall through */ + + /* A termination by ':' indicates the start of a nested group with + the given options set. This is again handled at compile time, but + we must allow for compiled space if any of the ims options are + set. We also have to allow for resetting space at the end of + the group, which is why 4 is added to the length and not just 2. + If there are several changes of options within the same group, this + will lead to an over-estimate on the length, but this shouldn't + matter very much. We also have to allow for resetting options at + the start of any alternations, which we do by setting + branch_newextra to 2. */ + + case ':': + if (((set|unset) & PCRE_IMS) != 0) + { + length += 4; + branch_newextra = 2; + } + goto END_OPTIONS; + + /* Unrecognized option character */ + + default: + *errorptr = ERR12; + goto PCRE_ERROR_RETURN; + } + } + + /* If we hit a closing bracket, that's it - this is a freestanding + option-setting. We need to ensure that branch_extra is updated if + necessary. The only values branch_newextra can have here are 0 or 2. + If the value is 2, then branch_extra must either be 2 or 5, depending + on whether this is a lookbehind group or not. */ + + END_OPTIONS: + if (c == ')') + { + if (branch_newextra == 2 && (branch_extra == 0 || branch_extra == 3)) + branch_extra += branch_newextra; + continue; + } + + /* If options were terminated by ':' control comes here. Fall through + to handle the group below. */ + } + } + + /* Extracting brackets must be counted so we can process escapes in a + Perlish way. */ + + else bracount++; + + /* Non-special forms of bracket. Save length for computing whole length + at end if there's a repeat that requires duplication of the group. Also + save the current value of branch_extra, and start the new group with + the new value. If non-zero, this will either be 2 for a (?imsx: group, or 3 + for a lookbehind assertion. */ + + if (brastackptr >= sizeof(brastack)/sizeof(int)) + { + *errorptr = ERR19; + goto PCRE_ERROR_RETURN; + } + + bralenstack[brastackptr] = branch_extra; + branch_extra = branch_newextra; + + brastack[brastackptr++] = length; + length += 3; + continue; + + /* Handle ket. Look for subsequent max/min; for certain sets of values we + have to replicate this bracket up to that many times. If brastackptr is + 0 this is an unmatched bracket which will generate an error, but take care + not to try to access brastack[-1] when computing the length and restoring + the branch_extra value. */ + + case ')': + length += 3; + { + int minval = 1; + int maxval = 1; + int duplength; + + if (brastackptr > 0) + { + duplength = length - brastack[--brastackptr]; + branch_extra = bralenstack[brastackptr]; + } + else duplength = 0; + + /* Leave ptr at the final char; for read_repeat_counts this happens + automatically; for the others we need an increment. */ + + if ((c = ptr[1]) == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &minval, &maxval, errorptr, + &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + } + else if (c == '*') { minval = 0; maxval = -1; ptr++; } + else if (c == '+') { maxval = -1; ptr++; } + else if (c == '?') { minval = 0; ptr++; } + + /* If there is a minimum > 1 we have to replicate up to minval-1 times; + if there is a limited maximum we have to replicate up to maxval-1 times + and allow for a BRAZERO item before each optional copy, as we also have + to do before the first copy if the minimum is zero. */ + + if (minval == 0) length++; + else if (minval > 1) length += (minval - 1) * duplength; + if (maxval > minval) length += (maxval - minval) * (duplength + 1); + } + continue; + + /* Non-special character. For a run of such characters the length required + is the number of characters + 2, except that the maximum run length is 255. + We won't get a skipped space or a non-data escape or the start of a # + comment as the first character, so the length can't be zero. */ + + NORMAL_CHAR: + default: + length += 2; + runlength = 0; + do + { + if ((options & PCRE_EXTENDED) != 0) + { + if ((compile_block.ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + } + + /* Backslash may introduce a data char or a metacharacter; stop the + string before the latter. */ + + if (c == '\\') + { + const uschar *saveptr = ptr; + c = check_escape(&ptr, errorptr, bracount, options, FALSE, + &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (c < 0) { ptr = saveptr; break; } + } + + /* Ordinary character or single-char escape */ + + runlength++; + } + + /* This "while" is the end of the "do" above. */ + + while (runlength < 255 && + (compile_block.ctypes[c = *(++ptr)] & ctype_meta) == 0); + + ptr--; + length += runlength; + continue; + } + } + +length += 4; /* For final KET and END */ + +if (length > 65539) + { + *errorptr = ERR20; + return NULL; + } + +/* Compute the size of data block needed and get it, either from malloc or +externally provided function. We specify "code[0]" in the offsetof() expression +rather than just "code", because it has been reported that one broken compiler +fails on "code" because it is also an independent variable. It should make no +difference to the value of the offsetof(). */ + +size = length + offsetof(real_pcre, code[0]); +re = (real_pcre *)(pcre_malloc)(size); + +if (re == NULL) + { + *errorptr = ERR21; + return NULL; + } + +/* Put in the magic number and the options. */ + +re->magic_number = MAGIC_NUMBER; +re->options = options; +re->tables = tables; + +/* Set up a starting, non-extracting bracket, then compile the expression. On +error, *errorptr will be set non-NULL, so we don't need to look at the result +of the function here. */ + +ptr = (const uschar *)pattern; +code = re->code; +*code = OP_BRA; +bracount = 0; +(void)compile_regex(options, -1, &bracount, &code, &ptr, errorptr, FALSE, -1, + &compile_block); +re->top_bracket = bracount; +re->top_backref = top_backref; + +/* If not reached end of pattern on success, there's an excess bracket. */ + +if (*errorptr == NULL && *ptr != 0) *errorptr = ERR22; + +/* Fill in the terminating state and check for disastrous overflow, but +if debugging, leave the test till after things are printed out. */ + +*code++ = OP_END; + +#ifndef DEBUG +if (code - re->code > length) *errorptr = ERR23; +#endif + +/* Give an error if there's back reference to a non-existent capturing +subpattern. */ + +if (top_backref > re->top_bracket) *errorptr = ERR15; + +/* Failed to compile */ + +if (*errorptr != NULL) + { + (pcre_free)(re); + PCRE_ERROR_RETURN: + *erroroffset = ptr - (const uschar *)pattern; + return NULL; + } + +/* If the anchored option was not passed, set flag if we can determine that it +is anchored by virtue of ^ characters or \A or anything else. Otherwise, see if +we can determine what the first character has to be, because that speeds up +unanchored matches no end. In the case of multiline matches, an alternative is +to set the PCRE_STARTLINE flag if all branches start with ^. */ + +if ((options & PCRE_ANCHORED) == 0) + { + int temp_options = options; + if (is_anchored(re->code, &temp_options)) + re->options |= PCRE_ANCHORED; + else + { + int ch = find_firstchar(re->code, &temp_options); + if (ch >= 0) + { + re->first_char = ch; + re->options |= PCRE_FIRSTSET; + } + else if (is_startline(re->code)) + re->options |= PCRE_STARTLINE; + } + } + +/* Print out the compiled data for debugging */ + +#ifdef DEBUG + +printf("Length = %d top_bracket = %d top_backref = %d\n", + length, re->top_bracket, re->top_backref); + +if (re->options != 0) + { + printf("%s%s%s%s%s%s%s%s\n", + ((re->options & PCRE_ANCHORED) != 0)? "anchored " : "", + ((re->options & PCRE_CASELESS) != 0)? "caseless " : "", + ((re->options & PCRE_EXTENDED) != 0)? "extended " : "", + ((re->options & PCRE_MULTILINE) != 0)? "multiline " : "", + ((re->options & PCRE_DOTALL) != 0)? "dotall " : "", + ((re->options & PCRE_DOLLAR_ENDONLY) != 0)? "endonly " : "", + ((re->options & PCRE_EXTRA) != 0)? "extra " : "", + ((re->options & PCRE_UNGREEDY) != 0)? "ungreedy " : ""); + } + +if ((re->options & PCRE_FIRSTSET) != 0) + { + if (isprint(re->first_char)) printf("First char = %c\n", re->first_char); + else printf("First char = \\x%02x\n", re->first_char); + } + +code_end = code; +code_base = code = re->code; + +while (code < code_end) + { + int charlength; + + printf("%3d ", code - code_base); + + if (*code >= OP_BRA) + { + printf("%3d Bra %d", (code[1] << 8) + code[2], *code - OP_BRA); + code += 2; + } + + else switch(*code) + { + case OP_OPT: + printf(" %.2x %s", code[1], OP_names[*code]); + code++; + break; + + case OP_COND: + printf("%3d Cond", (code[1] << 8) + code[2]); + code += 2; + break; + + case OP_CREF: + printf(" %.2d %s", code[1], OP_names[*code]); + code++; + break; + + case OP_CHARS: + charlength = *(++code); + printf("%3d ", charlength); + while (charlength-- > 0) + if (isprint(c = *(++code))) printf("%c", c); else printf("\\x%02x", c); + break; + + case OP_KETRMAX: + case OP_KETRMIN: + case OP_ALT: + case OP_KET: + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + printf("%3d %s", (code[1] << 8) + code[2], OP_names[*code]); + code += 2; + break; + + case OP_REVERSE: + printf("%3d %s", (code[1] << 8) + code[2], OP_names[*code]); + code += 2; + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + if (*code >= OP_TYPESTAR) + printf(" %s", OP_names[code[1]]); + else if (isprint(c = code[1])) printf(" %c", c); + else printf(" \\x%02x", c); + printf("%s", OP_names[*code++]); + break; + + case OP_EXACT: + case OP_UPTO: + case OP_MINUPTO: + if (isprint(c = code[3])) printf(" %c{", c); + else printf(" \\x%02x{", c); + if (*code != OP_EXACT) printf("0,"); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_MINUPTO) printf("?"); + code += 3; + break; + + case OP_TYPEEXACT: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + printf(" %s{", OP_names[code[3]]); + if (*code != OP_TYPEEXACT) printf(","); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_TYPEMINUPTO) printf("?"); + code += 3; + break; + + case OP_NOT: + if (isprint(c = *(++code))) printf(" [^%c]", c); + else printf(" [^\\x%02x]", c); + break; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + if (isprint(c = code[1])) printf(" [^%c]", c); + else printf(" [^\\x%02x]", c); + printf("%s", OP_names[*code++]); + break; + + case OP_NOTEXACT: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + if (isprint(c = code[3])) printf(" [^%c]{", c); + else printf(" [^\\x%02x]{", c); + if (*code != OP_NOTEXACT) printf(","); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_NOTMINUPTO) printf("?"); + code += 3; + break; + + case OP_REF: + printf(" \\%d", *(++code)); + code ++; + goto CLASS_REF_REPEAT; + + case OP_CLASS: + { + int i, min, max; + code++; + printf(" ["); + + for (i = 0; i < 256; i++) + { + if ((code[i/8] & (1 << (i&7))) != 0) + { + int j; + for (j = i+1; j < 256; j++) + if ((code[j/8] & (1 << (j&7))) == 0) break; + if (i == '-' || i == ']') printf("\\"); + if (isprint(i)) printf("%c", i); else printf("\\x%02x", i); + if (--j > i) + { + printf("-"); + if (j == '-' || j == ']') printf("\\"); + if (isprint(j)) printf("%c", j); else printf("\\x%02x", j); + } + i = j; + } + } + printf("]"); + code += 32; + + CLASS_REF_REPEAT: + + switch(*code) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + printf("%s", OP_names[*code]); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + min = (code[1] << 8) + code[2]; + max = (code[3] << 8) + code[4]; + if (max == 0) printf("{%d,}", min); + else printf("{%d,%d}", min, max); + if (*code == OP_CRMINRANGE) printf("?"); + code += 4; + break; + + default: + code--; + } + } + break; + + /* Anything else is just a one-node item */ + + default: + printf(" %s", OP_names[*code]); + break; + } + + code++; + printf("\n"); + } +printf("------------------------------------------------------------------\n"); + +/* This check is done here in the debugging case so that the code that +was compiled can be seen. */ + +if (code - re->code > length) + { + *errorptr = ERR23; + (pcre_free)(re); + *erroroffset = ptr - (uschar *)pattern; + return NULL; + } +#endif + +return (pcre *)re; +} + + + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* If a back reference hasn't been set, the length that is passed is greater +than the number of characters left in the string, so the match fails. + +Arguments: + offset index into the offset vector + eptr points into the subject + length length to be matched + md points to match data block + ims the ims flags + +Returns: TRUE if matched +*/ + +static BOOL +match_ref(int offset, register const uschar *eptr, int length, match_data *md, + int ims) +{ +const uschar *p = md->start_subject + md->offset_vector[offset]; + +#ifdef DEBUG +if (eptr >= md->end_subject) + printf("matching subject "); +else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + } +printf(" against backref "); +pchars(p, length, FALSE, md); +printf("\n"); +#endif + +/* Always fail if not enough characters left */ + +if (length > md->end_subject - eptr) return FALSE; + +/* Separate the caselesss case for speed */ + +if ((ims & PCRE_CASELESS) != 0) + { + while (length-- > 0) + if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; + } +else + { while (length-- > 0) if (*p++ != *eptr++) return FALSE; } + +return TRUE; +} + + + +/************************************************* +* Match from current position * +*************************************************/ + +/* On entry ecode points to the first opcode, and eptr to the first character +in the subject string, while eptrb holds the value of eptr at the start of the +last bracketed group - used for breaking infinite loops matching zero-length +strings. + +Arguments: + eptr pointer in subject + ecode position in code + offset_top current top pointer + md pointer to "static" info for the match + ims current /i, /m, and /s options + condassert TRUE if called to check a condition assertion + eptrb eptr at start of last bracket + +Returns: TRUE if matched +*/ + +static BOOL +match(register const uschar *eptr, register const uschar *ecode, + int offset_top, match_data *md, int ims, BOOL condassert, const uschar *eptrb) +{ +int original_ims = ims; /* Save for resetting on ')' */ + +for (;;) + { + int op = (int)*ecode; + int min, max, ctype; + register int i; + register int c; + BOOL minimize = FALSE; + + /* Opening capturing bracket. If there is space in the offset vector, save + the current subject position in the working slot at the top of the vector. We + mustn't change the current values of the data slot, because they may be set + from a previous iteration of this group, and be referred to by a reference + inside the group. + + If the bracket fails to match, we need to restore this value and also the + values of the final offsets, in case they were set by a previous iteration of + the same bracket. + + If there isn't enough space in the offset vector, treat this as if it were a + non-capturing bracket. Don't worry about setting the flag for the error case + here; that is handled in the code for KET. */ + + if (op > OP_BRA) + { + int number = op - OP_BRA; + int offset = number << 1; + + DPRINTF(("start bracket %d\n", number)); + + if (offset < md->offset_max) + { + int save_offset1 = md->offset_vector[offset]; + int save_offset2 = md->offset_vector[offset+1]; + int save_offset3 = md->offset_vector[md->offset_end - number]; + + DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); + md->offset_vector[md->offset_end - number] = eptr - md->start_subject; + + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + + DPRINTF(("bracket %d failed\n", number)); + + md->offset_vector[offset] = save_offset1; + md->offset_vector[offset+1] = save_offset2; + md->offset_vector[md->offset_end - number] = save_offset3; + return FALSE; + } + + /* Insufficient room for saving captured contents */ + + else op = OP_BRA; + } + + /* Other types of node can be handled by a switch */ + + switch(op) + { + case OP_BRA: /* Non-capturing bracket: optimized */ + DPRINTF(("start bracket 0\n")); + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + DPRINTF(("bracket 0 failed\n")); + return FALSE; + + /* Conditional group: compilation checked that there are no more than + two branches. If the condition is false, skipping the first branch takes us + past the end if there is only one branch, but that's OK because that is + exactly what going to the ket would do. */ + + case OP_COND: + if (ecode[3] == OP_CREF) /* Condition is extraction test */ + { + int offset = ecode[4] << 1; /* Doubled reference number */ + return match(eptr, + ecode + ((offset < offset_top && md->offset_vector[offset] >= 0)? + 5 : 3 + (ecode[1] << 8) + ecode[2]), + offset_top, md, ims, FALSE, eptr); + } + + /* The condition is an assertion. Call match() to evaluate it - setting + the final argument TRUE causes it to stop at the end of an assertion. */ + + else + { + if (match(eptr, ecode+3, offset_top, md, ims, TRUE, NULL)) + { + ecode += 3 + (ecode[4] << 8) + ecode[5]; + while (*ecode == OP_ALT) ecode += (ecode[1] << 8) + ecode[2]; + } + else ecode += (ecode[1] << 8) + ecode[2]; + return match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr); + } + /* Control never reaches here */ + + /* Skip over conditional reference data if encountered (should not be) */ + + case OP_CREF: + ecode += 2; + break; + + /* End of the pattern */ + + case OP_END: + md->end_match_ptr = eptr; /* Record where we ended */ + md->end_offset_top = offset_top; /* and how many extracts were taken */ + return TRUE; + + /* Change option settings */ + + case OP_OPT: + ims = ecode[1]; + ecode += 2; + DPRINTF(("ims set to %02x\n", ims)); + break; + + /* Assertion brackets. Check the alternative branches in turn - the + matching won't pass the KET for an assertion. If any one branch matches, + the assertion is true. Lookbehind assertions have an OP_REVERSE item at the + start of each branch to move the current point backwards, so the code at + this level is identical to the lookahead case. */ + + case OP_ASSERT: + case OP_ASSERTBACK: + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, NULL)) break; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + if (*ecode == OP_KET) return FALSE; + + /* If checking an assertion for a condition, return TRUE. */ + + if (condassert) return TRUE; + + /* Continue from after the assertion, updating the offsets high water + mark, since extracts may have been taken during the assertion. */ + + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + ecode += 3; + offset_top = md->end_offset_top; + continue; + + /* Negative assertion: all branches must fail to match */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, NULL)) return FALSE; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + + if (condassert) return TRUE; + ecode += 3; + continue; + + /* Move the subject pointer back. This occurs only at the start of + each branch of a lookbehind assertion. If we are too close to the start to + move back, this match function fails. */ + + case OP_REVERSE: + eptr -= (ecode[1] << 8) + ecode[2]; + if (eptr < md->start_subject) return FALSE; + ecode += 3; + break; + + + /* "Once" brackets are like assertion brackets except that after a match, + the point in the subject string is not moved back. Thus there can never be + a move back into the brackets. Check the alternative branches in turn - the + matching won't pass the KET for this kind of subpattern. If any one branch + matches, we carry on as at the end of a normal bracket, leaving the subject + pointer. */ + + case OP_ONCE: + { + const uschar *prev = ecode; + + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) break; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + + /* If hit the end of the group (which could be repeated), fail */ + + if (*ecode != OP_ONCE && *ecode != OP_ALT) return FALSE; + + /* Continue as from after the assertion, updating the offsets high water + mark, since extracts may have been taken. */ + + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*ecode == OP_KET || eptr == eptrb) + { + ecode += 3; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. We need to reset any options + that changed within the bracket before re-running it, so check the next + opcode. */ + + if (ecode[3] == OP_OPT) + { + ims = (ims & ~PCRE_IMS) | ecode[4]; + DPRINTF(("ims set to %02x at group repeat\n", ims)); + } + + if (*ecode == OP_KETRMIN) + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr) || + match(eptr, prev, offset_top, md, ims, FALSE, eptr)) return TRUE; + } + else /* OP_KETRMAX */ + { + if (match(eptr, prev, offset_top, md, ims, FALSE, eptr) || + match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + } + } + return FALSE; + + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group and go to there. */ + + case OP_ALT: + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + break; + + /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating + that it may occur zero times. It may repeat infinitely, or not at all - + i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper + repeat limits are compiled as a number of copies, with the optional ones + preceded by BRAZERO or BRAMINZERO. */ + + case OP_BRAZERO: + { + const uschar *next = ecode+1; + if (match(eptr, next, offset_top, md, ims, FALSE, eptr)) return TRUE; + do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); + ecode = next + 3; + } + break; + + case OP_BRAMINZERO: + { + const uschar *next = ecode+1; + do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); + if (match(eptr, next+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + ecode++; + } + break; + + /* End of a group, repeated or non-repeating. If we are at the end of + an assertion "group", stop matching and return TRUE, but record the + current high water mark for use by positive assertions. Do this also + for the "once" (not-backup up) groups. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + { + const uschar *prev = ecode - (ecode[1] << 8) - ecode[2]; + + if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || + *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || + *prev == OP_ONCE) + { + md->end_match_ptr = eptr; /* For ONCE */ + md->end_offset_top = offset_top; + return TRUE; + } + + /* In all other cases except a conditional group we have to check the + group number back at the start and if necessary complete handling an + extraction by setting the offsets and bumping the high water mark. */ + + if (*prev != OP_COND) + { + int number = *prev - OP_BRA; + int offset = number << 1; + + DPRINTF(("end bracket %d\n", number)); + + if (number > 0) + { + if (offset >= md->offset_max) md->offset_overflow = TRUE; else + { + md->offset_vector[offset] = + md->offset_vector[md->offset_end - number]; + md->offset_vector[offset+1] = eptr - md->start_subject; + if (offset_top <= offset) offset_top = offset + 2; + } + } + } + + /* Reset the value of the ims flags, in case they got changed during + the group. */ + + ims = original_ims; + DPRINTF(("ims reset to %02x\n", ims)); + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*ecode == OP_KET || eptr == eptrb) + { + ecode += 3; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. */ + + if (*ecode == OP_KETRMIN) + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr) || + match(eptr, prev, offset_top, md, ims, FALSE, eptr)) return TRUE; + } + else /* OP_KETRMAX */ + { + if (match(eptr, prev, offset_top, md, ims, FALSE, eptr) || + match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + } + } + return FALSE; + + /* Start of subject unless notbol, or after internal newline if multiline */ + + case OP_CIRC: + if (md->notbol && eptr == md->start_subject) return FALSE; + if ((ims & PCRE_MULTILINE) != 0) + { + if (eptr != md->start_subject && eptr[-1] != '\n') return FALSE; + ecode++; + break; + } + /* ... else fall through */ + + /* Start of subject assertion */ + + case OP_SOD: + if (eptr != md->start_subject) return FALSE; + ecode++; + break; + + /* Assert before internal newline if multiline, or before a terminating + newline unless endonly is set, else end of subject unless noteol is set. */ + + case OP_DOLL: + if ((ims & PCRE_MULTILINE) != 0) + { + if (eptr < md->end_subject) { if (*eptr != '\n') return FALSE; } + else { if (md->noteol) return FALSE; } + ecode++; + break; + } + else + { + if (md->noteol) return FALSE; + if (!md->endonly) + { + if (eptr < md->end_subject - 1 || + (eptr == md->end_subject - 1 && *eptr != '\n')) return FALSE; + + ecode++; + break; + } + } + /* ... else fall through */ + + /* End of subject assertion (\z) */ + + case OP_EOD: + if (eptr < md->end_subject) return FALSE; + ecode++; + break; + + /* End of subject or ending \n assertion (\Z) */ + + case OP_EODN: + if (eptr < md->end_subject - 1 || + (eptr == md->end_subject - 1 && *eptr != '\n')) return FALSE; + ecode++; + break; + + /* Word boundary assertions */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + { + BOOL prev_is_word = (eptr != md->start_subject) && + ((md->ctypes[eptr[-1]] & ctype_word) != 0); + BOOL cur_is_word = (eptr < md->end_subject) && + ((md->ctypes[*eptr] & ctype_word) != 0); + if ((*ecode++ == OP_WORD_BOUNDARY)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + return FALSE; + } + break; + + /* Match a single character type; inline for speed */ + + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == '\n') + return FALSE; + if (eptr++ >= md->end_subject) return FALSE; + ecode++; + break; + + case OP_NOT_DIGIT: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_digit) != 0) + return FALSE; + ecode++; + break; + + case OP_DIGIT: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_digit) == 0) + return FALSE; + ecode++; + break; + + case OP_NOT_WHITESPACE: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_space) != 0) + return FALSE; + ecode++; + break; + + case OP_WHITESPACE: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_space) == 0) + return FALSE; + ecode++; + break; + + case OP_NOT_WORDCHAR: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_word) != 0) + return FALSE; + ecode++; + break; + + case OP_WORDCHAR: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_word) == 0) + return FALSE; + ecode++; + break; + + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The code is similar + to that for character classes, but repeated for efficiency. Then obey + similar code to character type repeats - written out again for speed. + However, if the referenced string is the empty string, always treat + it as matched, any number of times (otherwise there could be infinite + loops). */ + + case OP_REF: + { + int length; + int offset = ecode[1] << 1; /* Doubled reference number */ + ecode += 2; /* Advance past the item */ + + /* If the reference is unset, set the length to be longer than the amount + of subject left; this ensures that every attempt at a match fails. We + can't just fail here, because of the possibility of quantifiers with zero + minima. */ + + length = (offset >= offset_top || md->offset_vector[offset] < 0)? + md->end_subject - eptr + 1 : + md->offset_vector[offset+1] - md->offset_vector[offset]; + + /* Set up for repetition, or handle the non-repeated case */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = (ecode[1] << 8) + ecode[2]; + max = (ecode[3] << 8) + ecode[4]; + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + if (!match_ref(offset, eptr, length, md, ims)) return FALSE; + eptr += length; + continue; /* With the main loop */ + } + + /* If the length of the reference is zero, just continue with the + main loop. */ + + if (length == 0) continue; + + /* First, ensure the minimum number of matches are present. We get back + the length of the reference string explicitly rather than passing the + address of eptr, so that eptr can be a register variable. */ + + for (i = 1; i <= min; i++) + { + if (!match_ref(offset, eptr, length, md, ims)) return FALSE; + eptr += length; + } + + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ + + if (min == max) continue; + + /* If minimizing, keep trying and advancing the pointer */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || !match_ref(offset, eptr, length, md, ims)) + return FALSE; + eptr += length; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest string and work backwards */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (!match_ref(offset, eptr, length, md, ims)) break; + eptr += length; + } + while (eptr >= pp) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + eptr -= length; + } + return FALSE; + } + } + /* Control never gets here */ + + + + /* Match a character class, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. Then obey similar + code to character type repeats - written out again for speed. */ + + case OP_CLASS: + { + const uschar *data = ecode + 1; /* Save for matching */ + ecode += 33; /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = (ecode[1] << 8) + ecode[2]; + max = (ecode[3] << 8) + ecode[4]; + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) return FALSE; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + return FALSE; + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject) return FALSE; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + return FALSE; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; eptr++, i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + break; + } + + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + } + /* Control never gets here */ + + /* Match a run of characters */ + + case OP_CHARS: + { + register int length = ecode[1]; + ecode += 2; + +#ifdef DEBUG /* Sigh. Some compilers never learn. */ + if (eptr >= md->end_subject) + printf("matching subject against pattern "); + else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + printf(" against pattern "); + } + pchars(ecode, length, FALSE, md); + printf("\n"); +#endif + + if (length > md->end_subject - eptr) return FALSE; + if ((ims & PCRE_CASELESS) != 0) + { + while (length-- > 0) + if (md->lcc[*ecode++] != md->lcc[*eptr++]) + return FALSE; + } + else + { + while (length-- > 0) if (*ecode++ != *eptr++) return FALSE; + } + } + break; + + /* Match a single character repeatedly; different opcodes share code. */ + + case OP_EXACT: + min = max = (ecode[1] << 8) + ecode[2]; + ecode += 3; + goto REPEATCHAR; + + case OP_UPTO: + case OP_MINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_MINUPTO; + ecode += 3; + goto REPEATCHAR; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + c = *ecode++ - OP_STAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATCHAR: + if (min > md->end_subject - eptr) return FALSE; + c = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max, + max, eptr)); + + if ((ims & PCRE_CASELESS) != 0) + { + c = md->lcc[c]; + for (i = 1; i <= min; i++) + if (c != md->lcc[*eptr++]) return FALSE; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject || + c != md->lcc[*eptr++]) + return FALSE; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c != md->lcc[*eptr]) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { + for (i = 1; i <= min; i++) if (c != *eptr++) return FALSE; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject || c != *eptr++) return FALSE; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c != *eptr) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + } + /* Control never gets here */ + + /* Match a negated single character */ + + case OP_NOT: + if (eptr >= md->end_subject) return FALSE; + ecode++; + if ((ims & PCRE_CASELESS) != 0) + { + if (md->lcc[*ecode++] == md->lcc[*eptr++]) return FALSE; + } + else + { + if (*ecode++ == *eptr++) return FALSE; + } + break; + + /* Match a negated single character repeatedly. This is almost a repeat of + the code for a repeated single character, but I haven't found a nice way of + commoning these up that doesn't require a test of the positive/negative + option for each character match. Maybe that wouldn't add very much to the + time taken, but character matching *is* what this is all about... */ + + case OP_NOTEXACT: + min = max = (ecode[1] << 8) + ecode[2]; + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTUPTO: + case OP_NOTMINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_NOTMINUPTO; + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + c = *ecode++ - OP_NOTSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATNOTCHAR: + if (min > md->end_subject - eptr) return FALSE; + c = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max, + max, eptr)); + + if ((ims & PCRE_CASELESS) != 0) + { + c = md->lcc[c]; + for (i = 1; i <= min; i++) + if (c == md->lcc[*eptr++]) return FALSE; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject || + c == md->lcc[*eptr++]) + return FALSE; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c == md->lcc[*eptr]) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { + for (i = 1; i <= min; i++) if (c == *eptr++) return FALSE; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject || c == *eptr++) return FALSE; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c == *eptr) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + } + /* Control never gets here */ + + /* Match a single character type repeatedly; several different opcodes + share code. This is very similar to the code for single characters, but we + repeat it in the interests of efficiency. */ + + case OP_TYPEEXACT: + min = max = (ecode[1] << 8) + ecode[2]; + minimize = TRUE; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_TYPEMINUPTO; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + c = *ecode++ - OP_TYPESTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single character type matches */ + + REPEATTYPE: + ctype = *ecode++; /* Code for the character type */ + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). Also test that there are at least the + minimum number of characters before we start. */ + + if (min > md->end_subject - eptr) return FALSE; + if (min > 0) switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { for (i = 1; i <= min; i++) if (*eptr++ == '\n') return FALSE; } + else eptr += min; + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_digit) != 0) return FALSE; + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_digit) == 0) return FALSE; + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_space) != 0) return FALSE; + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_space) == 0) return FALSE; + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_word) != 0) + return FALSE; + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_word) == 0) + return FALSE; + break; + } + + /* If min = max, continue at the same level without recursing */ + + if (min == max) continue; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) return TRUE; + if (i >= max || eptr >= md->end_subject) return FALSE; + + c = *eptr++; + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0 && c == '\n') return FALSE; + break; + + case OP_NOT_DIGIT: + if ((md->ctypes[c] & ctype_digit) != 0) return FALSE; + break; + + case OP_DIGIT: + if ((md->ctypes[c] & ctype_digit) == 0) return FALSE; + break; + + case OP_NOT_WHITESPACE: + if ((md->ctypes[c] & ctype_space) != 0) return FALSE; + break; + + case OP_WHITESPACE: + if ((md->ctypes[c] & ctype_space) == 0) return FALSE; + break; + + case OP_NOT_WORDCHAR: + if ((md->ctypes[c] & ctype_word) != 0) return FALSE; + break; + + case OP_WORDCHAR: + if ((md->ctypes[c] & ctype_word) == 0) return FALSE; + break; + } + } + /* Control never gets here */ + } + + /* If maximizing it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). */ + + else + { + const uschar *pp = eptr; + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || *eptr == '\n') break; + eptr++; + } + } + else + { + c = max - min; + if (c > md->end_subject - eptr) c = md->end_subject - eptr; + eptr += c; + } + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0) + break; + eptr++; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0) + break; + eptr++; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0) + break; + eptr++; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0) + break; + eptr++; + } + break; + } + + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + /* Control never gets here */ + + /* There's been some horrible disaster. */ + + default: + DPRINTF(("Unknown opcode %d\n", *ecode)); + md->errorcode = PCRE_ERROR_UNKNOWN_NODE; + return FALSE; + } + + /* Do not stick any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ +/* Control never reaches here */ +} + + + + +/************************************************* +* Execute a Regular Expression * +*************************************************/ + +/* This function applies a compiled re to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +Arguments: + external_re points to the compiled expression + external_extra points to "hints" from pcre_study() or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + options option bits + offsets points to a vector of ints to be filled in with offsets + offsetcount the number of elements in the vector + +Returns: > 0 => success; value is the number of elements filled in + = 0 => success, but offsets is not big enough + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +int +pcre_exec(const pcre *external_re, const pcre_extra *external_extra, + const char *subject, int length, int options, int *offsets, int offsetcount) +{ +int resetcount, ocount; +int first_char = -1; +int ims = 0; +match_data match_block; +const uschar *start_bits = NULL; +const uschar *start_match = (const uschar *)subject; +const uschar *end_subject; +const real_pcre *re = (const real_pcre *)external_re; +const real_pcre_extra *extra = (const real_pcre_extra *)external_extra; +BOOL using_temporary_offsets = FALSE; +BOOL anchored = ((re->options | options) & PCRE_ANCHORED) != 0; +BOOL startline = (re->options & PCRE_STARTLINE) != 0; + +if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; + +if (re == NULL || subject == NULL || + (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; + +match_block.start_subject = (const uschar *)subject; +match_block.end_subject = match_block.start_subject + length; +end_subject = match_block.end_subject; + +match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; + +match_block.notbol = (options & PCRE_NOTBOL) != 0; +match_block.noteol = (options & PCRE_NOTEOL) != 0; + +match_block.errorcode = PCRE_ERROR_NOMATCH; /* Default error */ + +match_block.lcc = re->tables + lcc_offset; +match_block.ctypes = re->tables + ctypes_offset; + +/* The ims options can vary during the matching as a result of the presence +of (?ims) items in the pattern. They are kept in a local variable so that +restoring at the exit of a group is easy. */ + +ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL); + +/* If the expression has got more back references than the offsets supplied can +hold, we get a temporary bit of working store to use during the matching. +Otherwise, we can use the vector supplied, rounding down its size to a multiple +of 3. */ + +ocount = offsetcount - (offsetcount % 3); + +if (re->top_backref > 0 && re->top_backref >= ocount/3) + { + ocount = re->top_backref * 3 + 3; + match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int)); + if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY; + using_temporary_offsets = TRUE; + DPRINTF(("Got memory to hold back references\n")); + } +else match_block.offset_vector = offsets; + +match_block.offset_end = ocount; +match_block.offset_max = (2*ocount)/3; +match_block.offset_overflow = FALSE; + +/* Compute the minimum number of offsets that we need to reset each time. Doing +this makes a huge difference to execution time when there aren't many brackets +in the pattern. */ + +resetcount = 2 + re->top_bracket * 2; +if (resetcount > offsetcount) resetcount = ocount; + +/* Reset the working variable associated with each extraction. These should +never be used unless previously set, but they get saved and restored, and so we +initialize them to avoid reading uninitialized locations. */ + +if (match_block.offset_vector != NULL) + { + register int *iptr = match_block.offset_vector + ocount; + register int *iend = iptr - resetcount/2 + 1; + while (--iptr >= iend) *iptr = -1; + } + +/* Set up the first character to match, if available. The first_char value is +never set for an anchored regular expression, but the anchoring may be forced +at run time, so we have to test for anchoring. The first char may be unset for +an unanchored pattern, of course. If there's no first char and the pattern was +studied, there may be a bitmap of possible first characters. */ + +if (!anchored) + { + if ((re->options & PCRE_FIRSTSET) != 0) + { + first_char = re->first_char; + if ((ims & PCRE_CASELESS) != 0) first_char = match_block.lcc[first_char]; + } + else + if (!startline && extra != NULL && + (extra->options & PCRE_STUDY_MAPPED) != 0) + start_bits = extra->start_bits; + } + +/* Loop for unanchored matches; for anchored regexps the loop runs just once. */ + +do + { + int rc; + register int *iptr = match_block.offset_vector; + register int *iend = iptr + resetcount; + + /* Reset the maximum number of extractions we might see. */ + + while (iptr < iend) *iptr++ = -1; + + /* Advance to a unique first char if possible */ + + if (first_char >= 0) + { + if ((ims & PCRE_CASELESS) != 0) + while (start_match < end_subject && + match_block.lcc[*start_match] != first_char) + start_match++; + else + while (start_match < end_subject && *start_match != first_char) + start_match++; + } + + /* Or to just after \n for a multiline match if possible */ + + else if (startline) + { + if (start_match > match_block.start_subject) + { + while (start_match < end_subject && start_match[-1] != '\n') + start_match++; + } + } + + /* Or to a non-unique first char */ + + else if (start_bits != NULL) + { + while (start_match < end_subject) + { + register int c = *start_match; + if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break; + } + } + +#ifdef DEBUG /* Sigh. Some compilers never learn. */ + printf(">>>> Match against: "); + pchars(start_match, end_subject - start_match, TRUE, &match_block); + printf("\n"); +#endif + + /* When a match occurs, substrings will be set for all internal extractions; + we just need to set up the whole thing as substring 0 before returning. If + there were too many extractions, set the return code to zero. In the case + where we had to get some local store to hold offsets for backreferences, copy + those back references that we can. In this case there need not be overflow + if certain parts of the pattern were not used. */ + + if (!match(start_match, re->code, 2, &match_block, ims, FALSE, start_match)) + continue; + + /* Copy the offset information from temporary store if necessary */ + + if (using_temporary_offsets) + { + if (offsetcount >= 4) + { + memcpy(offsets + 2, match_block.offset_vector + 2, + (offsetcount - 2) * sizeof(int)); + DPRINTF(("Copied offsets from temporary memory\n")); + } + if (match_block.end_offset_top > offsetcount) + match_block.offset_overflow = TRUE; + + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(match_block.offset_vector); + } + + rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2; + + if (match_block.offset_end < 2) rc = 0; else + { + offsets[0] = start_match - match_block.start_subject; + offsets[1] = match_block.end_match_ptr - match_block.start_subject; + } + + DPRINTF((">>>> returning %d\n", rc)); + return rc; + } + +/* This "while" is the end of the "do" above */ + +while (!anchored && + match_block.errorcode == PCRE_ERROR_NOMATCH && + start_match++ < end_subject); + +if (using_temporary_offsets) + { + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(match_block.offset_vector); + } + +DPRINTF((">>>> returning %d\n", match_block.errorcode)); + +return match_block.errorcode; +} + +/* End of pcre.c */ diff --git a/Utils/Libs/GLib/pcre.h b/Utils/Libs/GLib/pcre.h new file mode 100644 index 000000000..5224e256e --- /dev/null +++ b/Utils/Libs/GLib/pcre.h @@ -0,0 +1,70 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* Copyright (c) 1997-1999 University of Cambridge */ + +#ifndef _PCRE_H +#define _PCRE_H + +/* Have to include stdlib.h in order to ensure that size_t is defined; +it is needed here for malloc. */ + +#include +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options */ + +#define PCRE_CASELESS 0x0001 +#define PCRE_MULTILINE 0x0002 +#define PCRE_DOTALL 0x0004 +#define PCRE_EXTENDED 0x0008 +#define PCRE_ANCHORED 0x0010 +#define PCRE_DOLLAR_ENDONLY 0x0020 +#define PCRE_EXTRA 0x0040 +#define PCRE_NOTBOL 0x0080 +#define PCRE_NOTEOL 0x0100 +#define PCRE_UNGREEDY 0x0200 + +/* Exec-time error codes */ + +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_NULL (-2) +#define PCRE_ERROR_BADOPTION (-3) +#define PCRE_ERROR_BADMAGIC (-4) +#define PCRE_ERROR_UNKNOWN_NODE (-5) +#define PCRE_ERROR_NOMEMORY (-6) + +/* Types */ + +typedef void pcre; +typedef void pcre_extra; + +/* Store get and free functions. These can be set to alternative malloc/free +functions if required. */ + +extern void *(*pcre_malloc)(size_t); +extern void (*pcre_free)(void *); + +/* Functions */ + +extern pcre *pcre_compile(const char *, int, const char **, int *, + const unsigned char *); +extern int pcre_exec(const pcre *, const pcre_extra *, const char *, + int, int, int *, int); +extern unsigned const char *pcre_maketables(void); +extern int pcre_info(const pcre *, int *, int *); +extern pcre_extra *pcre_study(const pcre *, int, const char **); +extern const char *pcre_version(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcre.h */ diff --git a/Utils/Libs/GLib/study.c b/Utils/Libs/GLib/study.c new file mode 100644 index 000000000..40f489b0c --- /dev/null +++ b/Utils/Libs/GLib/study.c @@ -0,0 +1,393 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-1999 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. +----------------------------------------------------------------------------- +*/ + + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + +#include "internal.h" + + + +/************************************************* +* Set a bit and maybe its alternate case * +*************************************************/ + +/* Given a character, set its bit in the table, and also the bit for the other +version of a letter if we are caseless. + +Arguments: + start_bits points to the bit map + c is the character + caseless the caseless flag + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_bit(uschar *start_bits, int c, BOOL caseless, compile_data *cd) +{ +start_bits[c/8] |= (1 << (c&7)); +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) + start_bits[cd->fcc[c]/8] |= (1 << (cd->fcc[c]&7)); +} + + + +/************************************************* +* Create bitmap of starting chars * +*************************************************/ + +/* This function scans a compiled unanchored expression and attempts to build a +bitmap of the set of initial characters. If it can't, it returns FALSE. As time +goes by, we may be able to get more clever at doing this. + +Arguments: + code points to an expression + start_bits points to a 32-byte table, initialized to 0 + caseless the current state of the caseless flag + cd the block with char table pointers + +Returns: TRUE if table built, FALSE otherwise +*/ + +static BOOL +set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless, + compile_data *cd) +{ +register int c; + +/* This next statement and the later reference to dummy are here in order to +trick the optimizer of the IBM C compiler for OS/2 into generating correct +code. Apparently IBM isn't going to fix the problem, and we would rather not +disable optimization (in this module it actually makes a big difference, and +the pcre module can use all the optimization it can get). */ + +volatile int dummy; + +do + { + const uschar *tcode = code + 3; + BOOL try_next = TRUE; + + while (try_next) + { + try_next = FALSE; + + /* If a branch starts with a bracket or a positive lookahead assertion, + recurse to set bits from within them. That's all for this branch. */ + + if ((int)*tcode >= OP_BRA || *tcode == OP_ASSERT) + { + if (!set_start_bits(tcode, start_bits, caseless, cd)) + return FALSE; + } + + else switch(*tcode) + { + default: + return FALSE; + + /* Skip over lookbehind and negative lookahead assertions */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + try_next = TRUE; + do tcode += (tcode[1] << 8) + tcode[2]; while (*tcode == OP_ALT); + tcode += 3; + break; + + /* Skip over an option setting, changing the caseless flag */ + + case OP_OPT: + caseless = (tcode[1] & PCRE_CASELESS) != 0; + tcode += 2; + try_next = TRUE; + break; + + /* BRAZERO does the bracket, but carries on. */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + if (!set_start_bits(++tcode, start_bits, caseless, cd)) + return FALSE; + dummy = 1; + do tcode += (tcode[1] << 8) + tcode[2]; while (*tcode == OP_ALT); + tcode += 3; + try_next = TRUE; + break; + + /* Single-char * or ? sets the bit and tries the next item */ + + case OP_STAR: + case OP_MINSTAR: + case OP_QUERY: + case OP_MINQUERY: + set_bit(start_bits, tcode[1], caseless, cd); + tcode += 2; + try_next = TRUE; + break; + + /* Single-char upto sets the bit and tries the next */ + + case OP_UPTO: + case OP_MINUPTO: + set_bit(start_bits, tcode[3], caseless, cd); + tcode += 4; + try_next = TRUE; + break; + + /* At least one single char sets the bit and stops */ + + case OP_EXACT: /* Fall through */ + tcode++; + + case OP_CHARS: /* Fall through */ + tcode++; + + case OP_PLUS: + case OP_MINPLUS: + set_bit(start_bits, tcode[1], caseless, cd); + break; + + /* Single character type sets the bits and stops */ + + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_digit]; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_digit]; + break; + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_space]; + break; + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_space]; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~(cd->cbits[c] | cd->cbits[c+cbit_word]); + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= (cd->cbits[c] | cd->cbits[c+cbit_word]); + break; + + /* One or more character type fudges the pointer and restarts, knowing + it will hit a single character type and stop there. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + tcode++; + try_next = TRUE; + break; + + case OP_TYPEEXACT: + tcode += 3; + try_next = TRUE; + break; + + /* Zero or more repeats of character types set the bits and then + try again. */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + tcode += 2; /* Fall through */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + switch(tcode[1]) + { + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_digit]; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_digit]; + break; + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_space]; + break; + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_space]; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~(cd->cbits[c] | cd->cbits[c+cbit_word]); + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= (cd->cbits[c] | cd->cbits[c+cbit_word]); + break; + } + + tcode += 2; + try_next = TRUE; + break; + + /* Character class: set the bits and either carry on or not, + according to the repeat count. */ + + case OP_CLASS: + { + tcode++; + for (c = 0; c < 32; c++) start_bits[c] |= tcode[c]; + tcode += 32; + switch (*tcode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + tcode++; + try_next = TRUE; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (((tcode[1] << 8) + tcode[2]) == 0) + { + tcode += 5; + try_next = TRUE; + } + break; + } + } + break; /* End of class handling */ + + } /* End of switch */ + } /* End of try_next loop */ + + code += (code[1] << 8) + code[2]; /* Advance to next branch */ + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Study a compiled expression * +*************************************************/ + +/* This function is handed a compiled expression that it must study to produce +information that will speed up the matching. It returns a pcre_extra block +which then gets handed back to pcre_exec(). + +Arguments: + re points to the compiled expression + options contains option bits + errorptr points to where to place error messages; + set NULL unless error + +Returns: pointer to a pcre_extra block, + NULL on error or if no optimization possible +*/ + +pcre_extra * +pcre_study(const pcre *external_re, int options, const char **errorptr) +{ +uschar start_bits[32]; +real_pcre_extra *extra; +const real_pcre *re = (const real_pcre *)external_re; +compile_data compile_block; + +*errorptr = NULL; + +if (re == NULL || re->magic_number != MAGIC_NUMBER) + { + *errorptr = "argument is not a compiled regular expression"; + return NULL; + } + +if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) + { + *errorptr = "unknown or incorrect option bit(s) set"; + return NULL; + } + +/* For an anchored pattern, or an unchored pattern that has a first char, or a +multiline pattern that matches only at "line starts", no further processing at +present. */ + +if ((re->options & (PCRE_ANCHORED|PCRE_FIRSTSET|PCRE_STARTLINE)) != 0) + return NULL; + +/* Set the character tables in the block which is passed around */ + +compile_block.lcc = re->tables + lcc_offset; +compile_block.fcc = re->tables + fcc_offset; +compile_block.cbits = re->tables + cbits_offset; +compile_block.ctypes = re->tables + ctypes_offset; + +/* See if we can find a fixed set of initial characters for the pattern. */ + +memset(start_bits, 0, 32 * sizeof(uschar)); +if (!set_start_bits(re->code, start_bits, (re->options & PCRE_CASELESS) != 0, + &compile_block)) return NULL; + +/* Get an "extra" block and put the information therein. */ + +extra = (real_pcre_extra *)(pcre_malloc)(sizeof(real_pcre_extra)); + +if (extra == NULL) + { + *errorptr = "failed to get memory"; + return NULL; + } + +extra->options = PCRE_STUDY_MAPPED; +memcpy(extra->start_bits, start_bits, sizeof(start_bits)); + +return (pcre_extra *)extra; +} + +/* End of study.c */ diff --git a/Utils/Libs/GLib/tasker.c b/Utils/Libs/GLib/tasker.c new file mode 100644 index 000000000..a965ad80d --- /dev/null +++ b/Utils/Libs/GLib/tasker.c @@ -0,0 +1,860 @@ +/* =========================================================================== + File: TASKER.C + + Notes: Cooperative multitasking + + Author: G Robert Liddon @ 73b + + Created: Wednesday 27th March 1996 + + Copyright (C) 1996 DCI Ltd All rights reserved. + ============================================================================ */ + +/* --------------------------------------------------------------------------- + Standard Lib Includes + --------------------- */ + +/* --------------------------------------------------------------------------- + Glib Includes + ------------- */ +#include "tasker.h" +#include "gsys.h" +#include "gdebug.h" + +#include "gtimsys.h" + +/* --------------------------------------------------------------------------- + My Includes + ----------- */ + +/* --------------------------------------------------------------------------- + Defines + ------- */ + +/* --------------------------------------------------------------------------- + Function Prototypes + ------------------- */ +static void ReturnToSchedulerIfCurrentTask(TASK *T); +static void ExecuteTask(TASK *T); +static void AddToList(TASK **Head,TASK *ThisObj); +static void DetachFromList(TASK **Head,TASK *ThisObj); +static void LoTskKill(TASK *T); +static void DoEpi(TASK * T); +static void DoPro(TASK * T); +static void ExtraMarkStack(u32 * Stack,int SizeLongs); +static int CheckExtraStack(u32 * Stack,int LongsToCheck); + +/* --------------------------------------------------------------------------- + Defines + ------- */ +#define NUM_OF_TASKS 100 +#define DEFAULT_XTRA_PRO_STACK_LONGS 1024 + +/* --------------------------------------------------------------------------- + Variables + --------- */ +static TASK * ActiveTasks; +static TASK * CurrentTask; +static TASK * T; + +static U32 MemTypeForTasker; +static jmp_buf SchEnv; /* The Scheduler's enviroment */ +static U32 ExecId; +static U32 ExecMask; +static int TasksActive; + + +/* Vars for epi pro stuff + ---------------------- */ +static TSK_CBACK EpiFunc; /* Func to call before execing task of correct class */ +static TSK_CBACK ProFunc; /* Func to call after execing task of correct class */ +static U32 EpiProId; +static U32 EpiProMask; + +static DOTSK_CBACK DoTasksPrologue; +static DOTSK_CBACK DoTasksEpilogue; + +/* Vars for Xtra Stack Protection + ------------------------------ */ +static TSK_CBACK StackFloodCallback; +static BOOL ExtraStackProtection; +static int ExtraStackSizeLongs; + +/* --------------------------------------------------------------------------- + Function: static void DoEpi(TASK * T) + Purpose: Do epilogue route if appropriate + --------------------------------------------------------------------------- */ +static void DoEpi(TASK * T) +{ + if (EpiFunc) + if ((T->Id&EpiProMask)==EpiProId) + EpiFunc(T); +} + +/* --------------------------------------------------------------------------- + Function: static void DoPro(TASK * T) + Purpose: Do prologue route if appropriate + --------------------------------------------------------------------------- */ +static void DoPro(TASK * T) +{ + if (ProFunc) + if ((T->Id&EpiProMask)==EpiProId) + ProFunc(T); +} + +/* --------------------------------------------------------------------------- + Function: BOOL TSK_OpenModule(U32 MemType) + + Purpose: Init the tasker module + + Params: MemType = Mem tasker uses for workspace + + Returns: FALSE if an error + --------------------------------------------------------------------------- */ +BOOL TSK_OpenModule(U32 MemType) +{ + + TasksActive=0; + ActiveTasks=NULL; + MemTypeForTasker=MemType; + CurrentTask=NULL; + TSK_ClearExecFilter(); + TSK_ClearEpiProFilter(); + TSK_SetDoTasksEpilogue(NULL); + TSK_SetDoTasksPrologue(NULL); + TSK_SetExtraStackProtection(FALSE); + TSK_SetStackFloodCallback(NULL); + TSK_SetExtraStackSize(DEFAULT_XTRA_PRO_STACK_LONGS*sizeof(u32)); + + return TRUE; +} + +/* --------------------------------------------------------------------------- + Function: TASK * TSK_AddTask(U32 Id,void (*Main)(TASK *T)) + + Purpose: Add a task to the list of those to do + + Returns: -> to task to run + NULL if no tasks available + --------------------------------------------------------------------------- */ +TASK * TSK_AddTask(U32 Id,void (*Main)(TASK *T),int StackSize,int DataSize) +{ + TASK * RetTask; + MHANDLE hndTask; + + GAL_STRUCT G[4]; + + G[0].OriginalSize=sizeof(TASK); + G[1].OriginalSize=DataSize; + + if (ExtraStackProtection) + G[2].OriginalSize=StackSize+ExtraStackSizeLongs*sizeof(u32); + else + G[2].OriginalSize=StackSize; + + + G[3].OriginalSize=-1; + + hndTask=GAL_AllocMultiStruct(G,MemTypeForTasker,"TASK"); + + if (hndTask==NULL_HANDLE) + return(NULL); + + RetTask=GAL_Lock(hndTask); + + if (RetTask) + { + RetTask->Id = Id; + + RetTask->fToInit=1; + RetTask->fToDie=0; + RetTask->fKillable=1; + RetTask->fActive=1; + + RetTask->Main=Main; + + RetTask->hndTask=hndTask; + + RetTask->Stack=(void *)(((U32) RetTask)+G[2].Offset); + RetTask->StackSize=G[3].Offset-G[2].Offset; + + if (DataSize) + RetTask->Data=(void *)(((U32) RetTask)+G[1].Offset); + else + RetTask->Data=NULL; + + RetTask->SleepTime=1; + RetTask->fXtraStack=ExtraStackProtection; + RetTask->XtraLongs=ExtraStackSizeLongs; + + if (RetTask->fXtraStack) + ExtraMarkStack(RetTask->Stack,RetTask->StackSize/sizeof(u32)); + else + GSYS_MarkStack(RetTask->Stack,RetTask->StackSize); + + /* if a task running then add as next in list or at beggining */ + + if (CurrentTask) + { + AddToList(&CurrentTask->Next,RetTask); + RetTask->Prev=CurrentTask; + } + else + AddToList(&ActiveTasks,RetTask); + + TasksActive++; + + } + + return RetTask; + +} + +/* --------------------------------------------------------------------------- + Function: void TSK_DoTasks(void) + Purpose: Run All the tasks + --------------------------------------------------------------------------- */ +void TSK_DoTasks(void) +{ + T=ActiveTasks; + + if (DoTasksPrologue) + DoTasksPrologue(); + + while (T) + { + if (T->fActive && (T->Id&ExecMask)==ExecId) + { + T->SleepTime--; + + if (!T->SleepTime) + { + + if (!setjmp(SchEnv)) + { + + /* See if this task needs initing or not */ + CurrentTask=T; + + DoPro(T); + + if (T->fToInit) + { + T->fToInit=0; + GSYS_SetStackAndJump((void *)((U32)T->Stack+T->StackSize-sizeof(void *)*4),(void *)ExecuteTask,T); + } + else + longjmp(T->TskEnv,1); + } + else + { + + /* Back from the previous task */ + TASK * NextT; + + + NextT=T->Next; + + /* Top this task if it was intended to die */ + + if (T->fToDie) + LoTskKill(T); + + T=NextT; + } + } + else + T=T->Next; + } + else + T=T->Next; + } + + if (DoTasksEpilogue) + DoTasksEpilogue(); + + CurrentTask=NULL; +} + + +/* --------------------------------------------------------------------------- + Function: void TSK_Sleep(int Frames) + Purpose: This Task to sleep for an amount of frames + Params: T -> Task + Frames = num of frames to sleep + --------------------------------------------------------------------------- */ +void TSK_Sleep(int Frames) +{ + TASK * T; + + T=CurrentTask; + + + ASSERT(T); + + if (TSK_IsStackCorrupted(T)) + { + if (StackFloodCallback) + StackFloodCallback(T); + else + ASSERT(!"TSK STACK CORRUPTION"); + } + +// ASSERT(!GSYS_IsStackOutOfBounds(T->Stack,T->StackSize)); + + DoEpi(T); + + if (!setjmp(T->TskEnv)) + { + /* Saving enviro so go back to scheduler */ + T->SleepTime=Frames; + ReturnToSchedulerIfCurrentTask(CurrentTask); + } +} + +/* --------------------------------------------------------------------------- + Function: static void ReturnToScheduler(TASK *T) + Purpose: Return to scheduler + Params: T -> Current Task + --------------------------------------------------------------------------- */ +static void ReturnToSchedulerIfCurrentTask(TASK *T) +{ + if (TSK_IsStackCorrupted(T)) + { + if (StackFloodCallback) + StackFloodCallback(T); + else + ASSERT(!"TSK STACK CORRUPTION"); + } + + longjmp(SchEnv,1); +} + +/* --------------------------------------------------------------------------- + Function: void TSK_Die(void) + Purpose: Kill off current task + --------------------------------------------------------------------------- */ +void TSK_Die(void) +{ + if (CurrentTask) + TSK_Kill(CurrentTask); +} + +/* --------------------------------------------------------------------------- + Function: void TSK_Kill(TASK *T) + Purpose: Kill off a task + If this is the current task then return to scheduler + Params: T -> Task to kill + --------------------------------------------------------------------------- */ +void TSK_Kill(TASK *T) +{ + if (T==CurrentTask && CurrentTask) + { + T->fToDie=TRUE; + ReturnToSchedulerIfCurrentTask(T); + } + else + LoTskKill(T); +} + + +/* --------------------------------------------------------------------------- + Function: TASK * TSK_GetFirstActive(void); + + Purpose: Get the first in the chain of active tasks + + Params: T -> Task block to check + + Returns: True if it is + --------------------------------------------------------------------------- */ +TASK * TSK_GetFirstActive(void) +{ + return(ActiveTasks); +} + +/* --------------------------------------------------------------------------- + Function: BOOL TSK_IsStackCorrupted(TASK *T) + + Purpose: Check to see if this task's stack has flooded + + Params: T -> Task block to check + + Returns: True if it is + --------------------------------------------------------------------------- */ +BOOL TSK_IsStackCorrupted(TASK *T) +{ + if (T->fXtraStack) + { + int LongsOk; + LongsOk=CheckExtraStack(T->Stack,T->StackSize/4); + T->MaxStackSizeBytes=(u16)T->StackSize-(LongsOk*sizeof(u32)); + return (LongsOk < T->XtraLongs); + } + else + return(GSYS_IsStackCorrupted(T->Stack,T->StackSize)); +} + +/* --------------------------------------------------------------------------- + Function: void TSK_JumpAndResetStack(void (*RunFunc)(TASK *)) + + Purpose: Current running task stack is reset and jumped off to + another routine + + Params: T -> Task block to check + + Returns: True if it is + --------------------------------------------------------------------------- */ +void TSK_JumpAndResetStack(void (*RunFunc)(TASK *)) +{ + TASK * T; + + T=CurrentTask; + + if (T) + { + T->Main=RunFunc; + GSYS_SetStackAndJump((void *)((U32)T->Stack+T->StackSize-sizeof(void *)*4),(void *)ExecuteTask,T); + } +} + +/* --------------------------------------------------------------------------- + Function: void TSK_SetStackAndJump(void (*RunFunc)(TASK *)) + + Purpose: Current running task + + Params: T -> Task block to repoint + + Returns: True if it is + --------------------------------------------------------------------------- */ +void TSK_RepointProc(TASK *T,void (*Func)(TASK *T)) +{ + /* If the current task is this task then just jump there else mark as + needing initing and -> start to new func */ + + if (T==CurrentTask) + TSK_JumpAndResetStack(Func); + else + { + T->fToInit=1; + T->Main=Func; + } +} + + + +/* --------------------------------------------------------------------------- + Function: TASK * TSK_GetCurrentTask(void); + + Purpose: Get the current executing task + + Returns: -> Current execiting task block + + --------------------------------------------------------------------------- */ +TASK * TSK_GetCurrentTask(void) +{ + return(CurrentTask); +} + +/* --------------------------------------------------------------------------- + Function: BOOL TSK_IsCurrentTask(TASK *T) + + Purpose: Is this task the currently executing one? + + Returns: TRUE or FALSE + + --------------------------------------------------------------------------- */ +BOOL TSK_IsCurrentTask(TASK *T) +{ + return(T==CurrentTask); +} + +/* --------------------------------------------------------------------------- + Function: TASK *TSK_Exist(TASK *T,U32 Id,U32 Mask) + + Purpose: Is this task the currently executing one? + + Params: T -> Calling task + + Returns: -> first task found + + --------------------------------------------------------------------------- */ +TASK *TSK_Exist(TASK *T,U32 Id,U32 Mask) +{ + TASK * ptrTask; + TASK * RetTask; + + ptrTask=ActiveTasks; + RetTask=NULL; + + while (ptrTask && !RetTask) + { + if ((ptrTask != T) && (ptrTask->Id&Mask)==Id) + RetTask=ptrTask; + else + ptrTask=ptrTask->Next; + } + + return(RetTask); +} + +/* --------------------------------------------------------------------------- + Function: void TSK_SetExecFilter(U32 Id,U32 Mask) + + Purpose: Set a filter for tasks that are executed + If ((Task.Id&Mask) == Id) then task is run + + Params: + U32 = Id; + Mask = Task class mask + + --------------------------------------------------------------------------- */ +void TSK_SetExecFilter(U32 Id,U32 Mask) +{ + ExecId=Id; + ExecMask=Mask; +} + + +/* --------------------------------------------------------------------------- + Function: void TSK_ClearExecFilter(void) + + Purpose: Clears the exec filter, everything is run + + --------------------------------------------------------------------------- */ +void TSK_ClearExecFilter(void) +{ + TSK_SetExecFilter(0,0); +} + + +/* --------------------------------------------------------------------------- + Function: int TSK_KillTasks(TASK * CallingT,U32 Id,U32 Mask) + + Purpose: Mass task killer. Whacks through task list looking for victims. + If (T->fKillable && (Task.Id&Mask) == Id) then kill it. + + Params: CallingT -> Calling task + U32 = Id; + Mask = Task class mask + + Returns: # of Tasks Killed + --------------------------------------------------------------------------- */ +int TSK_KillTasks(TASK * CallingT,U32 Id,U32 Mask) +{ + int TasksKilled; + TASK * T; + BOOL WasCurrentTaskKilled; + + TasksKilled=0; + WasCurrentTaskKilled=FALSE; + T=ActiveTasks; + + while (T) + { + TASK * NextT; + + NextT=T->Next; + + if (T != CallingT) + { + if (T->fKillable && (T->Id&Mask)==Id) + { + if (T==CurrentTask) + WasCurrentTaskKilled=TRUE; + else + LoTskKill(T); + + TasksKilled++; + } + } + + T=NextT; + } + + /* If Current task was killed then we go on to next task */ + + if (WasCurrentTaskKilled) + { + CurrentTask->fToDie=TRUE; + ReturnToSchedulerIfCurrentTask(CurrentTask); + } + + return(TasksKilled); +} + + +/* --------------------------------------------------------------------------- + Function: void TSK_IterateTasks(U32 Id,U32 Mask,void (*CallBack)(TASK *T)) + Purpose: Go through task list and do a callback for all tasks which match. + If (Task->Id&Mask)==Id then callback function is invoked. + Params: Id = Task Id to match + Mask = Task class mask + --------------------------------------------------------------------------- */ +void TSK_IterateTasks(U32 Id,U32 Mask,void (*CallBack)(TASK *T)) +{ + TASK * T; + + T=ActiveTasks; + + while (T) + { + TASK * NextT; + + NextT=T->Next; + + if ((T->Id&Mask)==Id) + CallBack(T); + + T=NextT; + } +} + + +/* --------------------------------------------------------------------------- + Function: void TSK_MakeTaskInactive(TASK *T) + Purpose: Make a task temporarily inactive. + Params: T -> Task to knock out + --------------------------------------------------------------------------- */ +void TSK_MakeTaskInactive(TASK *T) +{ + T->fActive=0; +} + + +/* --------------------------------------------------------------------------- + Function: void TSK_MakeTaskActive(TASK *T) + Purpose: Make a task active again. + Params: T -> Task to reactivate + --------------------------------------------------------------------------- */ +void TSK_MakeTaskActive(TASK *T) +{ + T->fActive=1; +} + + +/* --------------------------------------------------------------------------- + Function: void TSK_MakeTaskImmortal(TASK *T) + Purpose: Make a task impervious to mass killings. + Note that task can still be killed explicitly with TSK_Kill. + Params: T -> Task to protect + --------------------------------------------------------------------------- */ +void TSK_MakeTaskImmortal(TASK *T) +{ + T->fKillable=0; +} + + +/* --------------------------------------------------------------------------- + Function: void TSK_MakeTaskMortal(TASK *T) + Purpose: Make a task vulnerable to mass killings. + Params: T -> Task to expose + --------------------------------------------------------------------------- */ +void TSK_MakeTaskMortal(TASK *T) +{ + T->fKillable=1; +} + + +/* --------------------------------------------------------------------------- + Function: BOOL TSK_IsTaskActive(TASK *T) + Purpose: Check if a task is active + Params: T -> Task to eheck + Returns: 0=Inactive, 1=Active + --------------------------------------------------------------------------- */ +BOOL TSK_IsTaskActive(TASK *T) +{ + return (T->fActive); +} + + +/* --------------------------------------------------------------------------- + Function: BOOL TSK_IsTaskMortal(TASK *T) + Purpose: Check if a task is easy to kill. + Params: T -> Task to check + Returns: 0=Immortal, 1=Mortal + --------------------------------------------------------------------------- */ +BOOL TSK_IsTaskMortal(TASK *T) +{ + return (T->fKillable); +} + + +/* --------------------------------------------------------------------------- + Function: void DetachFromList(TASK **Head,TASK *ThisObj) + Purpose: Take an object from an obj list + Params: Head -> Head ptr of list + ThisObj -> Obj to add + --------------------------------------------------------------------------- */ +static void DetachFromList(TASK **Head,TASK *ThisObj) +{ + if (ThisObj->Prev) + ThisObj->Prev->Next=ThisObj->Next; + else + *Head=ThisObj->Next; + + if (ThisObj->Next) + ThisObj->Next->Prev=ThisObj->Prev; +} + + +/* --------------------------------------------------------------------------- + Function: void AddToList(TASK **Head,TASK *ThisObj) + Purpose: Add an object to a obj list + Params: Head -> Head ptr of list + ThisObj -> Obj to add + --------------------------------------------------------------------------- */ +static void AddToList(TASK **Head,TASK *ThisObj) +{ + ThisObj->Prev=NULL; + + if ((ThisObj->Next=*Head)) + ThisObj->Next->Prev=ThisObj; + + *Head=ThisObj; +} + +/* --------------------------------------------------------------------------- + Function: static void LoTskKill(TASK *T) + Purpose: Low level killing of task helper routine + Params: T -> Task to kill + --------------------------------------------------------------------------- */ +static void LoTskKill(TASK *T) +{ + BOOL GalRet; + + /* Take out of list of active tasks */ + + DetachFromList(&ActiveTasks,T); + + /* Dealloc the task block */ + GalRet=GAL_Free(T->hndTask); + TasksActive--; + + ASSERT(GalRet); +} + + +/* --------------------------------------------------------------------------- + Function: static void ExecuteTask(TASK *T) + Purpose: Low level task executor, protects from tasks that return + without a TSK_Kill + Params: T -> Task to execute + --------------------------------------------------------------------------- */ +static void ExecuteTask(TASK *T) +{ + T->Main(T); + DoEpi(T); + T->fToDie=TRUE; + ReturnToSchedulerIfCurrentTask(T); +} + + +/* --------------------------------------------------------------------------- + Epilogue / Prologue Function stuff + --------------------------------------------------------------------------- */ +DOTSK_CBACK TSK_SetDoTasksPrologue(DOTSK_CBACK Func) +{ + DOTSK_CBACK Old; + + Old=DoTasksPrologue; + DoTasksPrologue=Func; + return(Old); +} + +DOTSK_CBACK TSK_SetDoTasksEpilogue(DOTSK_CBACK Func) +{ + DOTSK_CBACK Old; + + Old=DoTasksEpilogue; + DoTasksEpilogue=Func; + return(Old); +} + + +TSK_CBACK TSK_SetTaskPrologue(TSK_CBACK Pro) +{ + TSK_CBACK Old; + + Old=ProFunc; + ProFunc=Pro; + return(Old); +} + +TSK_CBACK TSK_SetTaskEpilogue(TSK_CBACK Epi) +{ + TSK_CBACK Old; + + Old=EpiFunc; + EpiFunc=Epi; + return(Old); +} + +void TSK_SetEpiProFilter(U32 Id,U32 Mask) +{ + EpiProId=Id; + EpiProMask=Id; +} + +void TSK_ClearEpiProFilter(void) +{ + TSK_SetEpiProFilter(1,0); + TSK_SetTaskEpilogue(NULL); + TSK_SetTaskPrologue(NULL); +} + +/* --------------------------------------------------------------------------- + Function: void TSK_SetExtraStackProtection(BOOL OnOff) + Purpose: Say if we want the slow, memory heavy xtra stack protection + --------------------------------------------------------------------------- */ +void TSK_SetExtraStackProtection(BOOL OnOff) +{ + ExtraStackProtection=OnOff; +} + +/* --------------------------------------------------------------------------- + Function: TSK_CBACK TSK_SetStackFloodCallback(TSK_CBACK Func); + Purpose: This gets called if stack is detected as broken + --------------------------------------------------------------------------- */ +TSK_CBACK TSK_SetStackFloodCallback(TSK_CBACK Func) +{ + TSK_CBACK OldFunc; + + OldFunc=StackFloodCallback; + + StackFloodCallback=Func; + return(OldFunc); +} + +/* --------------------------------------------------------------------------- + Function: int TSK_SetExtraStackSize(int Size) + Purpose: Set the xtra stack size for safety + --------------------------------------------------------------------------- */ +int TSK_SetExtraStackSize(int Size) +{ + int OldSize=ExtraStackSizeLongs*sizeof(u32); + ExtraStackSizeLongs=Size/4; + return(OldSize); + +} + +void ExtraMarkStack(u32 * Stack,int SizeLongs) +{ + int f; + for (f=0;f + +/* --------------------------------------------------------------------------- + Glib Includes + ------------- */ +#include "gtypes.h" +#include "gal.h" + + +/* --------------------------------------------------------------------------- + Structure Definitions + --------------------- */ +typedef struct TASK +{ + struct TASK *Next; + struct TASK *Prev; + + U32 Id; + U32 SleepTime; + + U32 fToInit:1, /* Has this task been inited */ + fToDie:1, /* Does this task deserve to die! */ + fKillable:1, /* Can this task be killed */ + fActive:1, /* Is this task active or what */ + fXtraStack:1; + + void * Stack; + U32 StackSize; + + void * Data; + + jmp_buf TskEnv; + + void (*Main)(struct TASK *T); + + MHANDLE hndTask; + u16 XtraLongs; + u16 MaxStackSizeBytes; + +} TASK; + + +/* --------------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +GLIB_API BOOL TSK_OpenModule(U32 MemType); +GLIB_API void TSK_DoTasks(void); + +/* Task management + --------------- */ +GLIB_API TASK * TSK_AddTask(U32 Id,void (*Main)(TASK *T),int StackSize,int DataSize); +GLIB_API void TSK_RepointProc(TASK *T,void (*Func)(TASK *T)); +GLIB_API void TSK_Kill(TASK *T); +GLIB_API int TSK_KillTasks(TASK * CallingT,U32 Id,U32 Mask); + +GLIB_API void TSK_Sleep(int Frames); +GLIB_API void TSK_Die(void); + +GLIB_API void TSK_JumpAndResetStack(void (*RunFunc)(TASK *)); + +GLIB_API void TSK_ClearExecFilter(void); +GLIB_API void TSK_SetExecFilter(U32 Id,U32 Mask); + +GLIB_API void TSK_IterateTasks(U32 Id,U32 Mask,void (*CallBack)(TASK *T)); + +GLIB_API void TSK_MakeTaskInactive(TASK *T); +GLIB_API void TSK_MakeTaskActive(TASK *T); +GLIB_API void TSK_MakeTaskImmortal(TASK *T); +GLIB_API void TSK_MakeTaskMortal(TASK *T); + +/* Info and reporting + ------------------ */ +GLIB_API TASK * TSK_Exist(TASK *T,U32 Id,U32 Mask); +GLIB_API TASK * TSK_GetCurrentTask(void); +GLIB_API BOOL TSK_IsCurrentTask(TASK *T); +GLIB_API TASK * TSK_GetFirstActive(void); +GLIB_API BOOL TSK_IsStackCorrupted(TASK *T); + +GLIB_API BOOL TSK_IsTaskActive(TASK *T); +GLIB_API BOOL TSK_IsTaskMortal(TASK *T); + +/* Debug Stuff + ----------- */ +typedef void (*TSK_CBACK)(TASK *T); +typedef void (*DOTSK_CBACK)(void); + +GLIB_API DOTSK_CBACK TSK_SetDoTasksEpilogue(DOTSK_CBACK Func); +GLIB_API DOTSK_CBACK TSK_SetDoTasksPrologue(DOTSK_CBACK Func); + +GLIB_API TSK_CBACK TSK_SetTaskPrologue(TSK_CBACK Pro); +GLIB_API TSK_CBACK TSK_SetTaskEpilogue(TSK_CBACK Epi); +GLIB_API void TSK_SetEpiProFilter(U32 Id,U32 Mask); +GLIB_API void TSK_ClearEpiProFilter(void); + +GLIB_API void TSK_SetExtraStackProtection(BOOL OnOff); +GLIB_API TSK_CBACK TSK_SetStackFloodCallback(TSK_CBACK Func); +GLIB_API int TSK_SetExtraStackSize(int Size); + +#ifdef __cplusplus +}; +#endif + + +/* --------------------------------------------------------------------------- */ +#endif + +/* --------------------------------------------------------------------------- + ends */ diff --git a/Utils/Libs/GLib/tick.c b/Utils/Libs/GLib/tick.c new file mode 100644 index 000000000..6235a507e --- /dev/null +++ b/Utils/Libs/GLib/tick.c @@ -0,0 +1,82 @@ +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + File: TICK.C + + Notes: Game Frame Clock stuff + + Author: G Robert Liddon @ Croydon + Created: Monday 2nd October 1995 + + Copyright (C) 1994/1995 G Robert Liddon + All rights reserved. + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Glib Includes + ÄÄÄÄÄÄÄÄÄÄÄÄÄ */ + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Includes + ÄÄÄÄÄÄÄÄ */ +#include "tick.h" + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Vars + ÄÄÄÄ */ +U32 GazTick; + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Intialise the module + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void TICK_InitModule(void) +{ + TICK_Set(0); +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Set the current tick value + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +void TICK_Set(U32 Val) +{ + GazTick=Val; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Set the current tick value + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +U32 TICK_Get(void) +{ + return(GazTick); +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Update Tick + ÄÄÄÄÄÄÄÄÄÄÄ */ +void TICK_Update(void) +{ + GazTick++; +} + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Get the frames passed since last tick + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ */ +U32 TICK_GetAge(U32 OldTick) +{ + return(TICK_Get()-OldTick); +} + +const char * TICK_GetDateString(void) +{ + return(__DATE__); +} + +const char * TICK_GetTimeString(void) +{ + return(__TIME__); +} + + +/* ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + ends */ diff --git a/Utils/Libs/GLib/tick.h b/Utils/Libs/GLib/tick.h new file mode 100644 index 000000000..9fde92b80 --- /dev/null +++ b/Utils/Libs/GLib/tick.h @@ -0,0 +1,59 @@ +/* =========================================================================== + File: TICK.C + + Notes: Game Frame Clock stuff + + Author: G Robert Liddon @ Croydon + + Created: Monday 2nd October 1995 + + Copyright (C) 1995 - 1997 Gary Liddon + All rights reserved. + =========================================================================== */ + +#ifndef __TICK_H +#define __TICK_H + +/* --------------------------------------------------------------------------- + Glib Includes + ------------- */ +#include "gtypes.h" + +/* --------------------------------------------------------------------------- + Defines + ------- */ + +/* --------------------------------------------------------------------------- + Structures + ---------- */ + +/* --------------------------------------------------------------------------- + Global Routines + --------------- */ +#ifdef __cplusplus +extern "C" { +#endif + +GLIB_API void TICK_InitModule(void); +GLIB_API void TICK_Set(U32 Val); +GLIB_API U32 TICK_Get(void); +GLIB_API void TICK_Update(void); +GLIB_API U32 TICK_GetAge(U32 OldTick); +GLIB_API const char * TICK_GetTimeString(void); +GLIB_API const char * TICK_GetDateString(void); + +#ifdef __cplusplus +}; +#endif + + +/* --------------------------------------------------------------------------- + Global Vars + ----------- */ + +/* --------------------------------------------------------------------------- */ +#endif + +/* --------------------------------------------------------------------------- + ends */ + diff --git a/Utils/Libs/GLib/tquant.cpp b/Utils/Libs/GLib/tquant.cpp new file mode 100644 index 000000000..a1afbb285 --- /dev/null +++ b/Utils/Libs/GLib/tquant.cpp @@ -0,0 +1,467 @@ +/********************************************************************** + C Implementation of Wu's Color Quantizer (v. 2) + (see Graphics Gems vol. II, pp. 126-133) + +Author: Wu + Dept. of Computer Science + Univ. of Western Ontario + London, Ontario N6A 5B7 + wu@csd.uwo.ca + +Algorithm: Greedy orthogonal bipartition of RGB space for variance + minimization aided by inclusion-exclusion tricks. + For speed no nearest neighbor search is done. Slightly + better performance can be expected by more sophisticated + but more expensive versions. + +The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of +additional documentation and a cure to a previous bug. + +Free to distribute, comments and suggestions are appreciated. +**********************************************************************/ + +#include "Tquant.h" +#include "gobject.hpp" + + +#define MAXCOLOR 256 +#define RED 2 +#define GREEN 1 +#define BLUE 0 + +struct box { + int r0; /* min value, exclusive */ + int r1; /* max value, inclusive */ + int g0; + int g1; + int b0; + int b1; + int vol; +}; + +/* Histogram is in elements 1..HISTSIZE along each axis, + * element 0 is for base or marginal value + * NB: these must start out 0! + */ + +float gm2[33][33][33]; +long int wt[33][33][33], mr[33][33][33], mg[33][33][33], mb[33][33][33]; +unsigned char *Ir, *Ig, *Ib; +//int size; /*image size*/ +//int K; /*color look-up table size*/ +unsigned short int *Qadd; +void ClearTables(void); + + +void ClearTables(void) +{ + for (int x=0;x<33;x++) + for (int y=0;y<33;y++) + for (int z=0;z<33;z++) + { + gm2[x][y][z]=0; + wt[x][y][z]=0; + mr[x][y][z]=0; + mg[x][y][z]=0; + mb[x][y][z]=0; + } +} + + + +/* build 3-D color histogram of counts, r/g/b, c^2 */ +void Hist3d(long int* vwt, long int *vmr, long int *vmg, long int *vmb, float *m2, int size) +{ + register int ind, r, g, b; + int inr, ing, inb, table[256]; + register long int i; + + for(i=0; i<256; ++i) table[i]=i*i; + Qadd = (unsigned short int *)malloc(sizeof(short int)*size); + if (Qadd==NULL) + GObject::Error(ERM_OUTOFMEM); + + for(i=0; i>3)+1; + ing=(g>>3)+1; + inb=(b>>3)+1; + Qadd[i]=ind=(inr<<10)+(inr<<6)+inr+(ing<<5)+ing+inb; + /*[inr][ing][inb]*/ + ++vwt[ind]; + vmr[ind] += r; + vmg[ind] += g; + vmb[ind] += b; + m2[ind] += (float)(table[r]+table[g]+table[b]); + } +} + +/* At conclusion of the histogram step, we can interpret + * wt[r][g][b] = sum over voxel of P(c) + * mr[r][g][b] = sum over voxel of r*P(c) , similarly for mg, mb + * m2[r][g][b] = sum over voxel of c^2*P(c) + * Actually each of these should be divided by 'size' to give the usual + * interpretation of P() as ranging from 0 to 1, but we needn't do that here. + */ + +/* We now convert histogram into moments so that we can rapidly calculate + * the sums of the above quantities over any desired box. + */ + + /* compute cumulative moments. */ +void M3d(long int *vwt, long int *vmr, long int *vmg, long int *vmb, float *m2) +{ + register unsigned short int ind1, ind2; + register unsigned char i, r, g, b; + long int line, line_r, line_g, line_b; + long int area[33], area_r[33], area_g[33], area_b[33]; + float line2, area2[33]; + + for(r=1; r<=32; ++r) + { + for(i=0; i<=32; ++i) + { + area[i]=area_r[i]=area_g[i]=area_b[i]=0; + area2[i]=0.f; + } + for(g=1; g<=32; ++g) + { + line = line_r = line_g = line_b = 0; + line2 = 0.f; + for(b=1; b<=32; ++b) + { + ind1 = (r<<10) + (r<<6) + r + (g<<5) + g + b; /* [r][g][b] */ + line += vwt[ind1]; + line_r += vmr[ind1]; + line_g += vmg[ind1]; + line_b += vmb[ind1]; + line2 += m2[ind1]; + area[b] += line; + area_r[b] += line_r; + area_g[b] += line_g; + area_b[b] += line_b; + area2[b] += line2; + ind2 = ind1 - 1089; /* [r-1][g][b] */ + vwt[ind1] = vwt[ind2] + area[b]; + vmr[ind1] = vmr[ind2] + area_r[b]; + vmg[ind1] = vmg[ind2] + area_g[b]; + vmb[ind1] = vmb[ind2] + area_b[b]; + m2[ind1] = m2[ind2] + area2[b]; + } + } + } +} + + +/* Compute sum over a box of any given statistic */ +long int Vol(box *cube, long int *mmt) +{ + return( mmt[((cube->r1)*33*33)+((cube->g1)*33)+(cube->b1)] + -mmt[((cube->r1)*33*33)+((cube->g1)*33)+(cube->b0)] + -mmt[((cube->r1)*33*33)+((cube->g0)*33)+(cube->b1)] + +mmt[((cube->r1)*33*33)+((cube->g0)*33)+(cube->b0)] + -mmt[((cube->r0)*33*33)+((cube->g1)*33)+(cube->b1)] + +mmt[((cube->r0)*33*33)+((cube->g1)*33)+(cube->b0)] + +mmt[((cube->r0)*33*33)+((cube->g0)*33)+(cube->b1)] + -mmt[((cube->r0)*33*33)+((cube->g0)*33)+(cube->b0)] ); +} + +/* The next two routines allow a slightly more efficient calculation + * of Vol() for a proposed subbox of a given box. The sum of Top() + * and Bottom() is the Vol() of a subbox split in the given direction + * and with the specified new upper bound. + */ + +/* Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1 */ +/* (depending on dir) */ +long int Bottom(box *cube, unsigned char dir, long int *mmt) +{ + switch(dir){ + case RED: + return( -mmt[((cube->r0)*33*33)+((cube->g1)*33)+(cube->b1)] + +mmt[((cube->r0)*33*33)+((cube->g1)*33)+(cube->b0)] + +mmt[((cube->r0)*33*33)+((cube->g0)*33)+(cube->b1)] + -mmt[((cube->r0)*33*33)+((cube->g0)*33)+(cube->b0)] ); + break; + case GREEN: + return( -mmt[((cube->r1)*33*33)+((cube->g0)*33)+(cube->b1)] + +mmt[((cube->r1)*33*33)+((cube->g0)*33)+(cube->b0)] + +mmt[((cube->r0)*33*33)+((cube->g0)*33)+(cube->b1)] + -mmt[((cube->r0)*33*33)+((cube->g0)*33)+(cube->b0)] ); + break; + case BLUE: + return( -mmt[((cube->r1)*33*33)+((cube->g1)*33)+(cube->b0)] + +mmt[((cube->r1)*33*33)+((cube->g0)*33)+(cube->b0)] + +mmt[((cube->r0)*33*33)+((cube->g1)*33)+(cube->b0)] + -mmt[((cube->r0)*33*33)+((cube->g0)*33)+(cube->b0)] ); + break; + default: + return NULL; + } +} + + +/* Compute remainder of Vol(cube, mmt), substituting pos for */ +/* r1, g1, or b1 (depending on dir) */ +long int Top(box *cube, unsigned char dir, int pos, long int *mmt) +{ + switch(dir){ + case RED: + return( mmt[(pos*33*33)+(cube->g1*33)+cube->b1] + -mmt[(pos*33*33)+(cube->g1*33)+cube->b0] + -mmt[(pos*33*33)+(cube->g0*33)+cube->b1] + +mmt[(pos*33*33)+(cube->g0*33)+cube->b0] ); + break; + case GREEN: + return( mmt[((cube->r1)*33*33)+(pos*33)+cube->b1] + -mmt[((cube->r1)*33*33)+(pos*33)+cube->b0] + -mmt[((cube->r0)*33*33)+(pos*33)+cube->b1] + +mmt[((cube->r0)*33*33)+(pos*33)+cube->b0] ); + break; + case BLUE: + return( mmt[(cube->r1*33*33)+(cube->g1*33)+pos] + -mmt[(cube->r1*33*33)+(cube->g0*33)+pos] + -mmt[(cube->r0*33*33)+(cube->g1*33)+pos] + +mmt[(cube->r0*33*33)+(cube->g0*33)+pos] ); + break; + default: + return NULL; + } +} + + +/* Compute the weighted variance of a box */ +/* NB: as with the raw statistics, this is really the variance * size */ +float Var(box *cube) +{ + float dr, dg, db, xx; + + dr = (float)Vol(cube, &mr[0][0][0]); + dg = (float)Vol(cube, &mg[0][0][0]); + db = (float)Vol(cube, &mb[0][0][0]); + xx = gm2[cube->r1][cube->g1][cube->b1] + -gm2[cube->r1][cube->g1][cube->b0] + -gm2[cube->r1][cube->g0][cube->b1] + +gm2[cube->r1][cube->g0][cube->b0] + -gm2[cube->r0][cube->g1][cube->b1] + +gm2[cube->r0][cube->g1][cube->b0] + +gm2[cube->r0][cube->g0][cube->b1] + -gm2[cube->r0][cube->g0][cube->b0]; + + return( xx - (dr*dr+dg*dg+db*db)/(float)Vol(cube,&wt[0][0][0]) ); +} + +/* We want to minimize the sum of the variances of two subboxes. + * The sum(c^2) terms can be ignored since their sum over both subboxes + * is the same (the sum for the whole box) no matter where we split. + * The remaining terms have a minus sign in the variance formula, + * so we drop the minus sign and MAXIMIZE the sum of the two terms. + */ + +float Maximize(box *cube, unsigned char dir, int first, int last, int *cut,long int whole_r, long int whole_g, long int whole_b, long int whole_w) +{ + register long int half_r, half_g, half_b, half_w; + long int base_r, base_g, base_b, base_w; + register int i; + register float temp, max; + + base_r = Bottom(cube, dir, &mr[0][0][0]); + base_g = Bottom(cube, dir, &mg[0][0][0]); + base_b = Bottom(cube, dir, &mb[0][0][0]); + base_w = Bottom(cube, dir, &wt[0][0][0]); + max = 0.0; + *cut = -1; + for(i=first; i max) {max=temp; *cut=i;} + } + return(max); +} + +int Cut(box *set1, box *set2) +{ + unsigned char dir; + int cutr, cutg, cutb; + float maxr, maxg, maxb; + long int whole_r, whole_g, whole_b, whole_w; + + whole_r = Vol(set1, &mr[0][0][0]); + whole_g = Vol(set1, &mg[0][0][0]); + whole_b = Vol(set1, &mb[0][0][0]); + whole_w = Vol(set1, &wt[0][0][0]); + + maxr = Maximize(set1, RED, set1->r0+1, set1->r1, &cutr, + whole_r, whole_g, whole_b, whole_w); + maxg = Maximize(set1, GREEN, set1->g0+1, set1->g1, &cutg, + whole_r, whole_g, whole_b, whole_w); + maxb = Maximize(set1, BLUE, set1->b0+1, set1->b1, &cutb, + whole_r, whole_g, whole_b, whole_w); + + if( (maxr>=maxg)&&(maxr>=maxb) ) + { + dir = RED; + if (cutr < 0) return 0; /* can't split the box */ + }else + if( (maxg>=maxr)&&(maxg>=maxb) ) + dir = GREEN; + else + dir = BLUE; + + set2->r1 = set1->r1; + set2->g1 = set1->g1; + set2->b1 = set1->b1; + + switch (dir){ + case RED: + set2->r0 = set1->r1 = cutr; + set2->g0 = set1->g0; + set2->b0 = set1->b0; + break; + case GREEN: + set2->g0 = set1->g1 = cutg; + set2->r0 = set1->r0; + set2->b0 = set1->b0; + break; + case BLUE: + set2->b0 = set1->b1 = cutb; + set2->r0 = set1->r0; + set2->g0 = set1->g0; + break; + } + set1->vol=(set1->r1-set1->r0)*(set1->g1-set1->g0)*(set1->b1-set1->b0); + set2->vol=(set2->r1-set2->r0)*(set2->g1-set2->g0)*(set2->b1-set2->b0); + return 1; +} + + +void Mark(box *cube, int label, unsigned char *tag) +{ + register int r, g, b; + + for(r=cube->r0+1; r<=cube->r1; ++r) + for(g=cube->g0+1; g<=cube->g1; ++g) + for(b=cube->b0+1; b<=cube->b1; ++b) + tag[(r<<10) + (r<<6) + r + (g<<5) + g + b] = label; +} + + + +void tquant(BYTE* Src, BYTE* Dst, BYTE* Pal, int K, int size) +{ + struct box cube[MAXCOLOR]; + unsigned char *tag; + unsigned char lut_r[MAXCOLOR], lut_g[MAXCOLOR], lut_b[MAXCOLOR]; + int next; + register long int i, weight; + register int k; + float vv[MAXCOLOR], temp; + + ClearTables(); + + /* input R,G,B components into Ir, Ig, Ib; + set size to width*height */ + +// screen_debug_msg("Reducing to %d cols",K); + + + Ir=(BYTE*)malloc(size); + Ig=(BYTE*)malloc(size); + Ib=(BYTE*)malloc(size); + + /* Split in data into RGB buffers */ + for(i=0;i1) ? Var(&cube[next]) : 0.f; + vv[i] = (cube[i].vol>1) ? Var(&cube[i]) : 0.f; + } else { + vv[next] = 0.0; /* don't try to split this box again */ + i--; /* didn't create box i */ + } + next = 0; temp = vv[0]; + for(k=1; k<=i; ++k) + if (vv[k] > temp) { + temp = vv[k]; next = k; + } + if (temp <= 0.0) { + K = i+1; + break; + } + } + + /* the space for array gm2 can be freed now */ + + tag = (unsigned char *)malloc(33*33*33); + if (tag==NULL) + GObject::Error(ERM_OUTOFMEM); + + for(k=0; k