/* @source ajfile ************************************************************* ** ** AJAX file routines ** ** @author Copyright (C) 1999 Peter Rice ** @version $Revision: 1.208 $ ** @modified May 14 Jon Ison Added ajFileExtnTrim & ajFileDirExtnTrim ** @modified $Date: 2013/01/31 13:25:12 $ by $Author: rice $ ** @@ ** ** This library is free software; you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public ** License as published by the Free Software Foundation; either ** version 2.1 of the License, or (at your option) any later version. ** ** This library 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 ** Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public ** License along with this library; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ** MA 02110-1301, USA. ** ******************************************************************************/ /* ========================================================================= */ /* ============================= include files ============================= */ /* ========================================================================= */ #include "ajlib.h" #include "ajfile.h" #include "ajsys.h" #include "ajreg.h" #include "ajnam.h" #include "ajhttp.h" #include "ajutil.h" #include "ajmath.h" #include "ajarr.h" #include "ajfileio.h" #include #include #include #include #include #include /*#include */ #ifndef WIN32 #include #else /* WIN32 */ #include "win32.h" #include "dirent_w32.h" #include #endif /* !WIN32 */ #ifndef WIN32 #include #endif /* !WIN32 */ #ifndef WIN32 #include #endif /* !WIN32 */ /* ========================================================================= */ /* =============================== constants =============================== */ /* ========================================================================= */ #ifdef WIN32 #define PATH_MAX _MAX_PATH #define getcwd _getcwd #define fileno _fileno #endif /* WIN32 */ #define FILERECURSLV 20 #ifdef __CYGWIN__ #define fopen(a,b) ajSysFuncFopen(a,b) #endif /* __CYGWIN__ */ /* ========================================================================= */ /* =========================== global variables ============================ */ /* ========================================================================= */ /* ========================================================================= */ /* ============================= private data ============================== */ /* ========================================================================= */ /* ========================================================================= */ /* =========================== private constants =========================== */ /* ========================================================================= */ /* ========================================================================= */ /* =========================== private variables =========================== */ /* ========================================================================= */ static ajuint fileBuffSize = 2049; static AjBool fileUsedStdin = AJFALSE; static AjBool fileUsedStdout = AJFALSE; static AjBool fileUsedStderr = AJFALSE; static ajint fileHandle = 0; static ajint fileOpenCnt = 0; static ajint fileOpenMax = 0; static ajint fileCloseCnt = 0; static ajint fileOpenTot = 0; static AjPStr fileNameFix = NULL; static AjPStr fileNameStrTmp = NULL; static AjPStr fileNameTmp = NULL; static AjPStr fileDirfixTmp = NULL; static AjPStr fileCwd = NULL; static AjPStr fileTmpStr = NULL; static AjPStr fileTempFilename = NULL; static AjPStr fileDirectory = NULL; static AjPRegexp fileUserExp = NULL; static AjPRegexp fileWildExp = NULL; static AjPRegexp fileEntryExp = NULL; static AjPRegexp fileFileExp = NULL; static AjPRegexp fileRestExp = NULL; static AjPRegexp fileDirExp = NULL; static AjPRegexp fileFilenameExp = NULL; /* ========================================================================= */ /* =========================== private functions =========================== */ /* ========================================================================= */ static void fileBuffLineDel(AjPFilebuff thys); static AjBool fileBuffLineNext(AjPFilebuff thys); static void fileClose(AjPFile thys); static void fileListRecurs(const AjPStr file, AjPList list, ajint *recurs); static DIR* fileOpenDir(AjPStr *dir); static void filebuffFreeClear(AjPFilebuff buff); static void filePrintname(const AjPStr name, AjPStr* Pprintname); /* ========================================================================= */ /* ======================= All functions by section ======================== */ /* ========================================================================= */ /* @filesection ajfile ******************************************************** ** ** @nam1rule aj Function belongs to the AJAX library. ** ******************************************************************************/ /* @datasection [AjPDir] Directory ******************************************** ** ** Function is for manipulating directories and returns or takes at least one ** AjPDir argument. ** ** @nam2rule Dir ** ******************************************************************************/ /* @section Directory constructors ******************************************** ** ** All constructors return a directory object by pointer. ** It is the responsibility of the user to first destroy any previous ** directory pointer. The target pointer ** does not need to be initialised to NULL, but it is good programming practice ** to do so anyway. ** ** To replace or reuse an existing file, see instead ** the file assignments and file modifiers functions. ** ** The range of constructors is provided to allow flexibility in how ** applications can open files to read various kinds of data. ** ** @nam3rule New Constructor ** @nam4rule Path Input file directory path ** @suffix Pre Filename prefix(es) specified ** @suffix Ext Filename extension(s) specified ** ** @argrule Path path [const AjPStr] Path (full or relative) to directory ** @argrule Pre prefix [const AjPStr] List of prefix wildcards for filename ** prefixes to be used ** @argrule Ext ext [const AjPStr] List of prefix wildcards for filename ** extensions to be used ** ** @valrule * [AjPDir] Directory object ** ** @fcategory new ** ** @fdata [AjPDir] ** ******************************************************************************/ /* @func ajDirNewPath ********************************************************* ** ** Creates a new directory object. ** ** @param [r] path [const AjPStr] Directory name ** @return [AjPDir] New directory object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPDir ajDirNewPath(const AjPStr path) { AjPDir thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); thys->Prefix = NULL; thys->Extension = NULL; return thys; } /* @func ajDirNewPathExt ****************************************************** ** ** Creates a new directory object. ** ** @param [r] path [const AjPStr] Directory name ** @param [r] ext [const AjPStr] File extension ** @return [AjPDir] New directory object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPDir ajDirNewPathExt(const AjPStr path, const AjPStr ext) { AjPDir thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); if (ajStrMatchC(ext, " ")) ajStrAssignC(&thys->Extension, ""); else if (ajStrGetLen(ext)) ajStrAssignS(&thys->Extension, ext); return thys; } /* @func ajDirNewPathPreExt *************************************************** ** ** Creates a new directory object. ** ** @param [r] path [const AjPStr] Directory name ** @param [r] prefix [const AjPStr] Filename prefix ** @param [r] ext [const AjPStr] Filename extension ** @return [AjPDir] New directory object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPDir ajDirNewPathPreExt(const AjPStr path, const AjPStr prefix, const AjPStr ext) { AjPDir thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); if (ajStrMatchC(prefix, " ")) ajStrAssignC(&thys->Prefix, ""); else if (ajStrGetLen(prefix)) ajStrAssignS(&thys->Prefix, prefix); if (ajStrMatchC(ext, " ")) ajStrAssignC(&thys->Extension, ""); else if (ajStrGetLen(ext)) ajStrAssignS(&thys->Extension, ext); return thys; } /* @section Directory destructors ********************************************* ** ** Destruction is achieved by deleting the object. ** ** @nam3rule Del Destructor ** ** @argrule Del Pdir [AjPDir*] Directory to be deleted ** ** @valrule * [void] ** ** @fcategory delete ** ** @fdata [AjPDir] ** ******************************************************************************/ /* @func ajDirDel ************************************************************* ** ** Close and free a directory object. ** ** @param [d] Pdir [AjPDir*] Directory object. ** @return [void] ** ** @release 2.9.0 ** @@ ******************************************************************************/ void ajDirDel(AjPDir* Pdir) { AjPDir thys; thys = *Pdir; if(!thys) return; ajStrDel(&thys->Name); ajStrDel(&thys->Printname); ajStrDel(&thys->Prefix); ajStrDel(&thys->Extension); AJFREE(*Pdir); return; } /* @section Directory element retrieval *************************************** ** ** Returns attributes of a directory ** ** @nam3rule Get Return attribute value ** @nam4rule Ext Return file extension ** @nam4rule Prefix Return filename prefix ** @nam4rule Path Return directory path ** @nam4rule Printpath Return directory printable path ** ** @argrule Get thys [const AjPDir] Directory ** ** @valrule * [const AjPStr] String value ** ** @fcategory use ** ** @fdata [AjPDir] ** ******************************************************************************/ /* @func ajDirGetExt ********************************************************** ** ** Returns the extension(s) of a directory object ** ** @param [r] thys [const AjPDir] Directory object. ** @return [const AjPStr] Directory name ** ** @release 6.0.0 ** @@ ******************************************************************************/ const AjPStr ajDirGetExt(const AjPDir thys) { if (!thys) return NULL; return thys->Extension; } /* @func ajDirGetPath ********************************************************* ** ** Returns the full path of a directory object ** ** @param [r] thys [const AjPDir] Directory object. ** @return [const AjPStr] Directory name ** ** @release 6.0.0 ** @@ ******************************************************************************/ const AjPStr ajDirGetPath(const AjPDir thys) { if (!thys) return NULL; return thys->Name; } /* @func ajDirGetPrefix ******************************************************* ** ** Returns the filename prefix(es) of a directory object ** ** @param [r] thys [const AjPDir] Directory object. ** @return [const AjPStr] Directory name ** ** @release 6.0.0 ** @@ ******************************************************************************/ const AjPStr ajDirGetPrefix(const AjPDir thys) { if (!thys) return NULL; return thys->Prefix; } /* @func ajDirGetPrintpath **************************************************** ** ** Returns the full printable path of a directory object ** ** @param [r] thys [const AjPDir] Directory object. ** @return [const AjPStr] Directory printable name ** ** @release 6.4.0 ** @@ ******************************************************************************/ const AjPStr ajDirGetPrintpath(const AjPDir thys) { if (!thys) return NULL; return thys->Printname; } /* @datasection [AjPDirout] Output directory ********************************** ** ** Function is for manipulating output directories and returns or ** takes at least one AjPDirout argument. ** ** @nam2rule Dirout ** ******************************************************************************/ /* @section Output directory constructors ************************************* ** ** All constructors return a directory object by pointer. ** It is the responsibility of the user to first destroy any previous ** directory pointer. The target pointer ** does not need to be initialised to NULL, but it is good programming practice ** to do so anyway. ** ** To replace or reuse an existing file, see instead ** the {File Assignments} and {File Modifiers} functions. ** ** The range of constructors is provided to allow flexibility in how ** applications can open files to read various kinds of data. ** ** @nam3rule New Constructor ** @nam4rule Path Output file directory path ** @suffix Pre Filename prefix(es) specified ** @suffix Ext Filename extension(s) specified ** ** @argrule Path path [const AjPStr] Path (full or relative) to directory ** @argrule Pre prefix [const AjPStr] List of prefix wildcards for filename ** prefixes to be used ** @argrule Ext ext [const AjPStr] List of prefix wildcards for filename ** extensions to be used ** ** @valrule * [AjPDirout] Output directory object ** ** @fcategory new ** ** @fdata [AjPDirout] ** ******************************************************************************/ /* @func ajDiroutNewPath ****************************************************** ** ** Creates a new directory output object. ** ** @param [r] path [const AjPStr] Directory name ** @return [AjPDirout] New directory object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPDirout ajDiroutNewPath(const AjPStr path) { AjPDirout thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); thys->Extension = NULL; return thys; } /* @func ajDiroutNewPathExt *************************************************** ** ** Creates a new directory output object. ** ** @param [r] path [const AjPStr] Directory name ** @param [r] ext [const AjPStr] File extension ** @return [AjPDirout] New directory object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPDirout ajDiroutNewPathExt(const AjPStr path, const AjPStr ext) { AjPDirout thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); if (ajStrGetLen(ext)) ajStrAssignS(&thys->Extension, ext); return thys; } /* @section Output directory destructors ************************************** ** ** Destruction is achieved by deleting the object. ** ** @nam3rule Del Destructor ** ** @argrule Del Pdir [AjPDirout*] Directory to be deleted ** ** @valrule * [void] ** ** @fcategory delete ** ** @fdata [AjPDirout] ** ******************************************************************************/ /* @func ajDiroutDel ********************************************************** ** ** Close and free a directory object. ** ** @param [d] Pdir [AjPDirout*] Directory object. ** @return [void] ** ** @release 2.9.0 ** @@ ******************************************************************************/ void ajDiroutDel(AjPDirout* Pdir) { AjPDirout thys; thys = *Pdir; if(!thys) return; ajStrDel(&thys->Name); ajStrDel(&thys->Printname); ajStrDel(&thys->Extension); AJFREE(*Pdir); return; } /* @section Output directory element retrieval ******************************** ** ** Returns attributes of a directory ** ** @nam3rule Get Return attribute value ** @nam4rule Ext Return file extension ** @nam4rule Path Return directory path ** @nam4rule Printpath Return directory printable path ** ** @argrule Get thys [const AjPDirout] Directory ** ** @valrule * [const AjPStr] String value ** ** @fdata [AjPDirout] ** @fcategory use ** ******************************************************************************/ /* @func ajDiroutGetExt ******************************************************* ** ** Returns the extension of an output directory object ** ** @param [r] thys [const AjPDirout] Directory object. ** @return [const AjPStr] Directory name ** ** @release 6.0.0 ** @@ ******************************************************************************/ const AjPStr ajDiroutGetExt(const AjPDirout thys) { if (!thys) return NULL; return thys->Extension; } /* @func ajDiroutGetPath ****************************************************** ** ** Returns the name of an output directory object ** ** @param [r] thys [const AjPDirout] Directory object. ** @return [const AjPStr] Directory name ** ** @release 6.0.0 ** @@ ******************************************************************************/ const AjPStr ajDiroutGetPath(const AjPDirout thys) { if (!thys) return NULL; return thys->Name; } /* @func ajDiroutGetPrintpath ************************************************* ** ** Returns the printable name of an output directory object ** ** @param [r] thys [const AjPDirout] Directory object. ** @return [const AjPStr] Directory printable name ** ** @release 6.4.0 ** @@ ******************************************************************************/ const AjPStr ajDiroutGetPrintpath(const AjPDirout thys) { if (!thys) return NULL; return thys->Printname; } /* @section Output directory element tests ************************************ ** ** Modifies attributes of a directory ** ** @nam3rule Created tests whether directory is newly created ** @nam3rule Exists tests whether directory exists already ** ** @argrule * thys [AjPDirout] Directory ** ** @valrule * [AjBool] True on success ** ** @fcategory use ** ** @fdata [AjPDirout] ** ******************************************************************************/ /* @func ajDiroutCreated ****************************************************** ** ** Tests if an output directory was created when it did not already exist ** ** @param [u] thys [AjPDirout] Directory name ** @return [AjBool] True if a newly cteated directory ** ** @release 6.5.0 ** @@ ******************************************************************************/ AjBool ajDiroutCreated(AjPDirout thys) { if(!thys) return ajFalse; return thys->Created; } /* @func ajDiroutExists ******************************************************* ** ** Tests a directory output object is for an existing directory ** ** @param [u] thys [AjPDirout] Directory name ** @return [AjBool] True on success. ** ** @release 6.1.0 ** @@ ******************************************************************************/ AjBool ajDiroutExists(AjPDirout thys) { #ifdef WIN32 if(ajStrMatchC(thys->Name, ".")) { ajStrAssignC(&thys->Name, ""); ajStrAssignC(&thys->Printname, ""); return ajTrue; } #endif /* WIN32 */ if(ajStrGetCharLast(thys->Name) != SLASH_CHAR) { ajStrAppendC(&thys->Name, SLASH_STRING); filePrintname(thys->Name, &thys->Printname); } if(!ajFilenameExistsDir(thys->Name)) return ajFalse; return ajTrue; } /* @section Output directory element modifiers ******************************** ** ** Modifies attributes of a directory ** ** @nam3rule Open Open the directory, creating if needed ** ** @argrule * thys [AjPDirout] Directory ** ** @valrule * [AjBool] True on success ** ** @fcategory modify ** ** @fdata [AjPDirout] ** ******************************************************************************/ /* @func ajDiroutOpen ********************************************************* ** ** Opens a directory output object, creating it if it does not already exist ** ** @param [u] thys [AjPDirout] Directory name ** @return [AjBool] True on success. ** ** @release 6.1.0 ** @@ ******************************************************************************/ AjBool ajDiroutOpen(AjPDirout thys) { #ifdef WIN32 if(ajStrMatchC(thys->Name, ".")) { ajStrAssignC(&thys->Name, ""); ajStrAssignC(&thys->Printname, ""); return ajTrue; } #endif /* WIN32 */ if(ajStrGetCharLast(thys->Name) != SLASH_CHAR) { ajStrAppendC(&thys->Name, SLASH_STRING); filePrintname(thys->Name, &thys->Printname); } if(!ajFilenameExists(thys->Name)) { ajSysCommandMakedirS(thys->Name); thys->Created = ajTrue; } if(!ajFilenameExistsDir(thys->Name)) return ajFalse; return ajTrue; } /* @datasection [AjPFile] File object ***************************************** ** ** Function is for manipulating buffered files and returns or takes at least ** one AjSFileBuff argument. ** ** Function is for manipulating file and file-related objects and usually ** processes an AjSOutfile, AjSDir, AjSFileBuff, AjSFileBuffList or AjSFile ** object. ** ** @nam2rule File ******************************************************************************/ /* @section File constructors ************************************************* ** ** All constructors return a new open file by pointer. It is the responsibility ** of the user to first destroy any previous file pointer. The target pointer ** does not need to be initialised to NULL, but it is good programming practice ** to do so anyway. ** ** The range of constructors is provided to allow flexibility in how ** applications can open files to read and write various kinds of data. ** ** @nam3rule New Constructor ** @nam4rule From Create from an already open file ** @nam4rule In Input file ** @nam4rule Listin List of one or more input files ** @nam4rule Out Output file created or rewritten ** @nam4rule Outappend Output file appended to existing content ** @nam5rule Block File opened for block read with internal system functions ** @nam5rule FromCfile C FILE* structure used to create file object ** @nam5rule Pipe Read from piped output of command ** @nam5rule ListinList List of files specified ** @nam5rule InPath Input directory path specified ** @nam5rule OutPath Output directory path specified ** @nam5rule Dir Input directory specified ** @nam6rule ListinNameDir Input directory specified ** @nam6rule OutNameDir Output directory specified ** ** @suffix Name Existing filename specified ** @suffix C Filename as C character string ** @suffix S Filename as string ** @suffix Pre Filename prefix(es) specified ** @suffix Ext Filename extension(s) specified ** @suffix Dir Input directory specified ** @suffix Path Input directory path specified ** @suffix Wild Wildcard filename ** @suffix Exclude Filename exclusion wildcard(s) ** ** ** ** @argrule C name [const char*] Filename ** @argrule S name [const AjPStr] Filename ** ** @argrule Block blocksize [ajuint] Block size for buffered system reads ** @argrule Cfile file [FILE*] C file pointer ** @argrule Pipe command [const AjPStr] Commandline ** @argrule ListinList list [AjPList] List of filenames as strings ** @argrule ListinNameDir dir [const AjPDir] Input directory ** @argrule ListinDir dir [const AjPDir] Input directory ** @argrule InNameDir dir [const AjPDir] Input directory ** @argrule OutNameDir dir [const AjPDirout] Output directory ** @argrule Path path [const AjPStr] Input directory path ** @argrule OutPath path [const AjPStr] Output directory path ** @argrule Pre prefix [const AjPStr] List of prefix wildcards for filename ** prefixes to be used ** @argrule Wild wildname [const AjPStr] Wildcard filename ** @argrule Exclude exclude [const AjPStr] Filename exclusion wildcard(s) ** @argrule Ext ext [const AjPStr] List of wildcards for filename ** extensions to be used ** ** @valrule * [AjPFile] New file object ** @fcategory new ** ** @fdata [AjPFile] ** ******************************************************************************/ /* @func ajFileNewFromCfile *************************************************** ** ** Creates a new file object from an open C file. ** ** The file is for input, output, or append - depending on how the C FILE ** pointer was opened. ** ** @param [u] file [FILE*] C file. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewFromCfile(FILE* file) { AjPFile thys; if(!file) ajFatal("Trying to create an AJAX file from a bad C RTL FILE*"); AJNEW0(thys); thys->fp = file; thys->Handle = ++fileHandle; if(file == stdout) thys->Name = ajStrNewC("stdout"); else if(file == stderr) thys->Name = ajStrNewC("stderr"); else if(file == stdin) thys->Name = ajStrNewC("stdin"); else thys->Name = ajStrNew(); ajStrAssignS(&thys->Printname, thys->Name); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; if(file == stdin) fileUsedStdin = ajTrue; else if(file == stdout) fileUsedStdout = ajTrue; else if(file == stderr) fileUsedStderr = ajTrue; ajDebug("Created file from C FILE %p\n", file); return thys; } /* @funcstatic fileNew ******************************************************** ** ** Creates a new file object. ** ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ static AjPFile fileNew(void) { AjPFile thys; AJNEW0(thys); thys->fp = NULL; thys->Handle = 0; thys->Name = ajStrNew(); thys->Printname = ajStrNew(); thys->Buff = ajStrNewRes(fileBuffSize); thys->Buffsize = fileBuffSize; thys->List = NULL; thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } /* @func ajFileNewInBlockS **************************************************** ** ** Creates a new file object to read a named file using blocked fread calls. ** ** If the filename ends with a pipe character then a pipe is opened ** using ajFileNewInPipe. ** ** @param [r] name [const AjPStr] File name. ** @param [r] blocksize [ajuint] Block size ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewInBlockS(const AjPStr name, ajuint blocksize) { AjPFile ret; ret = ajFileNewInNameS(name); ret->Blocksize = blocksize; if(blocksize) { ret->Workbuffer = ajCharNewRes(blocksize); /*setvbuf(ret->fp, ret->Workbuffer, _IOFBF, blocksize);*/ ret->Readblock = ajCharNewRes(blocksize+1); ret->Blockpos = 0; ret->Blocklen = 0; ret->Blocksize = blocksize; } ajDebug("ajFileNewInBlock '%S' blocksize:%u\n", name, blocksize); return ret; } /* @func ajFileNewInNameC ***************************************************** ** ** Creates a new file object to read a named file. ** ** If the filename begins with a pipe character then a pipe is opened ** using ajFileNewInPipe. ** ** @param [r] name [const char*] File name. ** @return [AjPFile] New file object. ** ** @release 6.2.0 ** @@ ******************************************************************************/ AjPFile ajFileNewInNameC(const char *name) { ajStrAssignC(&fileNameStrTmp, name); return ajFileNewInNameS(fileNameStrTmp); } /* @func ajFileNewInNameS ***************************************************** ** ** Creates a new file object to read a named file. ** ** If the filename ends with a pipe character then a pipe is opened ** using ajFileNewInPipe. ** ** @param [r] name [const AjPStr] File name. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewInNameS(const AjPStr name) { AjPFile thys = NULL; AjPStr userstr = NULL; AjPStr reststr = NULL; AjPStr dirname = NULL; AjPStr wildname = NULL; AjPFile ptr; char *hdir = NULL; ajDebug("ajFileNewInNameS '%S'\n", name); if(ajStrMatchC(name, "stdin")) { thys = ajFileNewFromCfile(stdin); ajStrAssignC(&thys->Name, "stdin"); ajStrAssignS(&thys->Printname, thys->Name); return thys; } ajStrAssignS(&fileNameTmp, name); if(ajStrGetCharLast(name) == '|') /* pipe character at end */ return ajFileNewInPipe(name); if(ajStrGetCharFirst(fileNameTmp) == '~') { ajDebug("starts with '~'\n"); if(!fileUserExp) fileUserExp = ajRegCompC("^~([^/\\\\]*)"); ajRegExec(fileUserExp, fileNameTmp); ajRegSubI(fileUserExp, 1, &userstr); ajRegPost(fileUserExp, &reststr); ajDebug(" user: '%S' rest: '%S'\n", userstr, reststr); if(ajStrGetLen(userstr)) { /* username specified */ hdir = ajSysGetHomedirFromName(ajStrGetPtr(userstr)); if(!hdir) { ajStrDel(&userstr); ajStrDelStatic(&fileNameTmp); ajStrDel(&reststr); return NULL; } ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr); AJFREE(hdir); ajDebug("use getpwnam: '%S'\n", fileNameTmp); } else { /* just ~/ */ hdir = ajSysGetHomedir(); if(hdir) { ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr); AJFREE(hdir); } else ajFmtPrintS(&fileNameTmp,"%S",reststr); ajDebug("use HOME: '%S'\n", fileNameTmp); } } ajStrDel(&userstr); ajStrDel(&reststr); if(!fileWildExp) fileWildExp = ajRegCompC("(.*/)?([^/]*[*?][^/]*)$"); if(ajRegExec(fileWildExp, fileNameTmp)) { /* wildcard file names */ ajRegSubI(fileWildExp, 1, &dirname); ajRegSubI(fileWildExp, 2, &wildname); ajDebug("wild dir '%S' files '%S'\n", dirname, wildname); ptr = ajFileNewListinPathWild(dirname, wildname); ajStrDelStatic(&fileNameTmp); ajStrDel(&dirname); ajStrDel(&wildname); return ptr; } AJNEW0(thys); ajStrAssignS(&thys->Name, fileNameTmp); ajStrDelStatic(&fileNameTmp); ajNamResolve(&thys->Name); #ifdef WIN32 if(ajStrMatchC(thys->Name, "/dev/null")) thys->fp = fopen("NUL", "rb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(thys->Name), "rb"); if(!thys->fp) { ajStrDel(&thys->Name); AJFREE(thys); /* thys->Handle = 0;*/ return NULL; } filePrintname(thys->Name, &thys->Printname); thys->Handle = ++fileHandle; thys->List = NULL; thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } /* @func ajFileNewInNamePathC ************************************************* ** ** Opens directory "dir". ** Looks for file "file" ** ** @param [r] name [const char*] Filename. ** @param [r] path [const AjPStr] Directory ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewInNamePathC(const char* name, const AjPStr path) { ajStrAssignC(&fileNameStrTmp, name); return ajFileNewInNamePathS(fileNameStrTmp, path); } /* @func ajFileNewInNamePathS ************************************************* ** ** Opens directory "dir". ** Looks for file "file" ** ** @param [r] name [const AjPStr] Filename. ** @param [r] path [const AjPStr] Directory ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewInNamePathS(const AjPStr name, const AjPStr path) { if(ajStrGetLen(path)) ajStrAssignS(&fileNameFix, path); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendS(&fileNameFix, name); return ajFileNewInNameS(fileNameFix); } /* @func ajFileNewInPipe ****************************************************** ** ** Creates a new file object to read the output from a command. ** ** @param [r] command [const AjPStr] Command string. ** The string may end with a trailing pipe character. ** @return [AjPFile] New file object. ** ** @release 1.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewInPipe(const AjPStr command) { AjPFile thys = NULL; thys = ajSysCreateInpipeS(command); if(!thys) return NULL; thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, command); ajStrAssignS(&thys->Printname, thys->Name); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } /* @func ajFileNewListinDirPre ************************************************ ** ** Opens directory "dir". ** Looks for file "file" with the extension (if any) specified ** for the directory ** ** @param [r] dir [const AjPDir] Directory ** @param [r] prefix [const AjPStr] Wildcard Filename. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewListinDirPre(const AjPDir dir, const AjPStr prefix) { if(ajStrGetLen(dir->Name)) ajStrAssignS(&fileNameFix, dir->Name); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendS(&fileNameFix, prefix); ajFilenameReplaceExtS(&fileNameFix, dir->Extension); return ajFileNewInNameS(fileNameFix); } /* @func ajFileNewListinList ************************************************** ** ** Creates a new file object with a list of input file names. ** ** @param [u] list [AjPList] List of input filenames as strings. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewListinList(AjPList list) { AjPFile thys; AJNEW0(thys); thys->List = list; thys->Name = NULL; ajListstrTrace(thys->List); ajListstrPop(thys->List, &thys->Name); ajDebug("ajFileNewListinList pop '%S'\n", thys->Name); ajListstrTrace(thys->List); ajNamResolve(&thys->Name); #ifdef WIN32 if(ajStrMatchC(thys->Name, "/dev/null")) thys->fp = fopen("NUL", "rb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(thys->Name), "rb"); if(!thys->fp) { ajDebug("ajFileNewListinList fopen failed\n"); ajStrDel(&thys->Name); thys->Handle = 0; AJFREE(thys); return NULL; } filePrintname(thys->Name, &thys->Printname); thys->Handle = ++fileHandle; thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } /* @func ajFileNewListinNameDirS ********************************************** ** ** Opens directory "dir" and looks for file "filename". ** ** @param [r] name [const AjPStr] Wildcard Filename. ** @param [r] dir [const AjPDir] Directory ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewListinNameDirS(const AjPStr name, const AjPDir dir) { if(ajStrGetLen(dir->Name)) ajStrAssignS(&fileNameFix, dir->Name); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendS(&fileNameFix, name); return ajFileNewInNameS(fileNameFix); } /* @func ajFileNewListinPathWild ********************************************** ** ** Opens directory "dir" ** Looks for file(s) matching "file" ** Opens them as a list of files using a simple file object. ** ** @param [r] path [const AjPStr] Directory ** @param [r] wildname [const AjPStr] Wildcard filename. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewListinPathWild(const AjPStr path, const AjPStr wildname) { DIR* dp; #if defined(AJ_IRIXLF) struct dirent64 *de; #else /* !AJ_IRIXLF */ struct dirent* de; #endif /* AJ_IRIXLF */ ajint dirsize; AjPList list = NULL; AjPStr name = NULL; #ifdef _POSIX_C_SOURCE char buf[sizeof(struct dirent)+MAXNAMLEN]; #endif /* _POSIX_C_SOURCE */ if(ajStrGetLen(path)) ajStrAssignS(&fileDirfixTmp, path); else ajStrAssignC(&fileDirfixTmp, CURRENT_DIR); if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); dp = fileOpenDir(&fileDirfixTmp); if(!dp) return NULL; dirsize = 0; list = ajListstrNew(); while( #if defined(AJ_IRIXLF) #ifdef _POSIX_C_SOURCE !readdir64_r(dp,(struct dirent64 *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir64(dp)) #endif /* _POSIX_C_SOURCE */ #else /* !AJ_IRIXLF */ #ifdef _POSIX_C_SOURCE !readdir_r(dp,(struct dirent *)buf,&de) #else /* _POSIX_C_SOURCE */ (de=readdir(dp)) #endif /* _POSIX_C_SOURCE */ #endif /* AJ_IRIXLF */ ) { #ifdef _POSIX_C_SOURCE if(!de) break; #endif /* _POSIX_C_SOURCE */ /* skip deleted files with inode zero */ #ifndef __CYGWIN__ if(!de->d_ino) continue; #endif /* !__CYGWIN__ */ if(ajCharMatchC(de->d_name, ".")) continue; if(ajCharMatchC(de->d_name, "..")) continue; if(!ajCharMatchWildS(de->d_name, wildname)) continue; dirsize++; ajDebug("accept '%s'\n", de->d_name); name = NULL; ajFmtPrintS(&name, "%S%s", fileDirfixTmp, de->d_name); ajListstrPushAppend(list, name); } closedir(dp); ajDebug("%d files for '%S' '%S'\n", dirsize, path, wildname); return ajFileNewListinList(list); } /* @func ajFileNewListinPathWildExclude *************************************** ** ** Opens directory "dir". ** Looks for file(s) matching "file". ** Skip files matching excluded files wildcard. ** Opens them as a list of files using a simple file object. ** ** @param [r] path [const AjPStr] Directory path ** @param [r] wildname [const AjPStr] Wildcard filename. ** @param [r] exclude [const AjPStr] Wildcard excluded filename. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewListinPathWildExclude(const AjPStr path, const AjPStr wildname, const AjPStr exclude) { DIR* dp; #if defined(AJ_IRIXLF) struct dirent64 *de; #else /* !AJ_IRIXLF */ struct dirent* de; #endif /* AJ_IRIXLF */ ajint dirsize; AjPList list = NULL; AjPStr name = NULL; #ifdef _POSIX_C_SOURCE char buf[sizeof(struct dirent)+MAXNAMLEN]; #endif /* _POSIX_C_SOURCE */ ajDebug("ajFileNewListinPathWildExclude " "path '%S' wildname '%S' exclude '%S\n", path, wildname, exclude); if(ajStrGetLen(path)) ajStrAssignS(&fileDirfixTmp, path); else ajStrAssignC(&fileDirfixTmp, CURRENT_DIR); if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); dp = fileOpenDir(&fileDirfixTmp); if(!dp) return NULL; dirsize = 0; list = ajListstrNew(); while( #if defined(AJ_IRIXLF) #ifdef _POSIX_C_SOURCE !readdir64_r(dp,(struct dirent64 *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir64(dp)) #endif /* _POSIX_C_SOURCE */ #else /* !AJ_IRIXLF */ #ifdef _POSIX_C_SOURCE !readdir_r(dp,(struct dirent *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir(dp)) #endif /* _POSIX_C_SOURCE */ #endif /* AJ_IRIXLF */ ) { #ifdef _POSIX_C_SOURCE if(!de) break; #endif /* _POSIX_C_SOURCE */ /* skip deleted files with inode zero */ #ifndef __CYGWIN__ if(!de->d_ino) continue; #endif /* !__CYGWIN__ */ if(ajCharMatchC(de->d_name, ".")) continue; if(ajCharMatchC(de->d_name, "..")) continue; ajStrAssignC(&fileNameTmp, de->d_name); if(!ajFilenameTestExclude(fileNameTmp, exclude, wildname)) continue; dirsize++; ajDebug("accept '%s'\n", de->d_name); name = NULL; ajFmtPrintS(&name, "%S%s", fileDirfixTmp, de->d_name); ajListstrPushAppend(list, name); } closedir(dp); ajDebug("%d files for '%S' '%S'\n", dirsize, path, wildname); return ajFileNewListinList(list); } /* @func ajFileNewOutNameC **************************************************** ** ** Creates a new output file object with a specified name. ** ** 'stdout' and 'stderr' are special names for standard output and ** standard error respectively. ** ** @param [r] name [const char*] File name. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewOutNameC(const char* name) { ajStrAssignC(&fileNameStrTmp, name); return ajFileNewOutNameS(fileNameStrTmp); } /* @func ajFileNewOutNameS **************************************************** ** ** Creates a new output file object with a specified name. ** ** 'stdout' and 'stderr' are special names for standard output and ** standard error respectively. ** ** @param [r] name [const AjPStr] File name. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewOutNameS(const AjPStr name) { AjPFile thys; if(ajStrMatchC(name, "stdout")) { thys = ajFileNewFromCfile(stdout); ajStrAssignC(&thys->Name, "stdout"); ajStrAssignS(&thys->Printname, thys->Name); return thys; } if(ajStrMatchC(name, "stderr")) { thys = ajFileNewFromCfile(stderr); ajStrAssignC(&thys->Name, "stderr"); ajStrAssignS(&thys->Printname, thys->Name); return thys; } AJNEW0(thys); #ifdef WIN32 if(ajStrMatchC(name, "/dev/null")) thys->fp = fopen(ajStrGetPtr(name), "wb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(name), "wb"); if(!thys->fp) { ajWarn("Failed to open output file '%S' error:%d '%s'", name, errno, strerror(errno)); thys->Handle = 0; return NULL; } thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, name); filePrintname(thys->Name, &thys->Printname); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; thys->App = ajFalse; return thys; } /* @func ajFileNewOutNameDirS ************************************************* ** ** Creates a new output file object with a specified directory and name. ** Uses the default extension (if any) specified for the directory. ** ** 'stdout' and 'stderr' are special names for standard output and ** standard error respectively. ** ** If the filename already has a directory specified, ** the "dir" argument is ignored. ** ** @param [r] name [const AjPStr] File name. ** @param [rN] dir [const AjPDirout] Directory ** (optional, can be empty or NULL). ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewOutNameDirS(const AjPStr name, const AjPDirout dir) { AjPFile thys; ajDebug("ajFileNewOutNameDirS('%S' '%S')\n", dir->Name, name); AJNEW0(thys); if(!dir) { #ifdef WIN32 if(ajStrMatchC(name, "/dev/null")) thys->fp = fopen("NUL", "wb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(name), "wb"); ajDebug("ajFileNewOutNameDirS open name '%S'\n", name); } else { if(ajFilenameHasPath(name)) ajStrAssignS(&fileDirfixTmp, name); else { ajStrAssignS(&fileDirfixTmp, dir->Name); if(ajStrGetCharLast(dir->Name) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); ajStrAppendS(&fileDirfixTmp, name); } ajFilenameSetExtS(&fileDirfixTmp, dir->Extension); thys->fp = fopen(ajStrGetPtr(fileDirfixTmp), "wb"); ajDebug("ajFileNewOutNameDirS open dirfix '%S'\n", fileDirfixTmp); } if(!thys->fp) { ajWarn("Failed to open output file '%S' in directory '%S' " "error:%d '%s'", name, dir, errno, strerror(errno)); thys->Handle = 0; return NULL; } thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, name); filePrintname(thys->Name, &thys->Printname); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } /* @func ajFileNewOutNamePathS ************************************************ ** ** Creates a new output file object with a specified directory and name. ** ** 'stdout' and 'stderr' are special names for standard output and ** standard error respectively. ** ** If the filename already has a directory specified, ** the "dir" argument is ignored. ** ** @param [r] name [const AjPStr] File name. ** @param [rN] path [const AjPStr] Directory (optional, can be empty or NULL). ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewOutNamePathS(const AjPStr name, const AjPStr path) { AjPFile thys; ajDebug("ajFileNewOutNamePathS('%S' '%S')\n", path, name); if(ajStrMatchC(name, "stdout")) return ajFileNewFromCfile(stdout); if(ajStrMatchC(name, "stderr")) return ajFileNewFromCfile(stderr); AJNEW0(thys); if(!ajStrGetLen(path)) { #ifdef WIN32 if(ajStrMatchC(name, "/dev/null")) thys->fp = fopen("NUL", "wb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(name), "wb"); ajDebug("ajFileNewOutNamePathS open name '%S'\n", name); } else { if(ajFilenameHasPath(name)) ajStrAssignS(&fileDirfixTmp, name); else { ajStrAssignS(&fileDirfixTmp, path); if(ajStrGetCharLast(path) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); ajStrAppendS(&fileDirfixTmp, name); } thys->fp = fopen(ajStrGetPtr(fileDirfixTmp), "wb"); ajDebug("ajFileNewOutNamePathS open dirfix '%S'\n", fileDirfixTmp); } if(!thys->fp) { ajWarn("Failed to open output file '%S' in directory '%S' " "error:%d '%s'", name, path, errno, strerror(errno)); thys->Handle = 0; return NULL; } thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, name); filePrintname(thys->Name, &thys->Printname); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } /* @func ajFileNewOutappendNameS ********************************************** ** ** Creates an output file object with a specified name. ** The file is opened for append so it either appends to an existing file ** or opens a new one. ** ** @param [r] name [const AjPStr] File name. ** @return [AjPFile] New file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFile ajFileNewOutappendNameS(const AjPStr name) { AjPFile thys; AJNEW0(thys); #ifdef WIN32 if(ajStrMatchC(name, "/dev/null")) thys->fp = fopen("NUL", "ab"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(name), "ab"); if(!thys->fp) { thys->Handle = 0; return NULL; } thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, name); filePrintname(thys->Name, &thys->Printname); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; thys->App = ajTrue; return thys; } /* @section File destructors ************************************************** ** ** Destruction is achieved by closing the file. ** ** Unlike ANSI C, there are tests to ensure a file is not closed twice. ** ** @nam3rule Close Close file and destroy object ** ** @argrule Close Pfile [AjPFile*] File to be closed and deleted ** ** @valrule Close [void] ** ** @fcategory delete ** ** @fdata [AjPFile] ** ******************************************************************************/ /* @func ajFileClose ********************************************************** ** ** Close and free a file object. ** ** @param [d] Pfile [AjPFile*] File. ** @return [void] ** ** @release 1.0.0 ** @@ ******************************************************************************/ void ajFileClose(AjPFile* Pfile) { AjPFile thys; thys = Pfile ? *Pfile : 0; if(!Pfile) return; if(!*Pfile) return; fileClose(thys); AJFREE(*Pfile); return; } /* @funcstatic fileClose ****************************************************** ** ** Closes a file object. Used as part of the public destructor and ** other public functions. ** ** @param [w] thys [AjPFile] File. ** @return [void] ** ** @release 1.0.0 ** @@ ******************************************************************************/ static void fileClose(AjPFile thys) { int sleepcount = 0; int status = 0; int maxsleep = 60; #ifndef WIN32 pid_t retval; #endif /* !WIN32 */ if(!thys) return; #ifndef WIN32 /* ** Only wait for the PID to close if we have read everything. ** Otherwise ... we will wait for ever as it will not ** go away until its output is read or closed */ if (thys->Pid && thys->End) { while((retval=waitpid(thys->Pid,&status,WNOHANG))!= thys->Pid) { if(sleepcount > maxsleep) break; sleepcount++; sleep(1); /*ajDebug("fileClose waitpid returns %d status %d\n", retval, status);*/ if(retval == -1) if(errno != EINTR) break; status = 0; } } #else /* WIN32 */ if(thys->Process && thys->End) { WaitForSingleObject(thys->Process,INFINITE); CloseHandle(thys->Process); CloseHandle(thys->Thread); } #endif /* !WIN32 */ if(thys->Handle) { if(thys->fp) { if(thys->fp == stdout) fileUsedStdout = ajFalse; else if(thys->fp == stderr) fileUsedStderr = ajFalse; else if(thys->fp == stdin) fileUsedStdin = ajFalse; else { if(fclose(thys->fp)) ajFatal("File close problem in fileClose error:%d '%s'", errno, strerror(errno)); } } thys->Handle = 0; fileCloseCnt++; fileOpenCnt--; } else ajDebug("file already closed\n"); ajStrDel(&thys->Name); ajStrDel(&thys->Printname); ajStrDel(&thys->Buff); ajListstrFreeData(&thys->List); AJFREE(thys->Workbuffer); AJFREE(thys->Readblock); return; } /* @section File reopen ******************************************************* ** ** These functions close a file and open a new file with the same file object ** ** @nam3rule Reopen Reopen an existing input file object ** @nam4rule Name Reopen using a new filename ** @nam4rule Next Reopen using the next filename in the input file list ** ** @argrule Reopen file [AjPFile] File object ** @argrule Name name [const AjPStr] Filename to open ** ** @valrule * [AjBool] True on success ** ** @fcategory modify ** ** @fdata [AjPFile] ** ******************************************************************************/ /* @func ajFileReopenName ***************************************************** ** ** Reopens an input file with a new name ** ** @param [u] file [AjPFile] Input file. ** @param [r] name [const AjPStr] name of file. ** @return [AjBool] True on success ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileReopenName(AjPFile file, const AjPStr name) { ajStrAssignS(&file->Name, name); filePrintname(file->Name, &file->Printname); if(!freopen(MAJSTRGETPTR(file->Name), "rb", file->fp)) return ajFalse; return ajTrue; } /* @func ajFileReopenNext ***************************************************** ** ** Given a file object that includes a list of input files, closes the ** current input file and opens the next one. ** ** @param [u] file [AjPFile] File object. ** @return [AjBool] ajTrue on success. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileReopenNext(AjPFile file) { static AjPStr name = NULL; if(!file->List) { ajDebug("ajFileReopenNext for non-list file %F\n", file); return ajFalse; } ajDebug("ajFileReopenNext for non-list file %F name '%S'\n", file, file->Name); /*ajListTrace(file->List);*/ if(!ajListPop(file->List, (void*) &name)) { /* end of list */ ajDebug("ajFileReopenNext failed - list completed\n"); return ajFalse; } ajDebug("ajFileReopenNext filename '%S'\n", name); if(!ajFileReopenName(file, name)) { /* popped from the list */ ajStrDel(&name); return ajFalse; } /* popped from the list */ ajStrDel(&name); file->End = ajFalse; ajDebug("ajFileNext success\n"); return ajTrue; } /* @section File modifiers **************************************************** ** ** Modifying file object attributes, reset file position. ** ** @nam3rule Fix Reset all file attribute that may have been changed ** by non-AJAX calls ** @nam3rule Reset Reset a file attribute that may have been changed ** by non-AJAX calls ** @nam3rule Seek Call system file seek function ** @nam3rule Set Set a file attribute ** @nam4rule ResetEof Reset the end of file value using the system ** 'feof' function ** @nam4rule ResetPos Reset the file position using the system ** 'ftell' function ** @nam4rule SetEof Set the end of file value for a file that has been read ** without going past the last record. ** @nam4rule SetUnbuffer Set file unbuffered ** ** @argrule * file [AjPFile] File object ** @argrule Seek offset [ajlong] File offset to pass to system seek call ** @argrule Seek wherefrom [ajint] File wherefrom value to pass to ** system seek call ** ** @valrule Seek [ajint] Return value from seek ** @valrule SetUnbuffer [void] ** @valrule Fix [AjBool] End of file reached ** @valrule Eof [AjBool] End of file reached ** @valrule Pos [ajlong] File position from ftell ** ** @fcategory modify ** ** @fdata [AjPFile] ** ******************************************************************************/ /* @func ajFileFix ************************************************************ ** ** Resets internal file attribute after non-AJAX operations. ** ** @param [u] file [AjPFile] File. ** @return [AjBool] True if end of file is reached ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileFix(AjPFile file) { ajFileResetPos(file); return ajFileResetEof(file); } /* @func ajFileResetEof ******************************************************* ** ** Resets the end of file attribute after non-AJAX operations. ** ** @param [u] file [AjPFile] File. ** @return [AjBool] True if end of file is set ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileResetEof(AjPFile file) { if(feof(file->fp)) file->End = ajTrue; else file->End = ajFalse; return file->End; } /* @func ajFileResetPos ******************************************************* ** ** Resets and returns the current position in an open file. ** ** @param [u] file [AjPFile] File. ** @return [ajlong] Result of 'ftell' ** ** @release 6.0.0 ** @@ ******************************************************************************/ ajlong ajFileResetPos(AjPFile file) { if(!file->fp) return 0; file->Filepos = ftell(file->fp); return file->Filepos; } /* @func ajFileSeek *********************************************************** ** ** Sets the current position in an open file. ** ** Resets the end-of-file flag End for cases where end-of-file was ** reached and then we seek back somewhere in the file. ** ** @param [u] file [AjPFile] File. ** @param [r] offset [ajlong] Offset ** @param [r] wherefrom [ajint] Start of offset, as defined for 'fseek'. ** @return [ajint] Result of 'fseek' ** ** @release 1.0.0 ** @@ ******************************************************************************/ ajint ajFileSeek(AjPFile file, ajlong offset, ajint wherefrom) { ajint ret; clearerr(file->fp); ret = fseek(file->fp, offset, wherefrom); if(feof(file->fp)) { file->End = ajTrue; ajDebug("EOF ajFileSeek file %F\n", file); } else file->End = ajFalse; return ret; } /* @func ajFileSetEof ********************************************************* ** ** Ensures a binary file has reached the end of file and sets the ** end of file attribute ** ** @param [u] file [AjPFile] File. ** @return [AjBool] True if end of file was already ** ** @release 6.5.0 ** @@ ******************************************************************************/ AjBool ajFileSetEof(AjPFile file) { AjBool ret = file->End; char c; fseek(file->fp, 0, SEEK_END); fread(&c, 1, 1, file->fp); if(feof(file->fp)) file->End = ajTrue; else file->End = ajFalse; return ret; } /* @func ajFileSetUnbuffer **************************************************** ** ** Turns off system buffering of an output file, for example to allow ** debug output to appear even in the event of a program abort. ** ** @param [u] file [AjPFile] File object. ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ void ajFileSetUnbuffer(AjPFile file) { setbuf(file->fp, NULL); return; } /* @section File casts ******************************************************** ** ** Returns attributes of a file object ** ** @nam3rule Get Return attribute of a file object ** @nam4rule GetFileptr Return C file pointer ** @nam4rule GetName Return filename ** @nam4rule GetPrintname Return printable filename ** @nam3rule Is Return true if attribute is set ** @nam4rule IsAppend Test file is open for appending output ** @nam4rule IsEof Test end of file has been reached ** @nam4rule IsFile Test file is a regular file, not a pipe or terminal ** @nam4rule IsStderr Test file is writing to standard error ** @nam4rule IsStdin test file is reading from standard input ** @nam4rule IsStdout test file is writing to standard output ** ** @suffix C Return a C character string ** @suffix S return a string object ** ** @argrule * file [const AjPFile] File object ** ** @valrule C [const char*] C character string ** @valrule S [const AjPStr] String object ** @valrule Is [AjBool] True on success ** @valrule GetFileptr [FILE*] C file pointer ** ** @fdata [AjPFile] ** @fcategory cast ** ******************************************************************************/ /* @func ajFileGetFileptr ***************************************************** ** ** Returns the C file pointer for an open file. ** ** Warning: Using the C file pointer will make internals of the file ** object invalid. The file position can be reset with ajFileResetPos. ** ** @param [r] file [const AjPFile] File. ** @return [FILE*] C file pointer for the file. ** ** @release 6.0.0 ** @@ ******************************************************************************/ FILE* ajFileGetFileptr(const AjPFile file) { if(!file) return NULL; return file->fp; } /* @func ajFileGetNameC ******************************************************* ** ** Returns the file name for a file object. The filename returned is a pointer ** to the real string internally, so the user must take care not to change ** it and cannot trust the value if the file object is deleted. ** ** @param [r] file [const AjPFile] File. ** @return [const char*] Filename as a C character string. ** ** @release 6.0.0 ** @@ ******************************************************************************/ const char* ajFileGetNameC(const AjPFile file) { if(!file) return ""; return ajStrGetPtr(file->Name); } /* @func ajFileGetNameS ******************************************************* ** ** Returns the file name for a file object. The filename returned is a pointer ** to the real string internally, so the user must take care not to change ** it and cannot trust the value if the file object is deleted. ** ** @param [r] file [const AjPFile] File. ** @return [const AjPStr] Filename as a C character string. ** ** @release 6.0.0 ** @@ ******************************************************************************/ const AjPStr ajFileGetNameS(const AjPFile file) { if(!file) return NULL; return file->Name; } /* @func ajFileGetPrintnameC ************************************************** ** ** Returns the file name for a file object. The filename returned is a pointer ** to the real string internally, so the user must take care not to change ** it and cannot trust the value if the file object is deleted. ** ** @param [r] file [const AjPFile] File. ** @return [const char*] Printable filename as a C character string. ** ** @release 6.4.0 ** @@ ******************************************************************************/ const char* ajFileGetPrintnameC(const AjPFile file) { if(!file) return ""; return ajStrGetPtr(file->Printname); } /* @func ajFileGetPrintnameS ************************************************** ** ** Returns the file name for a file object. The filename returned is a pointer ** to the real string internally, so the user must take care not to change ** it and cannot trust the value if the file object is deleted. ** ** @param [r] file [const AjPFile] File. ** @return [const AjPStr] Printable filename as a C character string. ** ** @release 6.4.0 ** @@ ******************************************************************************/ const AjPStr ajFileGetPrintnameS(const AjPFile file) { if(!file) return NULL; return file->Printname; } /* @func ajFileIsAppend ******************************************************* ** ** Returns the App element for a file object. The App element is True if the ** file was opened for appending to, False otherwise. ** ** @param [r] file [const AjPFile] File. ** @return [AjBool] App element, True if if file was opened for appending to, ** False otherwise. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileIsAppend(const AjPFile file) { if(!file) return ajFalse; return file->App; } /* @func ajFileIsEof ********************************************************** ** ** Tests whether we have reached end of file already ** ** @param [r] file [const AjPFile] File ** @return [AjBool] ajTrue if we already set end-of-file ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileIsEof(const AjPFile file) { if(!file) return ajTrue; return file->End; } /* @func ajFileIsFile ********************************************************* ** ** Tests whether a file object is really a regular file. ** ** Used to test for character devices, for example standard input from ** a terminal. ** ** @param [r] file [const AjPFile] File object. ** @return [AjBool] ajTrue if the file matches stderr. ** ** @release 6.3.0 ** @@ ******************************************************************************/ AjBool ajFileIsFile(const AjPFile file) { #if defined(AJ_IRIXLF) struct stat64 buf; #else /* !AJ_IRIXLF */ struct stat buf; #endif /* AJ_IRIXLF */ if( #if defined(AJ_IRIXLF) !fstat64(fileno(file->fp), &buf) #else /* !AJ_IRIXLF */ !fstat(fileno(file->fp), &buf) #endif /* AJ_IRIXLF */ ) if((ajuint)buf.st_mode & AJ_FILE_R) return ajTrue; return ajFalse; } /* @func ajFileIsStderr ******************************************************* ** ** Tests whether a file object is really stderr. ** ** @param [r] file [const AjPFile] File object. ** @return [AjBool] ajTrue if the file matches stderr. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileIsStderr(const AjPFile file) { if(!file) return ajFalse; if(file->fp == stderr) return ajTrue; return ajFalse; } /* @func ajFileIsStdin ******************************************************** ** ** Tests whether a file object is really stdin. ** ** @param [r] file [const AjPFile] File object. ** @return [AjBool] ajTrue if the file matches stdin. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileIsStdin(const AjPFile file) { if(!file) return ajFalse; if(file->fp == stdin) return ajTrue; return ajFalse; } /* @func ajFileIsStdout ******************************************************* ** ** Tests whether a file object is really stdout. ** ** @param [r] file [const AjPFile] File object. ** @return [AjBool] ajTrue if the file matches stdout. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFileIsStdout(const AjPFile file) { if(!file) return ajFalse; if(file->fp == stdout) return ajTrue; return ajFalse; } /* @section File debug ******************************************************** ** ** report file object contents for debugging ** ** ** @nam3rule Trace Print report to debug file (if any) ** ** @argrule * file [const AjPFile] ** ** @valrule * [void] ** ** @fcategory misc ** @fdata [AjPFile] ** ******************************************************************************/ /* @func ajFileTrace ********************************************************** ** ** Writes debug messages to trace the contents of a file object. ** ** @param [r] file [const AjPFile] File. ** @return [void] ** ** @release 1.0.0 ** @@ ******************************************************************************/ void ajFileTrace(const AjPFile file) { ajulong i; ajulong j; AjIList iter; ajDebug("File: '%S'\n", file->Name); ajDebug("Print: '%S'\n", file->Printname); ajDebug(" Handle: %d\n", file->Handle); ajDebug(" End: %B\n", file->End); ajDebug(" Append: %B\n", file->App); ajDebug(" Filepos: %ld\n", file->Filepos); #ifndef WIN32 ajDebug(" PID: %d\n", file->Pid); #endif /* !WIN32 */ ajDebug(" feof: %d\n", feof(file->fp)); ajDebug(" ftell: %ld\n", ftell(file->fp)); i = ajListGetLength(file->List); ajDebug(" List: %Lu\n", i); if(i) { j = 0; iter = ajListIterNewread(file->List); while (!ajListIterDone(iter)) ajDebug(" %3Ld: '%S'\n", ++j, ajListstrIterGet(iter)); } return; } /* @section File exit ********************************************************* ** ** Cleanup memory on program exit ** ** @fnote general exit functions, no arguments ** ** @nam3rule Exit Cleanup and report on exit ** ** @valrule * [void] ** ** @fcategory misc ** ** @fdata [AjPFile] ** ******************************************************************************/ /* @func ajFileExit *********************************************************** ** ** Prints a summary of file usage with debug calls ** ** @return [void] ** ** @release 1.0.0 ** @@ ******************************************************************************/ void ajFileExit(void) { ajDebug("File usage : %d opened, %d closed, %d max, %d total\n", fileOpenCnt, fileCloseCnt, fileOpenMax, fileOpenTot); ajStrDel(&fileNameFix); ajStrDel(&fileNameTmp); ajStrDel(&fileNameStrTmp); ajStrDel(&fileDirfixTmp); ajStrDel(&fileCwd); ajStrDel(&fileTmpStr); ajStrDel(&fileTempFilename); ajStrDel(&fileDirectory); ajRegFree(&fileUserExp); ajRegFree(&fileWildExp); ajRegFree(&fileEntryExp); ajRegFree(&fileFileExp); ajRegFree(&fileRestExp); ajRegFree(&fileDirExp); ajRegFree(&fileFilenameExp); return; } /* @datasection [AjPFilebuff] Buffered file object **************************** ** ** Function is for manipulating buffered files and returns or takes at least ** one AjSFileBuff argument. ** ** @nam2rule Filebuff ** ******************************************************************************/ /* @section Buffered file constructors **************************************** ** ** All constructors return a new open file by pointer. It is the responsibility ** of the user to first destroy any previous file pointer. The target pointer ** does not need to be initialised to NULL, but it is good programming practice ** to do so anyway. ** ** To replace or reuse an existing file, see instead ** the {File Assignments} and {File Modifiers} functions. ** ** The range of constructors is provided to allow flexibility in how ** applications can open files to read various kinds of data. ** ** @nam3rule New Constructor ** @nam4rule From Create from an already open file ** @nam4rule Nofile Create without an input file for use as a text buffer ** @nam5rule FromFile AjPfile object used to create buffered file object ** @nam5rule FromCfile C FILE* structure used to create buffered file object ** @nam4rule Line Create buffer with line of text ** @nam4rule Listin List of one or more input files ** @nam5rule ListinList List of files specified ** ** @suffix Name Existing filename specified ** @suffix C Filename as C character string ** @suffix S Filename as string ** @suffix Path Input directory path specified ** @suffix Wild Wildcard filename ** @suffix Exclude Filename exclusion wildcard(s) ** ** @argrule Cfile file [FILE*] C file pointer ** @argrule ListinList list [AjPList] List of filenames as strings ** @argrule C name [const char*] Filename ** @argrule S name [const AjPStr] Filename ** @argrule Line line [const AjPStr] First line of new buffer ** @argrule FromFile file [AjPFile] Input file object ** @argrule Path path [const AjPStr] Input directory path ** @argrule Wild wildname [const AjPStr] Wildcard filename ** @argrule Exclude exclude [const AjPStr] Filename exclusion wildcard(s) ** ** @valrule * [AjPFilebuff] New buffered file object ** ** @fcategory new ** ** @fdata [AjPFilebuff] ** ******************************************************************************/ /* @func ajFilebuffNewFromCfile *********************************************** ** ** Creates a new buffered input file from an already open C file. ** ** @param [u] file [FILE*] Open C file. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewFromCfile(FILE* file) { AjPFile tmpcfile; tmpcfile = ajFileNewFromCfile(file); if(!tmpcfile) return NULL; return ajFilebuffNewFromFile(tmpcfile); } /* @func ajFilebuffNewFromFile ************************************************ ** ** Creates a new buffered input file object from an open file. ** ** @param [u] file [AjPFile] File object to be buffered. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewFromFile(AjPFile file) { AjPFilebuff thys; if(!file) return NULL; AJNEW0(thys); thys->File = file; thys->Last = thys->Curr = thys->Prev = thys->Lines = NULL; thys->Freelines = thys->Freelast = NULL; thys->Pos = thys->Size = 0; return thys; } /* @func ajFilebuffNewLine **************************************************** ** ** Creates a new buffered input file object with no file but with ** one line of buffered data provided. ** ** @param [r] line [const AjPStr] One line of buffered data. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewLine(const AjPStr line) { AjPFilebuff thys; AjPFile file; file = fileNew(); file->End = ajTrue; ajDebug("EOF ajFilebuffNewLine file \n"); thys = ajFilebuffNewFromFile(file); thys->Lines = AJNEW0(thys->Last); ajStrAssignS(&thys->Last->Line,line); thys->Curr = thys->Lines; thys->Pos = 0; thys->Size = 1; return thys; } /* @func ajFilebuffNewListinList ********************************************** ** ** Creates a new buffered file object from a list of filenames. ** ** @param [u] list [AjPList] List of filenames as strings. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewListinList(AjPList list) { AjPFile file; file = ajFileNewListinList(list); if(!file) return NULL; return ajFilebuffNewFromFile(file); } /* @func ajFilebuffNewNameS *************************************************** ** ** Creates a new buffered input file object with an opened named file. ** ** @param [r] name [const AjPStr] File name. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewNameS(const AjPStr name) { AjPFile file; file = ajFileNewInNameS(name); return ajFilebuffNewFromFile(file); } /* @func ajFilebuffNewNamePathC *********************************************** ** ** Opens directory "dir", finds and opens file "name" ** ** @param [r] name [const char*] Filename. ** @param [r] path [const AjPStr] Directory. If empty uses current directory. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewNamePathC(const char* name, const AjPStr path) { if(ajStrGetLen(path)) ajStrAssignS(&fileNameFix, path); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendC(&fileNameFix, name); return ajFilebuffNewNameS(fileNameFix); } /* @func ajFilebuffNewNamePathS *********************************************** ** ** Opens directory "dir", finds and opens file "name" ** ** @param [r] name [const AjPStr] Filename. ** @param [r] path [const AjPStr] Directory. If empty uses current directory. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewNamePathS(const AjPStr name, const AjPStr path) { if(ajStrGetLen(path)) ajStrAssignS(&fileNameFix, path); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendS(&fileNameFix, name); return ajFilebuffNewNameS(fileNameFix); } /* @func ajFilebuffNewNofile ************************************************** ** ** Creates a new buffered input file object with an undefined file ** to be used as a text buffer. ** ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewNofile(void) { AjPFile file; file = fileNew(); /* dummy file */ return ajFilebuffNewFromFile(file); } /* @func ajFilebuffNewPathWild ************************************************ ** ** Opens directory "dir" ** Looks for file(s) matching "file" ** Opens them as a list of files using a buffered file object. ** ** @param [r] path [const AjPStr] Directory ** @param [r] wildname [const AjPStr] Wildcard filename. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewPathWild(const AjPStr path, const AjPStr wildname) { DIR* dp; #if defined(AJ_IRIXLF) struct dirent64 *de; #else /* !AJ_IRIXLF */ struct dirent* de; #endif /* AJ_IRIXLF */ ajint dirsize; AjPList list = NULL; AjPStr name = NULL; #ifdef _POSIX_C_SOURCE char buf[sizeof(struct dirent)+MAXNAMLEN]; #endif /* _POSIX_C_SOURCE */ if(ajStrGetLen(path)) ajStrAssignS(&fileDirfixTmp, path); else ajStrAssignC(&fileDirfixTmp, CURRENT_DIR); if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); dp = fileOpenDir(&fileDirfixTmp); if(!dp) return NULL; dirsize = 0; list = ajListstrNew(); while( #if defined(AJ_IRIXLF) #ifdef _POSIX_C_SOURCE !readdir64_r(dp,(struct dirent64 *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir64(dp)) #endif /* _POSIX_C_SOURCE */ #else /* !AJ_IRIXLF */ #ifdef _POSIX_C_SOURCE !readdir_r(dp,(struct dirent *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir(dp)) #endif /* _POSIX_C_SOURCE */ #endif /* AJ_IRIXLF */ ) { #ifdef _POSIX_C_SOURCE if(!de) break; #endif /* _POSIX_C_SOURCE */ /* skip deleted files with inode zero */ #ifndef __CYGWIN__ if(!de->d_ino) continue; #endif /* !__CYGWIN__ */ if(ajCharMatchC(de->d_name, ".")) continue; if(ajCharMatchC(de->d_name, "..")) continue; if(!ajCharMatchWildS(de->d_name, wildname)) continue; dirsize++; ajDebug("accept '%s'\n", de->d_name); name = NULL; ajFmtPrintS(&name, "%S%s", fileDirfixTmp, de->d_name); ajListstrPushAppend(list, name); } closedir(dp); ajDebug("%d files for '%S' '%S'\n", dirsize, path, wildname); return ajFilebuffNewListinList(list); } /* @func ajFilebuffNewPathWildExclude ***************************************** ** ** Opens directory "dir" ** Looks for file(s) matching "file" ** Skip files matching excluded files wildcard ** Opens them as a list of files using a buffered file object. ** ** @param [r] path [const AjPStr] Directory ** @param [r] wildname [const AjPStr] Wildcard filename. ** @param [r] exclude [const AjPStr] Wildcard excluded filename. ** @return [AjPFilebuff] New buffered file object. ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjPFilebuff ajFilebuffNewPathWildExclude(const AjPStr path, const AjPStr wildname, const AjPStr exclude) { DIR* dp; #if defined(AJ_IRIXLF) struct dirent64 *de; #else /* !AJ_IRIXLF */ struct dirent* de; #endif /* AJ_IRIXLF */ ajint dirsize; AjPList list = NULL; AjPStr name = NULL; #ifdef _POSIX_C_SOURCE char buf[sizeof(struct dirent)+MAXNAMLEN]; #endif /* _POSIX_C_SOURCE */ ajDebug("ajFilebuffNewPathWildExclude " "path '%S' wildname '%S' exclude '%S\n", path, wildname, exclude); if(ajStrGetLen(path)) ajStrAssignS(&fileDirfixTmp, path); else ajStrAssignC(&fileDirfixTmp, CURRENT_DIR); if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); dp = fileOpenDir(&fileDirfixTmp); if(!dp) return NULL; dirsize = 0; list = ajListstrNew(); while( #if defined(AJ_IRIXLF) #ifdef _POSIX_C_SOURCE !readdir64_r(dp,(struct dirent64 *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir64(dp)) #endif /* _POSIX_C_SOURCE */ #else /* !AJ_IRIXLF */ #ifdef _POSIX_C_SOURCE !readdir_r(dp,(struct dirent *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir(dp)) #endif /* _POSIX_C_SOURCE */ #endif /* AJ_IRIXLF */ ) { #ifdef _POSIX_C_SOURCE if(!de) break; #endif /* _POSIX_C_SOURCE */ /* skip deleted files with inode zero */ #ifndef __CYGWIN__ if(!de->d_ino) continue; #endif /* !__CYGWIN__ */ if(ajCharMatchC(de->d_name, ".")) continue; if(ajCharMatchC(de->d_name, "..")) continue; ajStrAssignC(&fileNameTmp, de->d_name); ajDebug("testing '%s'\n", de->d_name); if(!ajFilenameTestExclude(fileNameTmp, exclude, wildname)) continue; dirsize++; ajDebug("accept '%s'\n", de->d_name); name = NULL; ajFmtPrintS(&name, "%S%s", fileDirfixTmp, de->d_name); ajListstrPushAppend(list, name); } closedir(dp); ajDebug("%d files for '%S' '%S'\n", dirsize, path, wildname); ajStrDelStatic(&fileNameTmp); return ajFilebuffNewListinList(list); } /* @funcstatic fileOpenDir **************************************************** ** ** Runs 'opendir' on the specified directory. If the directory name ** has no trailing slash (on Unix) then one is added. This is why the ** directory name must be writable. ** ** @param [u] dir [AjPStr*] Directory name. ** @return [DIR*] result of the opendir call. ** ** @release 1.0.0 ** @@ ******************************************************************************/ static DIR* fileOpenDir(AjPStr* dir) { AjBool moved = ajFalse; AjPStr cwdpath = NULL; AjPStr userstr = NULL; AjPStr reststr = NULL; char *hdir = NULL; if(ajStrGetCharLast(*dir) != SLASH_CHAR) ajStrAppendC(dir, SLASH_STRING); /* going up */ while(ajStrPrefixC(*dir, UP_DIR)) { if(!moved) cwdpath = ajStrNewS(ajFileValueCwd()); moved = ajTrue; ajDirnameUp(&cwdpath); ajStrKeepRange(dir, 3, -1); ajDebug("Going up '%S' '%S'\n", *dir, fileCwd); } if(moved) ajStrInsertS(dir, 0, cwdpath); if(moved) ajStrDel(&cwdpath); ajStrAssignS(&fileNameTmp, *dir); if(ajStrGetCharFirst(fileNameTmp) == '~') { ajDebug("starts with '~'\n"); if(!fileUserExp) fileUserExp = ajRegCompC("^~([^/\\\\]*)"); ajRegExec(fileUserExp, fileNameTmp); ajRegSubI(fileUserExp, 1, &userstr); ajRegPost(fileUserExp, &reststr); ajDebug(" user: '%S' rest: '%S'\n", userstr, reststr); if(ajStrGetLen(userstr)) { /* username specified */ hdir = ajSysGetHomedirFromName(ajStrGetPtr(userstr)); if(!hdir) { ajStrDel(&userstr); ajStrDelStatic(&fileNameTmp); ajStrDel(&reststr); return NULL; } ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr); AJFREE(hdir); ajDebug("use getpwnam: '%S'\n", fileNameTmp); } else { /* just ~/ */ hdir = ajSysGetHomedir(); if(hdir) { ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr); AJFREE(hdir); } else ajFmtPrintS(&fileNameTmp,"%S",reststr); ajDebug("use HOME: '%S'\n", fileNameTmp); } ajStrAssignS(dir, fileNameTmp); } ajStrDel(&userstr); ajStrDel(&reststr); ajDebug("fileOpenDir opened '%S'\n", *dir); return opendir(ajStrGetPtr(*dir)); } /* @section Buffered file destructors ***************************************** ** ** Destruction is achieved by closing the file. ** ** Unlike ANSI C, there are tests to ensure a file is not closed twice. ** ** @nam3rule Del Destructor ** ** @argrule Del Pbuff [AjPFilebuff*] Buffered file to be deleted ** ** @valrule * [void] ** ** @fcategory delete ** ** @fdata [AjPFilebuff] ** ******************************************************************************/ /* @func ajFilebuffDel ******************************************************** ** ** Destructor for a buffered file object. ** ** @param [d] Pbuff [AjPFilebuff*] Buffered file object. ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ void ajFilebuffDel(AjPFilebuff* Pbuff) { AjPFilebuff thys; if(!Pbuff) return; thys = *Pbuff; if(!thys) return; if(thys->File) ajDebug("ajFilebuffDel '%F' fp: %p size:%u\n", thys->File, thys->File->fp, thys->Size); ajFilebuffClear(thys, -1); filebuffFreeClear(thys); ajFileClose(&thys->File); AJFREE(*Pbuff); return; } /* @section Buffered file reopen ********************************************** ** ** These functions close a file and open a new file with the same file object ** ** @nam3rule Reopen Reopen an existing buffered file object ** @nam4rule File Reopen using a new file object ** ** @argrule Reopen Pbuff [AjPFilebuff*] Buffered file object ** @argrule Reopen file [AjPFile] File object ** ** @valrule * [AjBool] True on success ** ** @fcategory modify ** ** @fdata [AjPFilebuff] ** ******************************************************************************/ /* @func ajFilebuffReopenFile ************************************************* ** ** Sets buffered input file to use a new open file. ** ** The AjPFile pointer is a clone, so we should simply overwrite ** whatever was there before, but we do need to clear the previous buffer ** contents. ** ** @param [w] Pbuff [AjPFilebuff*] Buffered file object. ** @param [u] file [AjPFile] File object to be buffered. ** @return [AjBool] ajTrue on success ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFilebuffReopenFile(AjPFilebuff* Pbuff, AjPFile file) { AjPFilebuff thys; if(!file) { ajFatal("used ajFilebuffReopenFile with deleted file buffer"); ajFilebuffDel(Pbuff); return ajFalse; } if(!*Pbuff) { *Pbuff = ajFilebuffNewFromFile(file); thys = *Pbuff; return ajTrue; } thys = *Pbuff; ajFilebuffClear(thys, -1); thys->File = file; return ajTrue; } /* @section Buffered file modifiers ******************************************* ** ** These functions use the attributes of a buffered file object and ** update them. ** ** @nam3rule Clear Removes processed lines from the buffer ** @nam3rule Fix Resets the pointer and current record of a file buffer ** after buffer contents have been edited ** @nam3rule Reset Reset buffered file attributes and contents. ** @nam3rule Set Set file attribute ** @nam4rule Pos Reset file pointer to start of buffer ** @nam5rule Buffered Set file unbuffered ** @nam4rule Buffered Set file buffered ** @nam4rule Unbuffered Set file unbuffered ** ** @suffix Store Reset test store buffer ** ** @argrule * buff [AjPFilebuff] Buffered input file object ** @argrule C line [const char*] Line of text ** @argrule S line [const AjPStr] Line of text ** @argrule Clear lines [ajint] Number of lines to retain ** (-1 to clear whole buffer) ** @argrule ClearStore lastline [const AjPStr] Last line of input ** @argrule Store dostore [AjBool] If true, use the text store buffer ** @argrule Store Pstore [AjPStr*] Original text store buffer ** ** @valrule * [void] ** @valrule *Set [AjBool] Previous buffer setting ** ** @fcategory modify ** ** @fdata [AjPFilebuff] ** ******************************************************************************/ /* @func ajFilebuffClear ****************************************************** ** ** Deletes processed lines from a file buffer. The buffer has a record ** (Pos) of the next unprocessed line in the buffer. ** ** Unbuffered files need special handling. The buffer can be turned off ** while it still contains data. If so, we have to carefully run it down. ** If this runs it to zero, we may want to save the last line read. ** ** @param [u] buff [AjPFilebuff] File buffer ** @param [r] lines [ajint] Number of lines to retain. -1 deletes everything. ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ void ajFilebuffClear(AjPFilebuff buff, ajint lines) { ajint i = 0; AjPFilebufflist list; AjPFilebufflist next; ajint first; ajint ifree = 0; ajDebug("ajFilebuffClear (%d) Nobuff: %B size: %u\n", lines, buff->Nobuff, buff->Size); /*FilebuffTraceFull(buff, buff->Size, 100);*/ if(!buff) return; if(!buff->File) return; if(lines < 0) first = buff->Size; else first = buff->Pos - lines; if(first < 0) first = 0; /* nobuff, and all read */ if(buff->Nobuff && buff->Pos == buff->Size) /* delete any old saved line */ first = buff->Pos; list = buff->Lines; for(i=0; i < first; i++) { /* we save one line at a time */ next = list->Next; /* so keep a note of the next one for later */ /*ajDebug("Try to reuse %x size: %d use: %d\n", list->Line, ajStrGetRes(list->Line), ajStrGetUse(list->Line));*/ if(buff->Nobuff) { ajStrDel(&list->Line); AJFREE(list); /* deleted, kill the list item */ } else { if(ajStrDelStatic(&list->Line)) { /* move free line to the end */ /*ajDebug("can save to free list %x %d bytes\n", list->Line, ajStrGetRes(list->Line));*/ ifree++; /* just save the one line */ list->Next = NULL; if(!buff->Freelines) { /* start a new free list */ buff->Freelines = list; buff->Freelast = list; /*ajDebug("start list Free %x Freelast %x \n", buff->Freelines, buff->Freelast);*/ } else { /* append to free list */ buff->Freelast->Next = list; buff->Freelast = buff->Freelast->Next; /*ajDebug("append list Free %x Freelast %x \n", buff->Freelines, buff->Freelast);*/ } } else { ajDebug("ajStrDelReuse was false\n"); } } list = next; } buff->Pos = 0; buff->Size -= i; /* // if(!buff->Size) // { // ajDebug("size 0: Lines: %x Curr: %x Prev: %x Last: %x Free: " // "%x Freelast: %x\n", // buff->Lines, buff->Curr, buff->Prev, buff->Last, // buff->Freelines, buff->Freelast); // } */ buff->Lines = buff->Curr = list; /* // ajDebug("ajFilebuffClear '%F' (%d lines)\n" // " %b size: %d pos: %d removed %d lines add to free: %d\n", // buff->File, lines, buff->Nobuff, buff->Size, buff->Pos, i, ifree); */ /* ajFilebuffTrace(buff);*/ if(buff->Nobuff && !buff->Size && lines == 1) { /* unbuffered - can only save last line */ if(buff->Lines) ajFatal("Buffer error clearing unbuffered file " "in ajFilebuffClear\n"); buff->Lines = AJNEW0(buff->Last); ajStrAssignS(&buff->Last->Line, buff->File->Buff); buff->Curr = buff->Last; buff->Curr->Fpos = buff->Fpos; buff->Last->Next = NULL; buff->Pos = 0; buff->Size = 1; } return; } /* @func ajFilebuffClearStore ************************************************* ** ** Deletes processed lines from a file buffer. The buffer has a record ** (Pos) of the next unprocessed line in the buffer. ** ** Unbuffered files need special handling. The buffer can be turned off ** while it still contains data. If so, we have to carefully run it down. ** If this runs it to zero, we may want to save the last line read. ** ** @param [u] buff [AjPFilebuff] File buffer ** @param [r] lines [ajint] Number of lines to retain. -1 deletes everything. ** @param [r] lastline [const AjPStr] Last line of input. ** Used to count characters to be saved ** @param [r] dostore [AjBool] append if true ** @param [w] Pstore [AjPStr*] Caller's record of the processed lines ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ void ajFilebuffClearStore(AjPFilebuff buff, ajint lines, const AjPStr lastline, AjBool dostore, AjPStr *Pstore) { ajFilebuffClear(buff, lines); if(dostore && ajStrGetLen(lastline)) ajStrCutEnd(Pstore, ajStrGetLen(lastline)); return; } /* @func ajFilebuffFix ******************************************************** ** ** Resets the pointer and current record of a file buffer so the next ** read starts at the first buffered line. Fixes buffer size after the ** buffer has been edited. ** ** @param [u] buff [AjPFilebuff] File buffer ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ void ajFilebuffFix(AjPFilebuff buff) { AjPFilebufflist list; ajint i = 1; ajFilebuffReset(buff); buff->Pos = 0; buff->Curr = buff->Lines; list = buff->Lines; if(!list) { buff->Size = 0; return; } while(list->Next) { i++; list = list->Next; } if(i != buff->Size) ajDebug("ajFilebuffFix size was %d now %d\n", buff->Size, i); buff->Size=i; return; } /* @func ajFilebuffReset ****************************************************** ** ** Resets the pointer and current record of a file buffer so the next read ** starts at the first buffered line. ** ** @param [u] buff [AjPFilebuff] File buffer ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ void ajFilebuffReset(AjPFilebuff buff) { ajDebug("ajFilebuffReset '%F' size: %u\n", buff->File, buff->Size); buff->Pos = 0; buff->Curr = buff->Lines; buff->Prev = NULL; return; } /* @func ajFilebuffResetPos *************************************************** ** ** Resets the pointer and current record of a file buffer so the next read ** starts at the first buffered line. ** ** Also resets the file position to the last known read, to undo the ** damage done by (for example) ajseqabi functions. ** ** @param [u] buff [AjPFilebuff] File buffer ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ void ajFilebuffResetPos(AjPFilebuff buff) { ajDebug("ajFilebuffResetPos End: %B Fpos: %ld ftell: %ld\n", buff->File->End, buff->Fpos, ftell(buff->File->fp)); /*ajFilebuffTraceFull(buff, 10, 10);*/ buff->Pos = 0; buff->Curr = buff->Lines; if(!buff->File->End && (buff->File->fp != stdin)) ajFileSeek(buff->File, buff->File->Filepos, SEEK_SET); buff->File->Filepos = buff->Fpos; /*ajFilebuffTraceFull(buff,10,10);*/ return; } /* @func ajFilebuffResetStore ************************************************* ** ** Resets the pointer and current record of a file buffer so the next read ** starts at the first buffered line. ** ** @param [u] buff [AjPFilebuff] File buffer ** @param [r] dostore [AjBool] True if text is stored ** @param [w] Pstore [AjPStr*] Stored string cleared if store is true ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ void ajFilebuffResetStore(AjPFilebuff buff, AjBool dostore, AjPStr *Pstore) { ajFilebuffReset(buff); if(dostore) ajStrAssignClear(Pstore); return; } /* @funcstatic filebuffFreeClear ********************************************** ** ** Deletes freed lines from a file buffer. The free list is used to avoid ** reallocating space for new records and must be deleted as part of ** the destructor. ** ** @param [u] buff [AjPFilebuff] File buffer ** @return [void] ** ** @release 6.0.0 ** @@ ******************************************************************************/ static void filebuffFreeClear(AjPFilebuff buff) { AjPFilebufflist list; if(!buff) return; /*ajDebug("filebuffFreeClear %x\n", buff->Freelines);*/ while(buff->Freelines) { list = buff->Freelines; buff->Freelines = buff->Freelines->Next; ajStrDel(&list->Line); AJFREE(list); } return; } /* @func ajFilebuffSetBuffered ************************************************ ** ** Sets file to be buffered. If it already has buffered data, we have to ** first run down the buffer. ** ** @param [u] buff [AjPFilebuff] Buffered file object. ** @return [AjBool] ajTrue if the file was unbuffered before ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFilebuffSetBuffered(AjPFilebuff buff) { AjBool ret; if(!buff) return ajFalse; ret = buff->Nobuff; ajDebug("ajFilebuffSetBuffered %F buffsize: %d\n", buff->File, buff->Size); buff->Nobuff = ajFalse; return ret; } /* @func ajFilebuffSetUnbuffered ********************************************** ** ** Sets file to be unbuffered. If it already has buffered data, we have to ** first run down the buffer. ** ** @param [u] buff [AjPFilebuff] Buffered file object. ** @return [AjBool] ajTrue if the file was unbuffered before ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFilebuffSetUnbuffered(AjPFilebuff buff) { AjBool ret; if(!buff) return ajFalse; ret = buff->Nobuff; ajDebug("ajFilebuffSetUnbuffered '%F' fsize: %d\n", buff->File, buff->Size); buff->Nobuff = ajTrue; return ret; } /* @section Buffered file content modifiers *********************************** ** ** These functions use the buffer contents of a buffered file object ** and update them. ** ** @nam3rule Html Modify HTML tags in the buffer ** @nam4rule HtmlPre Reduce to a pre-formatted section if found in HTML ** @nam4rule HtmlNoheader Remove HTML header in the buffer ** @nam4rule HtmlStrip Remove all HTML tags in the buffer ** @nam3rule Load Add text to the buffer ** @nam4rule LoadAll Read all file lines into buffer ** @nam4rule Readurl Replace buffer with results from a URL ** ** @suffix C Character string text ** @suffix S String object text ** ** @argrule * buff [AjPFilebuff] Buffered file object ** @argrule C line [const char*] Line of text ** @argrule S line [const AjPStr] Line of text ** @argrule Readurl url [const AjPStr] URL to be read ** ** @valrule * [void] ** @valrule *Noheader [ajuint] HTTP error code ** @valrule *HtmlPre [AjBool] True if changed ** @valrule *Readurl [AjBool] True on success ** ** @fcategory modify ** ** @fdata [AjPFilebuff] ** ******************************************************************************/ /* @func ajFilebuffHtmlNoheader *********************************************** ** ** Processes data in the file buffer, removing HTML titles and ** decoding possible chunked input ** ** @param [u] buff [AjPFilebuff] Buffered file with data loaded ** in the buffer. ** @return [ajuint] HTTP error code ** ** @release 6.2.0 ** @@ ******************************************************************************/ ajuint ajFilebuffHtmlNoheader(AjPFilebuff buff) { AjPRegexp httpexp = NULL; AjPRegexp nullexp = NULL; AjPRegexp chunkexp = NULL; AjPRegexp hexexp = NULL; AjPRegexp redirexp = NULL; AjBool doChunk = ajFalse; ajint ichunk; ajint chunkSize; ajint iline; AjPStr saveLine = NULL; AjPStr hexstr = NULL; AjPStr codestr = NULL; AjPStr newurl = NULL; ajuint httpcode = 0; AjBool isheader = ajFalse; /* first take out the HTTP header (HTTP 1.0 onwards) */ if(!buff || !buff->Size) return 0; httpexp = ajRegCompC("^HTTP/\\S+\\s+(\\d+)"); nullexp = ajRegCompC("^\r?\n?$"); /*ajFilebuffTraceTitle(buff, "Before ajFilebuffHtmlNoheader");*/ ajDebug("ajFilebuffHtmlNoheader: First line [%d] '%S' \n", ajStrGetUse(buff->Curr->Line), buff->Curr->Line); if(ajRegExec(httpexp, buff->Curr->Line)) { isheader = ajTrue; ajRegSubI(httpexp, 1, &codestr); ajStrToUint(codestr, &httpcode); ajStrDel(&codestr); fileBuffLineDel(buff); } if(isheader) { /* * ajHttpRedirect() function should be called * before calling this function to follow redirections * rather than using http redirection support here */ if(httpcode >= 301 && httpcode <= 302) { redirexp = ajRegCompC("^Location: (\\S+)"); while(buff->Pos < buff->Size && !ajRegExec(nullexp, buff->Curr->Line)) { /* to empty line */ if(ajRegExec(redirexp, buff->Curr->Line)) { ajRegSubI(redirexp, 1, &newurl); ajRegFree(&httpexp); ajRegFree(&nullexp); ajRegFree(&redirexp); ajFilebuffClear(buff, -1); ajFileClose(&buff->File); ajFilebuffLoadReadurl(buff, newurl); ajStrDel(&newurl); return ajFilebuffHtmlNoheader(buff); } fileBuffLineDel(buff); } /* blank line after header */ fileBuffLineDel(buff); ajRegFree(&redirexp); } else { /* HTTP header processing */ if(httpcode >= 300) { ajWarn("HTTP code %u", httpcode); ajRegFree(&httpexp); ajRegFree(&nullexp); return httpcode; } chunkexp = ajRegCompC("^Transfer-Encoding: +chunked"); while(buff->Pos < buff->Size && !ajRegExec(nullexp, buff->Curr->Line)) { if(ajRegExec(chunkexp, buff->Curr->Line)) { ajDebug("Chunk encoding: %S", buff->Curr->Line); /* chunked - see later */ doChunk = ajTrue; } fileBuffLineDel(buff); } /* blank line after header */ fileBuffLineDel(buff); ajRegFree(&chunkexp); } } if(doChunk) { /*ajFilebuffTraceFull(buff, 999999, 0);*/ hexexp = ajRegCompC("^([0-9a-fA-F]+) *\r?\n?$"); if(!ajRegExec(hexexp, buff->Curr->Line)) { ajFatal("Bad chunk data from HTTP, expect chunk size got '%S'", buff->Curr->Line); } ajRegSubI(hexexp, 1, &hexstr); ajStrToHex(hexstr, &chunkSize); ajDebug("chunkSize hex:%x %d\n", chunkSize, chunkSize); fileBuffLineDel(buff); /* chunk size */ ichunk = 0; iline = 0; while(chunkSize && buff->Curr) { iline++; /* get the chunk size - zero is the end */ /* process the chunk */ ichunk += ajStrGetLen(buff->Curr->Line); /*ajDebug("++input line [%d] ichunk=%d:%d %d:%S", iline, ichunk, chunkSize, ajStrGetLen(buff->Curr->Line), buff->Curr->Line);*/ if(ichunk >= chunkSize) /* end of chunk */ { if(ichunk == chunkSize) { /* end-of-chunk at end-of-line */ fileBuffLineNext(buff); ajStrAssignClear(&saveLine); /*ajDebug("end-of-chunk at end-of-line: '%S'\n", saveLine);*/ } else { /* end-of-chunk in mid-line, patch up the input */ ajDebug("end-of-chunk in mid-line, %d:%d have input size:" " %d\n", ichunk, chunkSize, ajStrGetLen(buff->Curr->Line)); ajStrAssignSubS(&saveLine, buff->Curr->Line, 0, -(ichunk-chunkSize+1)); ajStrKeepRange(&buff->Curr->Line, -(ichunk-chunkSize), -1); } /* skip a blank line */ if(!ajRegExec(nullexp, buff->Curr->Line)) { ajFilebuffTraceTitle(buff, "Blank line not found"); ajFatal("Bad chunk data from HTTP, expect blank line" " got '%S'", buff->Curr->Line); } fileBuffLineDel(buff); /** read the next chunk size */ if(!ajRegExec(hexexp, buff->Curr->Line)) { ajFilebuffTraceTitle(buff, "Chunk size not found"); ajFatal("Bad chunk data from HTTP, expect chunk size " "got '%S'", buff->Curr->Line); } ajRegSubI(hexexp, 1, &hexstr); ajStrToHex(hexstr, &chunkSize); ichunk = 0; fileBuffLineDel(buff); } if(saveLine) { if(ajStrGetLen(saveLine)) { ichunk = ajStrGetLen(buff->Curr->Line); ajDebug("curr-line/chunk: '%S'\n", buff->Curr->Line); /* preserve the line split by chunk size */ ajStrInsertS(&buff->Curr->Line, 0, saveLine); if(ichunk < chunkSize) { /* process the next line */ fileBuffLineNext(buff); /* after restored line */ } else { /* we already have the whole chunk! */ ichunk -= ajStrGetLen(buff->Curr->Line); } } else { /* just a chunk size, skip */ if(buff->Curr && chunkSize) { /*fileBuffLineDel(buff);*/ } else if (chunkSize)/* final non-zero chunk size */ { fileBuffLineDel(buff); } } ajStrDel(&saveLine); } else { /* next line */ fileBuffLineNext(buff); } } ajFilebuffFix(buff); /*ajFilebuffTraceFull(buff, 999999, 0);*/ ajStrDel(&hexstr); /*ajFilebuffTraceTitle(buff, "Chunks resolved");*/ } ajFilebuffReset(buff); ajRegFree(&httpexp); ajRegFree(&nullexp); ajRegFree(&hexexp); return 0; } /* @func ajFilebuffHtmlPre **************************************************** ** ** If we only have one pre-formatted section in HTML, that is all we keep. ** ** @param [u] buff [AjPFilebuff] buffer ** @return [AjBool] ajTrue=cleaned ajFalse=unchanged ** ** @release 6.0.0 ** @@ ******************************************************************************/ AjBool ajFilebuffHtmlPre(AjPFilebuff buff) { AjPFilebufflist lptr = NULL; AjPFilebufflist tptr = NULL; AjPRegexp preexp = NULL; AjPRegexp endexp = NULL; ajint ifound = 0; lptr = buff->Curr; preexp = ajRegCompC("<[Pp][Rr][Ee]>"); lptr=buff->Curr; ajDebug("ajFilebuffHtmlPre testing for
 line(s)\n");

    while(lptr)
    {
        if(ajRegExec(preexp, lptr->Line))
            ifound++;

        lptr = lptr->Next;
    }

    if(!ifound)
    {
        ajRegFree(&preexp);

        return ajFalse;
    }

    if (ifound > 1)
    {
        ajRegFree(&preexp);

        return ajFalse;
    }

    lptr=buff->Curr;


    while(lptr && !ajRegExec(preexp, lptr->Line))
    {
        tptr = lptr;
        lptr = lptr->Next;
        ajStrDel(&tptr->Line);
        AJFREE(tptr);
        buff->Size--;
    }

    buff->Lines = buff->Curr = lptr;
    ajRegPost(preexp, &lptr->Line);
    ajRegFree(&preexp);

    endexp = ajRegCompC("");

    while(lptr && !ajRegExec(endexp,lptr->Line))
    {
        lptr    = lptr->Next;
    }

    ajRegPre(endexp, &lptr->Line);
    buff->Last = lptr;
    lptr = lptr->Next;
    ajRegFree(&endexp);

    while(lptr)
    {
        tptr = lptr;
        lptr = lptr->Next;
        ajStrDel(&tptr->Line);
        AJFREE(tptr);
        buff->Size--;
    }

    buff->Last->Next = NULL;
    ajFilebuffReset(buff);
    ajFilebuffTraceTitle(buff, "ajFilebuffHtmlPre completed");

    return ajTrue;
}




/* @func ajFilebuffHtmlStrip **************************************************
**
** Processes data in the file buffer, removing HTML tokens between
** angle brackets, plus any TITLE. This seems to be enough to make HTML
** output readable.
**
** @param [u] buff [AjPFilebuff] Buffered file with data loaded
**                                     in the buffer.
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajFilebuffHtmlStrip(AjPFilebuff buff)
{
    AjPRegexp tagexp   = NULL;
    AjPRegexp fullexp  = NULL;
    AjPRegexp httpexp  = NULL;
    AjPRegexp nullexp  = NULL;
    AjPRegexp chunkexp = NULL;
    AjPRegexp hexexp   = NULL;
    AjPRegexp ncbiexp  = NULL;
    AjPRegexp ncbiexp2 = NULL;
    AjPRegexp srsdbexp = NULL;

    AjPStr s1 = NULL;
    AjPStr s2 = NULL;
    AjPStr s3 = NULL;
    ajint i;
    AjBool doChunk = ajFalse;
    ajint ichunk;
    ajint chunkSize;
    ajint iline;
    AjPStr saveLine = NULL;
    AjPStr hexstr   = NULL;

    /* first take out the HTTP header (HTTP 1.0 onwards) */
    if(!buff->Size)
        return;

    tagexp   = ajRegCompC("^(.*)(<[!/A-Za-z][^>]*>)(.*)$");
    fullexp  = ajRegCompC("^(.*)(<([A-Za-z]+)[^>]*>.*)(.*)$");
    httpexp  = ajRegCompC("^HTTP/");
    nullexp  = ajRegCompC("^\r?\n?$");
    chunkexp = ajRegCompC("^Transfer-Encoding: +chunked");
    hexexp   = ajRegCompC("^([0-9a-fA-F]+) *\r?\n?$");
    ncbiexp  = ajRegCompC("^Entrez Reports\r?\n$");
    ncbiexp2 = ajRegCompC("^----------------\r?\n$");
    srsdbexp = ajRegCompC("^([A-Za-z0-9_-]+)(:)([A-Za-z0-9_-]+)");

/*    ajFilebuffTraceTitle(buff, "Before ajFilebuffHtmlStrip");*/

    i = 0;

    /* ajDebug("First line [%d] '%S' \n",
       ajStrGetUse(buff->Curr->Line), buff->Curr->Line);*/

    if(ajRegExec(httpexp, buff->Curr->Line))
    {
        /* ^HTTP  header processing */
        while(buff->Pos < buff->Size &&
              !ajRegExec(nullexp, buff->Curr->Line))
        {
            /* to empty line */
            if(ajRegExec(chunkexp, buff->Curr->Line))
            {
                /*ajDebug("Chunk encoding: %S", buff->Curr->Line);*/
                /* chunked - see later */
                doChunk = ajTrue;
            }
            fileBuffLineDel(buff);
        }

        /* blank line after header */
        fileBuffLineDel(buff);
    }

    if(doChunk)
    {
        /*ajFilebuffTraceFull(buff, 999999, 0);*/

        if(!ajRegExec(hexexp, buff->Curr->Line))
        {
            ajFatal("Bad chunk data from HTTP, expect chunk size got '%S'",
                    buff->Curr->Line);
        }

        ajRegSubI(hexexp, 1, &hexstr);
        ajStrToHex(hexstr, &chunkSize);

        /*ajDebug("chunkSize hex:%x %d\n", chunkSize, chunkSize);*/
        fileBuffLineDel(buff);  /* chunk size */

        ichunk = 0;
        iline = 0;

        while(chunkSize && buff->Curr)
        {
            iline++;
            /* get the chunk size - zero is the end */
            /* process the chunk */
            ichunk += ajStrGetLen(buff->Curr->Line);

            /*ajDebug("++input line [%d] ichunk=%d:%d %d:%S",
              iline, ichunk, chunkSize,
              ajStrGetLen(buff->Curr->Line), buff->Curr->Line);*/

            if(ichunk >= chunkSize)     /* end of chunk */
            {
                if(ichunk == chunkSize)
                {
                    /* end-of-chunk at end-of-line */
                    fileBuffLineNext(buff);
                    ajStrAssignClear(&saveLine);
                    /*ajDebug("end-of-chunk at end-of-line: '%S'\n",
                      saveLine); */
                }
                else
                {
                    /* end-of-chunk in mid-line, patch up the input */
                    /*ajDebug("end-of-chunk in mid-line, %d:%d have input: "
                      "%d '%S'\n",
                      ichunk, chunkSize,
                      ajStrGetLen(buff->Curr->Line),
                      buff->Curr->Line);*/
                    ajStrAssignSubS(&saveLine, buff->Curr->Line, 0,
                                    -(ichunk-chunkSize+1));
                    ajStrKeepRange(&buff->Curr->Line, -(ichunk-chunkSize), -1);
                }

                /* skip a blank line */

                if(!ajRegExec(nullexp, buff->Curr->Line))
                {
                    ajFilebuffTraceTitle(buff, "Blank line not found");
                    ajFatal("Bad chunk data from HTTP, expect blank line"
                            " got '%S'", buff->Curr->Line);
                }

                fileBuffLineDel(buff);

                /** read the next chunk size */

                if(!ajRegExec(hexexp, buff->Curr->Line))
                {
                    ajFilebuffTraceTitle(buff, "Chunk size not found");
                    ajFatal("Bad chunk data from HTTP, expect chunk size "
                            "got '%S'",
                            buff->Curr->Line);
                }

                ajRegSubI(hexexp, 1, &hexstr);
                ajStrToHex(hexstr, &chunkSize);
                ichunk = 0;
                fileBuffLineDel(buff);
            }

            if(saveLine)
            {
                if(ajStrGetLen(saveLine))
                {
                    ichunk = ajStrGetLen(buff->Curr->Line);
                    /* preserve the line split by chunk size */
                    ajStrInsertS(&buff->Curr->Line, 0, saveLine);

                    if(ichunk < chunkSize)
                    {
                        /* process the next line */
                        fileBuffLineNext(buff); /* after restored line */
                    }
                    else
                    {
                        /* we already have the whole chunk! */
                        ichunk -= ajStrGetLen(buff->Curr->Line);
                    }
                }
                else
                {
                    /* just a chunk size, skip */
                    if(buff->Curr && chunkSize)
                    {
                        /*fileBuffLineDel(buff);*/
                    }
                    else if (chunkSize)/* final non-zero chunk size */
                    {
                        fileBuffLineDel(buff);
                    }
                }

                ajStrDel(&saveLine);
            }
            else
            {
                /* next line */
                fileBuffLineNext(buff);
            }
        }

        ajFilebuffFix(buff);
        /*ajFilebuffTraceFull(buff, 999999, 0);*/
        ajStrDel(&hexstr);
        /*ajFilebuffTraceTitle(buff, "Chunks resolved");*/
    }

    ajFilebuffReset(buff);

    /*
    ** Now we have a clean single file to process
    */

    /*ajFilebuffTraceTitle(buff, "About to preprocess");*/
    ajFilebuffHtmlPre(buff);

    while(buff->Curr)
    {
        if(ajRegExec(ncbiexp, buff->Curr->Line))
            ajStrAssignC(&buff->Curr->Line, "\n");

        if(ajRegExec(ncbiexp2, buff->Curr->Line))
            ajStrAssignC(&buff->Curr->Line, "\n");

        while(ajRegExec(fullexp, buff->Curr->Line))
        {
            ajRegSubI(fullexp, 1, &s1);
            ajRegSubI(fullexp, 2, &s2);
            ajRegSubI(fullexp, 4, &s3);
            ajFmtPrintS(&buff->Curr->Line, "%S%S", s1, s3);
        }

        while(ajRegExec(tagexp, buff->Curr->Line))
        {
            ajRegSubI(tagexp, 1, &s1);
            ajRegSubI(tagexp, 2, &s2);
            ajRegSubI(tagexp, 3, &s3);
            ajFmtPrintS(&buff->Curr->Line, "%S%S", s1, s3);
        }

        if(ajRegExec(srsdbexp, buff->Curr->Line))
        {
            ajRegSubI(srsdbexp,1,&s1);
            ajRegSubI(srsdbexp,2,&s2);
            ajRegSubI(srsdbexp,3,&s3);
            fileBuffLineDel(buff);
            ++i;
            continue;
        }

        if(ajRegExec(nullexp, buff->Curr->Line))
        {
            /* allow for newline */
            fileBuffLineDel(buff);
        }
        else
            fileBuffLineNext(buff);

        i++;
    }

    ajFilebuffReset(buff);

    ajStrDel(&s1);
    ajStrDel(&s2);
    ajStrDel(&s3);

    /* free the regular expressions - we expect to use them once only */

    ajRegFree(&tagexp);
    ajRegFree(&fullexp);
    ajRegFree(&httpexp);
    ajRegFree(&nullexp);
    ajRegFree(&chunkexp);
    ajRegFree(&hexexp);
    ajRegFree(&ncbiexp);
    ajRegFree(&ncbiexp2);
    ajRegFree(&srsdbexp);

    /*ajFilebuffTraceTitle(buff, "After ajFilebuffHtmlStrip");*/

    return;
}




/* @func ajFilebuffLoadC ******************************************************
**
** Adds a line to the buffer.
**
** Intended for cases where the file data must be pre-processed before
** being seen by the sequence reading routines. The first case was
** for stripping HTML tags after reading via HTTP.
**
** @param [u] buff [AjPFilebuff] Buffered file.
** @param [r] line [const char*] Line of input.
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajFilebuffLoadC(AjPFilebuff buff, const char* line)
{
    if(!buff->Lines)
        buff->Curr = buff->Lines = AJNEW0(buff->Last);
    else
        buff->Last = AJNEW0(buff->Last->Next);

    ajStrAssignC(&buff->Last->Line,line);
    buff->Last->Next = NULL;
    buff->Size++;

    return;
}




/* @func ajFilebuffLoadS ******************************************************
**
** Adds a copy of a line to the buffer.
**
** Intended for cases where the file data must be pre processed before
** being seen by the sequence reading routines. The first case was
** for stripping HTML tags after reading via HTTP.
**
** @param [u] buff [AjPFilebuff] Buffered file.
** @param [r] line [const AjPStr] Line of input.
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajFilebuffLoadS(AjPFilebuff buff, const AjPStr line)
{
    if(!buff->Lines)
        buff->Curr = buff->Lines = AJNEW0(buff->Last);
    else
        buff->Last = AJNEW0(buff->Last->Next);

    ajStrAssignS(&buff->Last->Line,line);
    buff->Last->Next = NULL;
    buff->Size++;

    return;
}




/* @func ajFilebuffLoadAll ****************************************************
**
** Reads all input lines from a file into the buffer.
**
** Intended for cases where the file data must be pre-processed before
** being seen by the sequence reading routines. The first case was
** for stripping HTML tags after reading via HTTP.
**
** @param [u] buff [AjPFilebuff] Buffered file.
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajFilebuffLoadAll(AjPFilebuff buff)
{
    AjPStr rdline = NULL;
    AjBool status = ajTrue;

    while(status)
    {
        status = ajBuffreadLine(buff, &rdline);
        /*ajDebug("read: <%S>\n", rdline);*/
    }

    ajFilebuffReset(buff);
    ajStrDel(&rdline);

    /*ajFilebuffTrace(buff);*/

    return;
}




/* @func ajFilebuffLoadReadurl ************************************************
**
** Reads from a URL and loads all input lines into the buffer.
**
** @param [u] buff [AjPFilebuff] Buffered file.
** @param [r] url [const AjPStr] URL
** @return [AjBool] True on success
**
** @release 6.4.0
** @@
******************************************************************************/

AjBool ajFilebuffLoadReadurl(AjPFilebuff buff, const AjPStr url)
{
    AjPStr host      = NULL;
    AjPStr urlget    = NULL;
    AjPStr get       = NULL;
    AjPStr proxyName = NULL;            /* host for proxy access.*/
    AjPStr proxyAuth = NULL;
    AjPStr proxyCred = NULL;
    AjPStr httpver   = NULL;          /* HTTP version 1.0, 1.1, ... */
    ajint iport = 80;
    ajint proxyPort;
    FILE *fp;
    AjOSysTimeout timo;
    AjOSysSocket sock;

    AjPStr dbproxy = ajStrNew();

    ajHttpUrlDeconstruct(url, &iport, &host, &urlget);

    ajHttpGetVersion(NULL, &httpver);

    if(ajHttpGetProxyinfo(dbproxy, &proxyPort, &proxyName,
                          &proxyAuth, &proxyCred))
        ajFmtPrintS(&get, "GET http://%S:%d%S HTTP/%S\n",
                    host, iport, urlget, httpver);
    else
        ajFmtPrintS(&get, "GET %S HTTP/%S\n", urlget, httpver);

    if(ajStrGetLen(proxyName))
        fp = ajHttpOpenProxy(dbproxy, proxyName, proxyPort,
                             proxyAuth, proxyCred,
                             host, iport,
                             get, &sock);
    else
        fp = ajHttpOpen(NULL, host, iport, get, &sock);

    buff->File = ajFileNewFromCfile(fp);

    if(!buff)
    {
        ajDebug("socket buffer attach failed\n");
        ajErr("socket buffer attach failed for '%S'", url);

        return ajFalse;
    }

    ajDebug("Ready to read errno %d msg '%s'\n",
            errno, ajMessGetSysmessageC());

    timo.seconds = 180;
    ajSysTimeoutSet(&timo);

    ajFilebuffLoadAll(buff);

    ajSysTimeoutUnset(&timo);

    return ajTrue;
}




/* @section Buffered file operators *******************************************
**
** These functions use the contents of a file object but do not make
** any changes.
**
** @nam3rule Get Return an attribute value
** @nam4rule GetFile Return the file object
** @nam4rule GetFileptr Return the C file pointer
** @nam4rule GetFirst Return the first line in the buffer
** @nam3rule Is Tests a condition
** @nam4rule IsBuffered Tests whether file input is buffered
** @nam4rule IsEmpty Tests whether buffer is exhausted for all input files
** @nam4rule IsEnded Tests whether buffer is at end of current file and empty
** @nam4rule IsEof Tests whether buffer is at end of file
**
** @argrule * buff [const AjPFilebuff] Buffered file object
**
** @valrule GetFileptr [FILE*] C file pointer
** @valrule GetFile [AjPFile] File object
** @valrule GetFirst [const AjPStr] First line of buffer
** @valrule IsBuffered [AjBool] True if file is buffered
** @valrule IsEmpty [AjBool] True if buffer is empty
** @valrule IsEnded [AjBool] True if buffer is ended for this file
** @valrule IsEof [AjBool] True if buffer has read to end of this file
**
** @fcategory use
**
** @fdata [AjPFilebuff]
**
******************************************************************************/




/* @func ajFilebuffGetFile ****************************************************
**
** Returns the file object from a buffered file object.
**
** @param [r] buff [const AjPFilebuff] Buffered file.
** @return [AjPFile] File object.
**
** @release 6.0.0
** @@
******************************************************************************/

AjPFile ajFilebuffGetFile(const AjPFilebuff buff)
{
    if(!buff)
        return NULL;

    return buff->File;
}




/* @func ajFilebuffGetFileptr *************************************************
**
** Returns the C file pointer for an open buffered file.
**
** @param [r] buff [const AjPFilebuff] Buffered file.
** @return [FILE*] C file pointer for the file.
**
** @release 6.0.0
** @@
******************************************************************************/

FILE* ajFilebuffGetFileptr(const AjPFilebuff buff)
{
    if(!buff)
        return NULL;

    return buff->File->fp;
}




/* @func ajFilebuffGetFirst ***************************************************
**
** Returns the first line of a file buffer
**
** @param [r] buff [const AjPFilebuff] File buffer
** @return [const AjPStr] First line
**
**
** @release 6.4.0
******************************************************************************/

const AjPStr ajFilebuffGetFirst(const AjPFilebuff buff)
{
    if(!buff)
        return NULL;

    if(!buff->Lines)
        return NULL;

    return buff->Lines->Line;
}




/* @func ajFilebuffIsBuffered *************************************************
**
** Tests whether an input file is buffered.
**
** @param [r] buff [const AjPFilebuff] Buffered file object.
** @return [AjBool] ajTrue if the file was unbuffered before
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilebuffIsBuffered(const AjPFilebuff buff)
{
    if(!buff)
        return ajFalse;

    if(buff->Nobuff)
        return ajFalse;

    return ajTrue;
}




/* @func ajFilebuffIsEmpty ****************************************************
**
** Tests whether a file buffer is empty, including testing for a
** possible next file
**
** @param [r] buff [const AjPFilebuff] Buffered file.
** @return [AjBool] ajTrue if the buffer is empty.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilebuffIsEmpty(const AjPFilebuff buff)
{
    /*ajDebug("ajFilebuffIsEmpty Size: %d Pos: %d End: %b Handle: %d "
      "Fp: %x List; %d\n",
      buff->Size, buff->Pos, buff->File->End, buff->File->Handle,
      buff->File->fp, ajListstrGetLength(buff->File->List));*/

    if(!buff)
        return ajTrue;

    if(!buff->File)
        return ajTrue;

    if(buff->Pos < buff->Size)
        return ajFalse;

    /* not open */
    if(!buff->File->fp || !buff->File->Handle)
        return ajTrue;

    if(buff->File->End && !ajListstrGetLength(buff->File->List))
        /* EOF and done */
        return ajTrue;

    ajDebug("ajFilebuffIsEmpty false\n");

    return ajFalse;
}




/* @func ajFilebuffIsEnded ****************************************************
**
** Tests whether the current file is exhausted.
** This means end of file is reached and the buffer is empty.
**
** It is possible that further files are defined in the input list
** (see ajFilebuffIsEmpty)
**
** @param [r] buff [const AjPFilebuff] File buffer
** @return [AjBool] ajTrue if we already set end-of-file
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilebuffIsEnded(const AjPFilebuff buff)
{
    if(!buff)
        return ajTrue;

    ajDebug("ajFilebuffIsEnded End: %B Size: %d\n",
            buff->File->End, buff->Size);

    /* not reached EOF yet */
    if(!buff->File->End)
        return ajFalse;

    /* Something in the buffer*/
    if(buff->Size != 0)
        return ajFalse;

    return ajTrue;
}




/* @func ajFilebuffIsEof ******************************************************
**
** Tests whether we have reached end of file already
**
** @param [r] buff [const AjPFilebuff] File buffer
** @return [AjBool] ajTrue if we already set end-of-file
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilebuffIsEof(const AjPFilebuff buff)
{
    if(!buff)
        return ajTrue;

    if(!buff->File)
        return ajTrue;

    return buff->File->End;
}




/* @section Buffered file debug ***********************************************
**
** Report file object contents for debugging
**
**
** @nam3rule Trace    Print report to debug file (if any)
** @nam4rule TraceFull    Print full contents report to debug file (if any)
** @nam4rule TraceTitle    Print report with a set title
**
** @argrule * buff [const AjPFilebuff]
** @argrule Full nlines [size_t] Maximum number of lines to report
** @argrule Full nfree [size_t] Maximum number of free lines to report
** @argrule Title title [const char*] Report title
**
** @valrule * [void]
**
** @fcategory misc
** @fdata [AjPFilebuff]
**
******************************************************************************/




/* @func ajFilebuffTrace ******************************************************
**
** Writes debug messages to indicate the contents of a buffered file.
**
** @param [r] buff [const AjPFilebuff] Buffered file.
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajFilebuffTrace(const AjPFilebuff buff)
{
    AjPFilebufflist test;
    ajint i = 0;
    ajint j = -1;

    ajDebug("Trace buffer file '%S'\n"
            "             Pos: %d Size: %d FreeSize: %d Fpos: %Ld End: %b\n",
            buff->File->Name, buff->Pos, buff->Size,buff->FreeSize,
            buff->Fpos, buff->File->End);

    if(buff->Size)
    {
        ajDebug(" Lines: %u\n", buff->Size);

        if(buff->Curr)
            ajDebug("  Curr: %8Ld %x %x %u <%S>\n",
                    buff->Curr->Fpos,
                    buff->Curr->Line, buff->Curr->Next,
                    ajStrGetLen(buff->Curr->Line),
                    buff->Curr->Line);
        else
            ajDebug("  Curr: \n");

        if(buff->Lines)
            ajDebug("  From: %8Ld %x %x %u <%S>\n",
                    buff->Lines->Fpos,
                    buff->Lines->Line, buff->Lines->Next,
                    ajStrGetLen(buff->Lines->Line),
                    buff->Lines->Line);
        else
            ajDebug("  From: \n");

        if(buff->Last)
            ajDebug("    To: %8Ld %x %x %u <%S>\n",
                    buff->Last->Fpos,
                    buff->Last->Line, buff->Last->Next,
                    ajStrGetLen(buff->Last->Line),
                    buff->Last->Line);
        else
            ajDebug("    To: \n");
    }

    if(buff->Freelines)
        for(test = buff->Freelines; test; test=test->Next)
        {
            i++;

            if(test == buff->Freelast)
                j = i;
        }

    ajDebug(" Free: %d Last: %d\n", i, j);

    return;
}




/* @func ajFilebuffTraceFull **************************************************
**
** Writes debug messages to show the full contents of a buffered file.
**
** @param [r] buff [const AjPFilebuff] Buffered file.
** @param [r] nlines [size_t] Maximum number of lines to trace.
** @param [r] nfree [size_t] Maximum number of free lines to trace.
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajFilebuffTraceFull(const AjPFilebuff buff, size_t nlines,
                         size_t nfree)
{
    ajint i;
    AjPFilebufflist line;
    AjBool last = ajFalse;

    ajDebug("Trace buffer file '%S' End: %B\n"
            "             Pos: %d Size: %d Nobuff: %B Fpos: %Ld\n",
            buff->File->Name, buff->File->End,
            buff->Pos, buff->Size, buff->Nobuff, buff->Fpos);

    line = buff->Lines;

    for(i=1; line && (i <= (ajint)nlines); i++)
    {
        if(line == buff->Curr)
            ajDebug("*Line %x %d: %5d %5d <%-20S>\n",
                    line->Line, i,
                    ajStrGetLen(line->Line), strlen(ajStrGetPtr(line->Line)),
                    line->Line);
        else
            ajDebug(" Line %x %d: %5d %5d <%-20S>\n",
                    line->Line, i,
                    ajStrGetLen(line->Line), strlen(ajStrGetPtr(line->Line)),
                    line->Line);
        line = line->Next;
    }

    line = buff->Freelines;

    for(i=1; line && (i <= (ajint)nfree);  i++)
    {
        if(line == buff->Freelast)
            last = ajTrue;

        ajDebug(" Free %x %d: %d bytes %B\n",
                line->Line, i, ajStrGetRes(line->Line), last);
        line = line->Next;
    }

    return;
}




/* @func ajFilebuffTraceTitle *************************************************
**
** Writes the full contents of a buffered file to the debug file
**
** @param [r] buff [const AjPFilebuff] Buffered file.
** @param [r] title [const char*] Report title
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajFilebuffTraceTitle(const AjPFilebuff buff, const char* title)
{
    ajint i;
    AjPFilebufflist line;
    ajint last = 0;

    ajDebug("=== File Buffer: %s ===\n", title);
    line = buff->Lines;

    for(i=1; line; i++)
    {
        ajStrAssignS(&fileTmpStr, line->Line);
        ajStrRemoveLastNewline(&fileTmpStr);

        if(line == buff->Curr)
            ajDebug("* %x %S\n", line->Line, fileTmpStr);
        else
            ajDebug("  %x %S\n", line->Line, fileTmpStr);

        line = line->Next;
    }

    line = buff->Freelines;

    for(i=1; line;  i++)
    {
        if(line == buff->Freelast)
            last = i;

        if(line == buff->Freelast)
            ajDebug("F %x %S\n", line->Line, fileTmpStr);
        else
            ajDebug("f %x %S\n", line->Line, fileTmpStr);

        line = line->Next;
    }

    if (!last)
        last = i;
    ajDebug("=== end of file, free list %d lines ===\n", last);

    return;
}




/* @funcstatic fileBuffLineDel ************************************************
**
** Delete a line from a buffer.
**
** @param [u] buff [AjPFilebuff] File buffer
** @return [void]
**
** @release 2.5.0
******************************************************************************/

static void fileBuffLineDel(AjPFilebuff buff)
{
    AjPFilebufflist saveprev;

    if(!buff->Curr)
        return;

    /*ajDebug("fileBuffLineDel removing line [%d%s%d], '%S' len %d\n",
      buff->Pos, SLASH_STRING, buff->Size, buff->Curr->Line,
      ajStrGetLen(buff->Curr->Line));*/

    /* first line */
    if(!buff->Prev)
    {
        buff->Prev = buff->Lines;
        buff->Curr = buff->Lines = buff->Lines->Next;
        ajStrDel(&buff->Prev->Line);
        AJFREE(buff->Prev);
        --buff->Size;

        return;
    }

    /* last line */
    if(!buff->Curr->Next)
    {
        saveprev = buff->Prev;
        buff->Prev = buff->Lines;

        while(buff->Prev && buff->Prev->Next != saveprev)
            buff->Prev = buff->Prev->Next;

        saveprev->Next = NULL;

        ajStrDel(&buff->Curr->Line);
        AJFREE(buff->Curr);
        buff->Curr = NULL;
        buff->Last = saveprev;
        --buff->Size;
        buff->Pos = buff->Size;

        return;
    }

    buff->Prev->Next = buff->Curr->Next;
    ajStrDel(&buff->Curr->Line);
    AJFREE(buff->Curr);
    buff->Curr = buff->Prev->Next;
    --buff->Size;

    return;
}




/* @funcstatic fileBuffLineNext ***********************************************
**
** Steps the Curr pointer to the next line in a buffer.
**
** Not for use when reading from a file. This steps through the buffer
**
** @param [u] buff [AjPFilebuff] File buffer
** @return [AjBool] ajTrue if there was another line
**
** @release 2.5.0
******************************************************************************/

static AjBool fileBuffLineNext(AjPFilebuff buff)
{
    if(buff->Pos < buff->Size)
    {
        buff->Prev = buff->Curr;
        buff->Curr = buff->Curr->Next;
        buff->Pos++;

        return ajTrue;
    }

    return ajFalse;
}




/* @datasection [AjPOutfile] Output file object *******************************
**
** Function is for manipulating output files
** and their attributes.
**
** @nam2rule Outfile
******************************************************************************/




/* @section Outfile constructors **********************************************
**
** All constructors return a new open file by pointer. It is the responsibility
** of the user to first destroy any previous file pointer. The target pointer
** does not need to be initialised to NULL, but it is good programming practice
** to do so anyway.
**
** To replace or reuse an existing file, see instead
** the {File Assignments} and {File Modifiers} functions.
**
** The range of constructors is provided to allow flexibility in how
** applications can open files to read various kinds of data.
**
** @nam3rule New Output file object constructor
** @suffix Name File name
** @suffix S File name as a string
**
** @argrule S name [const AjPStr] Output filename
**
** @valrule * [AjPOutfile] Output file
**
** @fcategory new
**
** @fdata [AjPOutfile]
**
******************************************************************************/




/* @func ajOutfileNewNameS ****************************************************
**
** Creates a new formatted output file object with a specified name.
**
** 'stdout' and 'stderr' are special names for standard output and
** standard error respectively.
**
** @param [r] name [const AjPStr] File name.
** @return [AjPOutfile] New output file object.
**
** @release 6.0.0
** @@
******************************************************************************/

AjPOutfile ajOutfileNewNameS(const AjPStr name)
{
    AjPOutfile thys;
    ajlong ipos;
    AjPStr tmpname = NULL;

    AJNEW0(thys);

    ipos = ajStrFindC(name, "::");
    if(ipos > 0)
    {
        ajStrAssignSubS(&thys->Formatstr, name, 0, ipos-1);
        ajStrAssignSubS(&tmpname, name, ipos+2, -1);
        thys->File = ajFileNewOutNameS(tmpname);
        ajStrDel(&tmpname);
    }
    else
    {
        thys->File = ajFileNewOutNameS(name);
    }

    if(!thys->File)
        return NULL;

    return thys;
}




/* @section Outfile destructors ***********************************************
**
** Destruction is achieved by closing the file.
**
** Unlike ANSI C, there are tests to ensure a file is not closed twice.
**
** @nam3rule Close Close file and delete output file object
**
** @argrule * Pfile [AjPOutfile*] Output file object pointer
**
** @valrule * [void]
**
** @fcategory delete
**
** @fdata [AjPOutfile]
**
******************************************************************************/




/* @func ajOutfileClose *******************************************************
**
** Close and free an outfile object.
**
** @param [d] Pfile [AjPOutfile*] Output file.
** @return [void]
**
** @release 2.9.0
** @@
******************************************************************************/

void ajOutfileClose(AjPOutfile* Pfile)
{
    AjPOutfile thys;

    thys = Pfile ? *Pfile : 0;

    if(!Pfile)
        return;

    if(!*Pfile)
        return;

    fileClose(thys->File);
    AJFREE(thys->File);
    ajStrDel(&thys->Type);
    ajStrDel(&thys->Formatstr);
    AJFREE(*Pfile);

    return;
}




/* @section Outfile casts *****************************************************
**
** These functions examine the contents of an outfile object and return some
** derived information. Some of them provide access to the internal
** components of a file object. They are provided for programming convenience
** but should be used with caution.
**
** @nam3rule Get Return an output file attribute value
** @nam4rule GetFile Return the file object
** @nam4rule GetFileptr Return the C file pointer
** @nam4rule GetFormat Return the output format name
** @nam4rule GetFormatindex Return the output format index number
**
** @argrule * file [const AjPOutfile] Output file object
**
** @valrule GetFile [AjPFile] File object
** @valrule GetFileptr [FILE*] C file pointer
** @valrule GetFormat [const AjPStr] Filename as a string
** @valrule GetFormatindex [ajuint] Index in internal formats
**
** @fcategory cast
**
** @fdata [AjPOutfile]
**
******************************************************************************/




/* @func ajOutfileGetFile *****************************************************
**
** Returns the AjPFile for an AjPOutfile object
**
** @param [r] file [const AjPOutfile] Outfile object
** @return [AjPFile] AjPFile object
**
** @release 6.0.0
******************************************************************************/

AjPFile ajOutfileGetFile (const AjPOutfile file)
{
    return file->File;
}




/* @func ajOutfileGetFileptr **************************************************
**
** Returns the C FILE* for an AjPOutfile object
**
** @param [r] file [const AjPOutfile] Outfile object
** @return [FILE*] C file pointer
**
** @release 6.0.0
******************************************************************************/

FILE* ajOutfileGetFileptr (const AjPOutfile file)
{
    return file->File->fp;
}




/* @func ajOutfileGetFormat ***************************************************
**
** Returns the file format name for an AjPOutfile object
**
** @param [r] file [const AjPOutfile] Outfile object
** @return [const AjPStr] Format name
**
** @release 6.0.0
******************************************************************************/

const AjPStr ajOutfileGetFormat (const AjPOutfile file)
{
    return file->Formatstr;
}




/* @func ajOutfileGetFormatindex **********************************************
**
** Returns the file format name for an AjPOutfile object
**
** @param [r] file [const AjPOutfile] Outfile object
** @return [ajuint] Format name
**
** @release 6.4.0
******************************************************************************/

ajuint ajOutfileGetFormatindex (const AjPOutfile file)
{
    return file->Format;
}




/* @section Outfile modifiers *************************************************
**
** These functions modify an output file object and its asociated output file
**
** @nam3rule Reset
**
** @argrule * outf [AjPOutfile] Output file object
**
** @valrule * [void]
**
** @fcategory modify
**
** @fdata [AjPOutfile]
**
******************************************************************************/




/* @func ajOutfileReset *******************************************************
**
** Close and free an outfile object.
**
** @param [u] outf [AjPOutfile] Output file.
** @return [void]
**
** @release 6.5.0
** @@
******************************************************************************/

void ajOutfileReset(AjPOutfile outf)
{
    if(!outf)
        return;

    if(!outf->Cleanup)
        return;

    outf->Cleanup(outf);

    return;
}




/* @datasection [AjPStr] Filename functions ***********************************
**
** Functions operating on strings containing filenames
**
** @nam2rule Filename Operations on filename strings
**
******************************************************************************/




/* @section Filename tests ****************************************************
**
** Tests on filenames and searching the file system
**
** @nam3rule Exists tests whether file exists in current directory
** @nam4rule Dir Tests whether file exists and is a directory
** @nam4rule Exec Tests whether file exists and is executable by the user
** @nam4rule Read Tests whether file exists and is readable by the user
** @nam4rule Write Tests whether file exists and is writable by the user
** @nam3rule Get Return a filename element
** @nam4rule GetSize Return the file size
** @nam3rule Has Filename contains element
** @nam4rule Ext Filename extension
** @nam4rule Path Directory path
** @nam3rule Test Test a filename
** @nam4rule TestExclude Test a filename against a list of files to include.
**                       Any file not in the list is excluded.
** @nam4rule TestInclude Test a filename against a list of files to exclude
**                       Any file not in the list is included.
** @suffix Path Include the file path in tests
**
** @argrule * filename [const AjPStr] Filename string
** @argrule Stat mode [ajint] Mode
** @argrule Test exclude [const AjPStr] Excluded filenames wildcard
** @argrule Test include [const AjPStr] Included filenames wildcard
**
** @valrule Exists [AjBool] True if file exists with any requested access
** @valrule GetSize [ajlong] File size or -1 if not found
** @valrule Has [AjBool] True if file has attribute
** @valrule Test [AjBool] True if file passed test
** @fcategory use
**
** @fdata [AjPStr]
**
******************************************************************************/




/* @func ajFilenameExists *****************************************************
**
** Returns true if file exists
**
** @param [r] filename [const AjPStr] Filename.
** @return [AjBool] ajTrue on success
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameExists(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(ajStrMatchC(filename, "stdin"))
            return ajTrue;

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        return ajTrue;

    return ajFalse;
}




/* @func ajFilenameExistsDir **************************************************
**
** Returns true if file exists and is a directory
**
** @param [r] filename [const AjPStr] Filename.
** @return [AjBool] ajTrue on success
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameExistsDir(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

#ifdef WIN32
    if(!ajStrGetLen(filename))
        return ajTrue;
#endif /* WIN32 */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & AJ_FILE_DIR)
            return ajTrue;

    return ajFalse;
}




/* @func ajFilenameExistsExec *************************************************
**
** Returns true if file exists and is executable by the user
**
** @param [r] filename [const AjPStr] Filename.
** @return [AjBool] ajTrue on success
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameExistsExec(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & AJ_FILE_X)
            return ajTrue;

    return ajFalse;
}




/* @func ajFilenameExistsRead *************************************************
**
** Returns true if file exists and is readable by the user
**
** @param [r] filename [const AjPStr] Filename.
** @return [AjBool] ajTrue on success
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameExistsRead(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & AJ_FILE_R)
            return ajTrue;

    return ajFalse;
}




/* @func ajFilenameExistsWrite ************************************************
**
** Returns true if file exists and is writable by the user
**
** @param [r] filename [const AjPStr] Filename.
** @return [AjBool] ajTrue on success
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameExistsWrite(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & AJ_FILE_W)
            return ajTrue;

    return ajFalse;
}




/* @func ajFilenameGetSize ****************************************************
**
** Returns the length of a file
**
** @param [r] filename [const AjPStr] Filename.
** @return [ajlong] length or -1 if file doesn't exist
**
** @release 6.0.0
** @@
******************************************************************************/

ajlong ajFilenameGetSize(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

#if defined(AJ_IRIXLF)
    if(!stat64(ajStrGetPtr(filename), &buf))
        return (ajlong)buf.st_size;
#else /* !AJ_IRIXLF */
    if(!stat(ajStrGetPtr(filename), &buf))
        return (ajlong)buf.st_size;
#endif /* AJ_IRIXLF */

    return -1;
}




/* @func ajFilenameHasPath ****************************************************
**
** Tests whether a filename includes a directory specification.
**
** @param [r] filename [const AjPStr] File name.
** @return [AjBool] ajTrue if directory filename syntax was found
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameHasPath(const AjPStr filename)
{
    if(ajStrFindC(filename, SLASH_STRING) < 0)
        return ajFalse;

    return ajTrue;
}




/* @func ajFilenameTestExclude ************************************************
**
** Tests a filename against wildcard
** lists of file names to be included and excluded.
**
** The path (if any) is removed before checking.
**
** By default files are excluded. The inclusion list is used to select
** files, and the exclusion list is then used to exclude selected
** files again.
**
** @param [r] filename [const AjPStr] File to test
** @param [r] exclude [const AjPStr] List of wildcard names to exclude
** @param [r] include [const AjPStr] List of wildcard names to include
** @return [AjBool] ajTrue if the filename is accepted.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameTestExclude(const AjPStr filename,
                             const AjPStr exclude,
                             const AjPStr include)
{
    AjBool ret = ajFalse;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;

    ajDebug("ajFilenameTestExclude "
            "filename '%S' exclude '%S' include '%S'\n",
            filename, exclude, include);

    ajStrAssignS(&fileNameTmp, filename);
    ajFilenameTrimPath(&fileNameTmp);

    if(ajStrGetLen(include))
    {
        ajStrTokenAssignC(&handle, include, " \t,;\n");

        while(ajStrTokenNextParse(handle, &token))
            if(ajStrMatchWildS(filename, token) ||
               ajStrMatchWildS(fileNameTmp, token))
                ret = ajTrue;

        ajStrTokenReset(handle);
    }

    if(ajStrGetLen(exclude))
    {   /* nokeep, test exclude last */
        ajStrTokenAssignC(&handle, exclude, " \t,;\n");

        while(ajStrTokenNextParse(handle, &token))
            if(ajStrMatchWildS(filename, token) ||
               ajStrMatchWildS(fileNameTmp, token))
                ret = ajFalse;

        ajStrTokenReset(handle);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);

    return ret;
}




/* @func ajFilenameTestExcludePath ********************************************
**
** Tests a full path filename against wildcard
** lists of file names to be included and excluded.
**
** The full path is retained and included in the tests.
**
** By default files are excluded. The inclusion list is used to select
** files, and the exclusion list is then used to exclude selected
** files again.
**
** @param [r] filename [const AjPStr] File to test
** @param [r] exclude [const AjPStr] List of wildcard names to exclude
** @param [r] include [const AjPStr] List of wildcard names to include
** @return [AjBool] ajTrue if the filename is accepted.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameTestExcludePath(const AjPStr filename,
                                 const AjPStr exclude,
                                 const AjPStr include)
{
    AjBool ret = ajFalse;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;

    ajDebug("ajFilenameTestExcludePath "
            "filename '%S' exclude '%S' include '%S'\n",
            filename, exclude, include);

    if(ajStrGetLen(include))
    {
        ajStrTokenAssignC(&handle, include, " \t,;\n");

        while(ajStrTokenNextParse(handle, &token))
            if(ajStrMatchWildS(filename, token))
                ret = ajTrue;

        ajStrTokenReset(handle);
    }

    if(ajStrGetLen(exclude))
    {   /* nokeep, test exclude last */
        ajStrTokenAssignC(&handle, exclude, " \t,;\n");

        while(ajStrTokenNextParse(handle, &token))
            if(ajStrMatchWildS(filename, token))
                ret = ajFalse;

        ajStrTokenReset(handle);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);

    return ret;
}




/* @func ajFilenameTestInclude ************************************************
**
** Tests a filename against wildcard
** lists of file names to be included and excluded.
**
** By default files are included. The exclusion list is used to trim
** out files, and the inclusion list is then used to add selected
** files again.
**
** @param [r] filename [const AjPStr] File to test
** @param [r] exclude [const AjPStr] List of wildcard names to exclude
** @param [r] include [const AjPStr] List of wildcard names to include
** @return [AjBool] ajTrue if the filename is accepted.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameTestInclude(const AjPStr filename,
                             const AjPStr exclude,
                             const AjPStr include)
{
    AjBool ret = ajTrue;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;

    ajStrAssignS(&fileNameTmp, filename);
    ajFilenameTrimPath(&fileNameTmp);

    ajDebug("ajFilenameTestInclude "
            "filename '%S' exclude '%S' include '%S'\n",
            filename, exclude, include);

    if(ajStrGetLen(exclude))
    {
        /* keep, so test exclude first */
        ajStrTokenAssignC(&handle, exclude, " \t,;\n");

        while(ajStrTokenNextParse(handle, &token))
            if(ajStrMatchWildS(filename, token) ||
               ajStrMatchWildS(fileNameTmp, token))
                ret = ajFalse;

        ajStrTokenReset(handle);
    }

    if(ajStrGetLen(include))
    {
        ajStrTokenAssignC(&handle, include, " \t,;\n");

        while(ajStrTokenNextParse(handle, &token))
            if(ajStrMatchWildS(filename, token) ||
               ajStrMatchWildS(fileNameTmp, token))
                ret = ajTrue;

        ajStrTokenReset(handle);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);

    return ret;
}




/* @func ajFilenameTestIncludePath ********************************************
**
** Tests a filename against wildcard
** lists of file names to be included and excluded.
**
** By default files are included. The exclusion list is used to trim
** out files, and the inclusion list is then used to add selected
** files again.
**
** @param [r] filename [const AjPStr] File to test
** @param [r] exclude [const AjPStr] List of wildcard names to exclude
** @param [r] include [const AjPStr] List of wildcard names to include
** @return [AjBool] ajTrue if the filename is accepted.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameTestIncludePath(const AjPStr filename,
                                 const AjPStr exclude,
                                 const AjPStr include)
{
    AjBool ret = ajTrue;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;

    ajDebug("ajFilenameTestIncludePath "
            "filename '%S' exclukde '%S' include '%S\n",
            filename, exclude, include);

    if(ajStrGetLen(exclude))
    {
        /* keep, so test exclude first */
        ajStrTokenAssignC(&handle, exclude, " \t,;\n");

        while(ajStrTokenNextParse(handle, &token))
            if(ajStrMatchWildS(filename, token))
                ret = ajFalse;

        ajStrTokenReset(handle);
    }

    if(ajStrGetLen(include))
    {
        ajStrTokenAssignC(&handle, include, " \t,;\n");

        while(ajStrTokenNextParse(handle, &token))
            if(ajStrMatchWildS(filename, token))
                ret = ajTrue;

        ajStrTokenReset(handle);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);

    return ret;
}




/* @section Filename modifiers ************************************************
**
** These functions overwrite a previous filename
**
** @nam3rule Replace Replace part of a filename
** @nam3rule Set     Set undefined part of a filename
** @nam4rule Tempname Set a new temporary filename
** @nam3rule Trim    Remove part of a filename
** @nam4rule TrimAll  Remove path and extension and trailing :identifier
**                    to leave the file basename
**
** @suffix Ext Filename extension
** @suffix Path Filename path
** @suffix C New character value
** @suffix S New string value
**
** @argrule * Pfilename [AjPStr*] Filename
** @argrule C txt [const char*] New character value
** @argrule S str [const AjPStr] New string value
**
** @valrule * [AjBool] True on success
**
** @fcategory modify
**
** @fdata [AjPStr]
**
******************************************************************************/




/* @func ajFilenameReplaceExtC ************************************************
**
** Replaces the extension part of a filename
**
** @param [u] Pfilename [AjPStr*] Filename.
** @param [r] txt [const char*] New file extension
** @return [AjBool] ajTrue if the replacement succeeded.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameReplaceExtC(AjPStr* Pfilename, const char* txt)
{
    AjBool doext;
    char *p = NULL;

    doext = ajTrue;
    if(!txt || !*txt)
        doext = ajFalse;


    /*ajDebug("ajFilenameReplaceExtC '%S' '%s'\n", *Pfilename, txt);*/

    ajStrAssignS(&fileTmpStr,*Pfilename);

    /* Skip any directory path */
    p = strrchr(ajStrGetuniquePtr(&fileTmpStr),SLASH_CHAR);

    if (!p)
        p = ajStrGetuniquePtr(&fileTmpStr);

    p = strrchr(p,'.');

    if(p)
    {
        *p='\0';
        fileTmpStr->Len = p - ajStrGetPtr(fileTmpStr);
    }


    if(doext)
    {
        ajStrAppendC(&fileTmpStr,".");
        ajStrAppendC(&fileTmpStr,txt);
    }

    ajStrAssignS(Pfilename,fileTmpStr);

    /*ajDebug("result '%S'\n", *Pfilename);*/

    return ajTrue;
}




/* @func ajFilenameReplaceExtS ************************************************
**
** Replaces the extension part of a filename
**
** @param [u] Pfilename [AjPStr*] Filename.
** @param [r] str [const AjPStr] New file extension
** @return [AjBool] ajTrue if the replacement succeeded.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameReplaceExtS(AjPStr* Pfilename, const AjPStr str)
{
    if(!str)
        return ajFilenameReplaceExtC(Pfilename, NULL);

    return ajFilenameReplaceExtC(Pfilename, ajStrGetPtr(str));
}




/* @func ajFilenameReplacePathC ***********************************************
**
** Sets the directory part of a filename
**
** @param [u] Pfilename [AjPStr*] Filename.
** @param [r] txt [const char*] Directory
** @return [AjBool] ajTrue if the replacement succeeded.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameReplacePathC(AjPStr* Pfilename, const char* txt)
{
    AjPStr tmpname = NULL;
    AjPStr tmpdir = NULL;

    if(!txt)
        return ajFalse;

    if(!fileFilenameExp)
#ifndef WIN32
        fileFilenameExp = ajRegCompC("(.*/)?([^/]+)$");
#else /* WIN32 */
    fileFilenameExp = ajRegCompC("(.*[/\\\\])?([^/\\\\]+)$");
#endif /* !WIN32 */

    if(ajRegExec(fileFilenameExp, *Pfilename))
    {
        ajRegSubI(fileFilenameExp, 1, &tmpdir);

        /* we already have a directory */
        if(ajStrGetLen(tmpdir))
        {
            ajStrDel(&tmpdir);
            return ajFalse;
        }

        ajRegSubI(fileFilenameExp, 2, &tmpname);

        if(txt[strlen(txt)-1] == SLASH_CHAR)
            ajFmtPrintS(Pfilename, "%s%S", txt, tmpname);
        else
            ajFmtPrintS(Pfilename, "%s%s%S", txt, SLASH_STRING,tmpname);

        ajStrDel(&tmpname);
    }

    return ajTrue;
}




/* @func ajFilenameReplacePathS ***********************************************
**
** Sets the directory part of a filename
**
** @param [u] Pfilename [AjPStr*] Filename.
** @param [r] str [const AjPStr] New directory
** @return [AjBool] ajTrue if the replacement succeeded.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameReplacePathS(AjPStr* Pfilename, const AjPStr str)
{
    if(!ajStrGetLen(str))
        return ajFalse;

    return ajFilenameReplacePathC(Pfilename, ajStrGetPtr(str));
}




/* @func ajFilenameSetExtC ****************************************************
**
** Sets the extension part of a filename
**
** @param [u] Pfilename [AjPStr*] Filename.
** @param [r] txt [const char*] New file extension
** @return [AjBool] ajTrue if the replacement succeeded.
**
** @release 6.3.0
** @@
******************************************************************************/

AjBool ajFilenameSetExtC(AjPStr* Pfilename, const char* txt)
{
    char *p = NULL;

    if(!txt || !*txt)
        return ajFalse;

    /*ajDebug("ajFilenameSetExtC '%S' '%s'\n", *Pfilename, txt);*/

    ajStrAssignS(&fileTmpStr,*Pfilename);

    /* Skip any directory path */
    p = strrchr(ajStrGetuniquePtr(&fileTmpStr),SLASH_CHAR);

    if (!p)
        p = ajStrGetuniquePtr(&fileTmpStr);

    ajStrAppendC(&fileTmpStr,".");
    ajStrAppendC(&fileTmpStr,txt);

    ajStrAssignS(Pfilename,fileTmpStr);

    /*ajDebug("result '%S'\n", *Pfilename);*/

    return ajTrue;
}




/* @func ajFilenameSetExtS ****************************************************
**
** Sets the extension part of a base filename
**
** @param [u] Pfilename [AjPStr*] Filename.
** @param [r] str [const AjPStr] New file extension
** @return [AjBool] ajTrue if the replacement succeeded.
**
** @release 6.3.0
** @@
******************************************************************************/

AjBool ajFilenameSetExtS(AjPStr* Pfilename, const AjPStr str)
{
    if(!str)
        return ajFalse;

    return ajFilenameSetExtC(Pfilename, ajStrGetPtr(str));
}




/* @func ajFilenameSetTempname ************************************************
**
** Returns an available temporary filename that can be opened for writing
** Filename will be of the form progname-time.randomnumber
** Tries 5 times to find a new filename. Returns ajFalse if not
** successful or the file cannot be opened for writing.
** This function returns only the filename, not a file pointer.
**
** @param [w] Pfilename [AjPStr*] Filename of new temporary file
** @return [AjBool] True on success
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameSetTempname(AjPStr* Pfilename)
{
#if defined(AJ_IRIXLF)
    struct  stat64 buf;
#else /* !AJ_IRIXLF */
    struct  stat buf;
#endif /* AJ_IRIXLF */
    ajint   retry;
    AjBool  ok;
    AjPFile outf;

    if(!fileTempFilename)
        fileTempFilename = ajStrNew();

    ajStrAssignC(&fileDirectory,".");
    ajDirnameFix(&fileDirectory);

    ajFmtPrintS(&fileTempFilename,
                "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                ajRandomNumber());

    retry = 5;
    ok    = ajTrue;

    while(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(fileTempFilename),&buf) && retry
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(fileTempFilename),&buf) && retry
#endif /* AJ_IRIXLF */
          )
    {
        ajFmtPrintS(&fileTempFilename,
                    "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                    ajRandomNumber());
        --retry;
    }

    if(!retry)
    {
        ajDebug("Cannot find a unique filename [last try %S]\n",
                fileTempFilename);
        ok = ajFalse;
    }

    if(!(outf = ajFileNewOutNameS(fileTempFilename)))
    {
        ajDebug("Cannot write to file %S\n",fileTempFilename);
        ok = ajFalse;
    }
    else
    {
        ajFileClose(&outf);
        ajSysFileUnlinkS(fileTempFilename);
    }

    ajStrAssignS(Pfilename,fileTempFilename);

    return ok;
}




/* @func ajFilenameSetTempnamePathC *******************************************
**
** Returns an available temporary filename that can be opened for writing
** Filename will be of the form progname-time.randomnumber
** Tries 5 times to find a new filename. Returns ajFalse if not
** successful or the file cannot be opened for writing.
** This function returns only the filename, not a file pointer.
**
** @param [w] Pfilename [AjPStr*] Filename of new temporary file
** @param [r] txt [const char*] Directory path
** @return [AjBool] True on success
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameSetTempnamePathC(AjPStr* Pfilename, const char* txt)
{
#if defined(AJ_IRIXLF)
    struct  stat64 buf;
#else /* !AJ_IRIXLF */
    struct  stat buf;
#endif /* AJ_IRIXLF */
    ajint   retry;
    AjBool  ok;
    AjPFile outf;

    if(!fileTempFilename)
        fileTempFilename = ajStrNew();

    ajStrAssignC(&fileDirectory,txt);
    ajDirnameFix(&fileDirectory);

    ajFmtPrintS(&fileTempFilename,
                "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                ajRandomNumber());

    retry = 5;
    ok    = ajTrue;

    while(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(fileTempFilename),&buf) && retry
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(fileTempFilename),&buf) && retry
#endif /* AJ_IRIXLF */
          )
    {
        ajFmtPrintS(&fileTempFilename,
                    "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                    ajRandomNumber());
        --retry;
    }

    if(!retry)
    {
        ajDebug("Cannot find a unique filename [last try %S]\n",
                fileTempFilename);
        ok = ajFalse;
    }

    if(!(outf = ajFileNewOutNameS(fileTempFilename)))
    {
        ajDebug("Cannot write to file %S\n",fileTempFilename);
        ok = ajFalse;
    }
    else
    {
        ajFileClose(&outf);
        ajSysFileUnlinkS(fileTempFilename);
    }

    ajStrAssignS(Pfilename,fileTempFilename);

    return ok;
}




/* @func ajFilenameSetTempnamePathS *******************************************
**
** Returns an available temporary filename that can be opened for writing
** Filename will be of the form progname-time.randomnumber
** Tries 5 times to find a new filename. Returns ajFalse if not
** successful or the file cannot be opened for writing.
** This function returns only the filename, not a file pointer.
**
** @param [w] Pfilename [AjPStr*] Filename of new temporary file
** @param [r] str [const AjPStr] Directory path
** @return [AjBool] True on success
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameSetTempnamePathS(AjPStr* Pfilename, const AjPStr str)
{
#if defined(AJ_IRIXLF)
    struct  stat64 buf;
#else /* !AJ_IRIXLF */
    struct  stat buf;
#endif /* AJ_IRIXLF */
    ajint   retry;
    AjBool  ok;
    AjPFile outf;

    if(!fileTempFilename)
        fileTempFilename = ajStrNew();

    ajStrAssignS(&fileDirectory,str);
    ajDirnameFix(&fileDirectory);

    ajFmtPrintS(&fileTempFilename,
                "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                ajRandomNumber());

    retry = 5;
    ok    = ajTrue;

    while(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(fileTempFilename),&buf) && retry
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(fileTempFilename),&buf) && retry
#endif /* AJ_IRIXLF */
          )
    {
        ajFmtPrintS(&fileTempFilename,
                    "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                    ajRandomNumber());
        --retry;
    }

    if(!retry)
    {
        ajDebug("Cannot find a unique filename [last try %S]\n",
                fileTempFilename);
        ok = ajFalse;
    }

    if(!(outf = ajFileNewOutNameS(fileTempFilename)))
    {
        ajDebug("Cannot write to file %S\n",fileTempFilename);
        ok = ajFalse;
    }
    else
    {
        ajFileClose(&outf);
        ajSysFileUnlinkS(fileTempFilename);
    }

    ajStrAssignS(Pfilename,fileTempFilename);

    return ok;
}




/* @func ajFilenameTrimAll ****************************************************
**
** Truncates a filename to a basic file name.
**
** @param [uP] Pfilename [AjPStr*] File name
** @return [AjBool] ajTrue on success, and returns a filename.
**                  ajFalse on failure, and returns an empty string.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFilenameTrimAll(AjPStr* Pfilename)
{
    /* entryname at end */
    if(!fileEntryExp)
        fileEntryExp = ajRegCompC(":([A-Za-z0-9_-]+)$");

    if(ajRegExec(fileEntryExp, *Pfilename))
    {
        ajRegSubI(fileEntryExp, 1, Pfilename);
        return ajTrue;
    }

    /* name.ext */
    if(!fileFileExp)
        fileFileExp = ajRegCompC("([A-Za-z0-9_-]+)[.][A-Za-z0-9_-]+$");

    if(ajRegExec(fileFileExp, *Pfilename))
    {
        ajRegSubI(fileFileExp, 1, Pfilename);
        return ajTrue;
    }

    /* last valid word */
    if(!fileRestExp)
        fileRestExp = ajRegCompC("([A-Za-z0-9_-]+)[^A-Za-z0-9_-]*$");

    if(ajRegExec(fileRestExp, *Pfilename))
    {
        ajRegSubI(fileRestExp, 1, Pfilename);
        return ajTrue;
    }

    ajStrAssignClear(Pfilename);

    return ajFalse;
}




/* @func ajFilenameTrimExt ****************************************************
**
** Trims the extension (if any) from a filename
**
** @param [u] Pfilename [AjPStr*] Filename
** @return [AjBool] ajTrue is there was an extension
**
** @release 6.0.0
******************************************************************************/

AjBool ajFilenameTrimExt(AjPStr* Pfilename)
{
    ajlong i;
    ajlong len;

    if(!ajStrGetLen(*Pfilename))
        return ajFalse;

    len = ajStrGetLen(*Pfilename);
    i = ajStrFindlastC(*Pfilename, ".");

    if(i < 0)
        return ajFalse;

    ajStrCutEnd(Pfilename, (size_t) (len-i));

    return ajTrue;
}




/* @func ajFilenameTrimPath ***************************************************
**
** Trims the directory path (if any) from a filename
**
** @param [u] Pfilename [AjPStr*] Filename
** @return [AjBool] ajTrue is there was a directory
**
** @release 6.0.0
******************************************************************************/

AjBool ajFilenameTrimPath(AjPStr* Pfilename)
{
    ajlong i;

    if(!ajStrGetLen(*Pfilename))
        return ajFalse;

    i = ajStrFindlastC(*Pfilename, SLASH_STRING);

    if(i < 0)
        return ajFalse;

    ajStrCutStart(Pfilename, (size_t) (i+1));

    return ajTrue;
}




/* @func ajFilenameTrimPathExt ************************************************
**
** Trims the directory path (if any) and extension (if any) from a filename.
**
** @param [u] Pfilename [AjPStr*] Filename
** @return [AjBool] ajTrue is there was a directory path or extension.
**
** @release 6.0.0
******************************************************************************/

AjBool ajFilenameTrimPathExt(AjPStr* Pfilename)
{
    ajlong i;
    ajlong j;
    ajlong len;

    if(!ajStrGetLen(*Pfilename))
        return ajFalse;

    i = ajStrFindlastC(*Pfilename, SLASH_STRING);

    if(i >= 0)
        ajStrCutStart(Pfilename, (size_t) (i+1));

    len = ajStrGetLen(*Pfilename);
    j = ajStrFindlastC(*Pfilename, ".");

    if(j >= 0)
        ajStrCutEnd(Pfilename, (size_t) (len-j));

    if((i < 0) && (j < 0))
        return ajFalse;

    return ajTrue;
}




/* @datasection [AjPStr] Filename wildcard functions **************************
**
** Functions operating on strings containing filenames with possible wildcards
**
** @nam2rule Filewildname Operations on wildcard filename strings
**
******************************************************************************/




/* @section Filename tests ****************************************************
**
** Tests on filenames and searching the file system
**
** @nam3rule Exists tests whether file exists in current directory
** @nam4rule Dir Tests whether file exists and is a directory
**
** @argrule * wildname [const AjPStr] Filename string
** @argrule Dir path [const AjPStr] Directory path
**
** @valrule Exists [AjBool] True if file exists with any requested access
** @fcategory use
**
** @fdata [AjPStr]
**
******************************************************************************/




/* @func ajFilewildnameExists *************************************************
**
** Returns true if wildcard filename matches an existing file
**
** @param [r] wildname [const AjPStr] Wildcard filename.
** @return [AjBool] ajTrue on success
**
** @release 6.6.0
** @@
******************************************************************************/

AjBool ajFilewildnameExists(const AjPStr wildname)
{
    AjBool ret = ajFalse;

    AjPStr userstr = NULL;
    AjPStr reststr = NULL;

    AjPStr dirname        = NULL;
    AjPStr wildfilename   = NULL;

    char *hdir = NULL;

    ajDebug("ajFilewildnameExists '%S'\n", wildname);

    if(ajStrMatchC(wildname, "stdin"))
        return ajTrue;

    if(ajStrGetCharLast(wildname) == '|')   /* pipe character at end */
        return ajTrue;

    /* keep a copy of the filename */

    ajStrAssignS(&fileNameTmp, wildname);

    /* replace ~user/ or ~/ with directory */

    if(ajStrGetCharFirst(fileNameTmp) == '~')
    {
        ajDebug("starts with '~'\n");

        if(!fileUserExp)
            fileUserExp = ajRegCompC("^~([^/\\\\]*)");

        ajRegExec(fileUserExp, fileNameTmp);
        ajRegSubI(fileUserExp, 1, &userstr);
        ajRegPost(fileUserExp, &reststr);
        ajDebug("  user: '%S' rest: '%S'\n", userstr, reststr);

        if(ajStrGetLen(userstr))
        {
            /* username specified */
            hdir = ajSysGetHomedirFromName(ajStrGetPtr(userstr));

            if(!hdir)
            {
                ajStrDel(&userstr);
                ajStrDelStatic(&fileNameTmp);
                ajStrDel(&reststr);

                return ajFalse;
            }

            ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr);
            AJFREE(hdir);

            ajDebug("use getpwnam: '%S'\n", fileNameTmp);
        }
        else
        {
            /* just ~/ */
            hdir = ajSysGetHomedir();

            if(hdir)
            {
                ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr);
                AJFREE(hdir);
            }
            else
                ajFmtPrintS(&fileNameTmp,"%S",reststr);

            ajDebug("use HOME: '%S'\n", fileNameTmp);
        }

        ajStrDel(&userstr);
        ajStrDel(&reststr);
    }


    if(!fileWildExp)
        fileWildExp = ajRegCompC("(.*/)?([^/]*[*?][^/]*)$");

    /* wildcard file names (directory names do not allow wildcards) */

    if(ajRegExec(fileWildExp, fileNameTmp))
    {
        ajRegSubI(fileWildExp, 1, &dirname);
        ajRegSubI(fileWildExp, 2, &wildfilename);
        ajDebug("wild dir '%S' files '%S'\n", dirname, wildfilename);
        ret = ajFilewildnameExistsDir(wildfilename, dirname);
        ajStrDelStatic(&fileNameTmp);
        ajStrDel(&dirname);
        ajStrDel(&wildfilename);

        return ret;
    }

#ifdef WIN32
    if(ajStrMatchC(wildname, "/dev/null"))
    {
        ajStrDelStatic(&fileNameTmp);
        return ajTrue;
    }
    else
#endif /* WIN32 */

    ret = ajFilenameExists(fileNameTmp);
    ajStrDelStatic(&fileNameTmp);

    return ret;
}




/* @func ajFilewildnameExistsDir **********************************************
**
** Opens directory "dir"
** Looks for file(s) matching wildcard filename
**
** @param [r] wildname [const AjPStr] Wildcard filename.
** @param [r] path [const AjPStr] Directory
** @return [AjBool] True if one or more files matched.
**
** @release 6.6.0
** @@
******************************************************************************/

AjBool ajFilewildnameExistsDir(const AjPStr wildname, const AjPStr path)
{
    DIR* dp;
#if defined(AJ_IRIXLF)
    struct dirent64 *de;
#else /* !AJ_IRIXLF */
    struct dirent* de;
#endif /* AJ_IRIXLF */

#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    if(ajStrGetLen(path))
        ajStrAssignS(&fileDirfixTmp, path);
    else
        ajStrAssignC(&fileDirfixTmp, CURRENT_DIR);

    if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR)
        ajStrAppendC(&fileDirfixTmp, SLASH_STRING);

    dp = fileOpenDir(&fileDirfixTmp);

    if(!dp)
        return ajFalse;

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(dp,(struct dirent64 *)buf,&de)
#else /* !_POSIX_C_SOURCE */
        (de=readdir64(dp))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(dp,(struct dirent *)buf,&de)
#else /* _POSIX_C_SOURCE */
        (de=readdir(dp))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!de)
            break;
#endif /* _POSIX_C_SOURCE */
        /* skip deleted files with inode zero */
#ifndef __CYGWIN__
        if(!de->d_ino)
            continue;
#endif /* !__CYGWIN__ */

        if(ajCharMatchC(de->d_name, "."))
            continue;

        if(ajCharMatchC(de->d_name, ".."))
            continue;

        if(!ajCharMatchWildS(de->d_name, wildname))
            continue;

        ajDebug("accept '%s'\n", de->d_name);
        closedir(dp);
        return ajTrue;
    }

    ajDebug("no files for '%S' '%S'\n", path, wildname);
    closedir(dp);

    return ajFalse;
}




/* @datasection [AjPStr] Directory name functions *****************************
**
** Functions operating on strings containing directory names
**
** @nam2rule Dirname Operations on directory name strings
**
******************************************************************************/




/* @section Directory name modifiers ******************************************
**
** These functions overwrite a previous filename
**
** @nam3rule Fill    Fill in all parts of incomplete directory name
** @nam3rule Fix     Fix incomplete directory name
** @nam4rule FixExists Fix incomplete directory name and test it can be opened
** @nam3rule Up      Change to parent directory
**
** @suffix Ext Filename extension
** @suffix Path Filename path
** @suffix C New character value
** @suffix S New string value
**
** @argrule * Pdirname [AjPStr*] Filename
** @argrule C txt [const char*] New character value
** @argrule S str [const AjPStr] New string value
**
** @valrule * [void]
** @valrule *Exists [AjBool] True on success
** @valrule *Fill [AjBool] True on success
** @valrule *Up [AjBool] True on success
**
** @fcategory modify
**
** @fdata [AjPStr]
**
******************************************************************************/




/* @func ajDirnameFillPath ****************************************************
**
** Checks that a string is a valid directory, and makes sure it has the
** full path definition.
**
** @param [u] Pdirname [AjPStr*] Directory path
** @return [AjBool] true if a valid directory.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajDirnameFillPath(AjPStr* Pdirname)
{
    DIR* odir;
    AjPStr cwdpath = NULL;

    ajDebug("ajDirnameFillPath '%S'\n", *Pdirname);

    /* appends trailing slash if needed */
    odir = fileOpenDir(Pdirname);

    if(!odir)
        return ajFalse;

    free(odir);

    ajDebug("So far '%S'\n", *Pdirname);

    /* full path already */
    if(*ajStrGetPtr(*Pdirname) == SLASH_CHAR)
        return ajTrue;

    /* current directory */
    if(ajStrMatchC(*Pdirname, CURRENT_DIR))
    {
        ajStrAssignS(Pdirname, ajFileValueCwd());
        ajDebug("Current '%S'\n", *Pdirname);

        return ajTrue;
    }

    /*  going up */
    ajStrAssignS(&cwdpath, ajFileValueCwd());

    while(ajStrPrefixC(*Pdirname, UP_DIR))
    {
        ajDirnameUp(&cwdpath);
        ajStrKeepRange(Pdirname, 3, -1);
        ajDebug("Going up '%S' '%S'\n", *Pdirname, cwdpath);
    }

    ajStrInsertS(Pdirname, 0, cwdpath);

    ajDebug("Full path '%S'\n", *Pdirname);

    ajStrDel(&cwdpath);

    return ajTrue;
}




/* @func ajDirnameFix *********************************************************
**
** If the directory name has no trailing slash (on Unix) then one is
** added. This is why the directory name must be writable.
**
** @param [u] Pdirname [AjPStr*] Directory name.
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajDirnameFix(AjPStr* Pdirname)
{
    if(ajStrGetCharLast(*Pdirname) != SLASH_CHAR)
        ajStrAppendC(Pdirname, SLASH_STRING);

    return;
}




/* @func ajDirnameFixExists ***************************************************
**
** Checks that a string is a valid existing directory, and appends a
** trailing '/' if it is missing.
**
** @param [u] Pdirname [AjPStr*] Directory path
** @return [AjBool] true if a valid directory.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajDirnameFixExists(AjPStr* Pdirname)
{
    DIR* odir;

    odir = fileOpenDir(Pdirname);       /* appends trailing slash if needed */

    if(!odir)
        return ajFalse;

    closedir(odir);

    return ajTrue;
}




/* @func ajDirnameUp **********************************************************
**
** Changes directory name to one level up
**
** @param [u] Pdirname [AjPStr*] Directory name.
** @return [AjBool] ajTrue on success.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajDirnameUp(AjPStr* Pdirname)
{
    AjBool modded = ajFalse;

    const char *p;
    const char *q;
    ajint len;

    len = ajStrGetLen(*Pdirname);
    p   = ajStrGetPtr(*Pdirname);
    q   = p + len -2;

    if(q > p)
    {
        while(q!=p && *q==SLASH_CHAR)
            --q;

        while(q!=p && *q!=SLASH_CHAR)
            --q;

        if(q-p > 1)
        {
            ajStrCutEnd(Pdirname, len-(q-p)-1);
            modded = ajTrue;
        }
    }

    return modded;
}




/* @section Directory name functions ******************************************
**
** Functions using a directory name
**
** @nam3rule Print Print directory or filenames
** @nam4rule Recursive Step through subdirectories
**
** @suffix Ignore Process list of sub directory names to ignore
** @argrule * path [const AjPStr] directory name
** @argrule Ignore ignorelist [AjPList] List of names to ignore
** @argrule Print outfile [AjPFile] Output file
**
** @valrule * [void]
**
** @fcategory use
**
** @fdata [AjPStr]
**
******************************************************************************/




/* @func ajDirnamePrintRecursiveIgnore ****************************************
**
** Recursively scan through a directory, printing directory and file names
**
** @param [r] path [const AjPStr] Directory to scan
** @param [u] ignorelist [AjPList] List of directories to ignore
** @param [u] outfile [AjPFile] File for "show" results (or NULL)
**
** @return [void]
**
** @release 6.0.0
** @@
******************************************************************************/

void ajDirnamePrintRecursiveIgnore(const AjPStr path,
                                   AjPList ignorelist, AjPFile outfile)
{
    AjPList dirs = NULL;
    AjIList iter = NULL;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;

    AjPStr tstr = NULL;

    AjBool flag;
    AjPStr tpath = NULL;
    AjPList plist = NULL;

#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    tpath = ajStrNewS(path);

    ajFmtPrintF(outfile,"\n\nDIRECTORY: %S\n\n",path);

    if(!ajDirnameFixExists(&tpath))
    {
        ajStrDel(&tpath);

        return;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajStrDel(&tpath);

        return;
    }


    s = ajStrNew();
    dirs = ajListNew();
    plist = ajListNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        /*      ajStrAppendC(&s,SLASH_STRING);*/
        ajStrAppendC(&s,dp->d_name);

        /* Its a directory */
        if(ajFilenameExistsDir(s))
        {
            /* Ignore selected directories */
            if(ajListGetLength(ignorelist))
            {
                flag = ajFalse;
                iter = ajListIterNewread(ignorelist);

                while(!ajListIterDone(iter))
                {
                    t = ajListIterGet(iter);

                    if(!strcmp(ajStrGetPtr(t),dp->d_name))
                    {
                        flag = ajTrue;
                        break;
                    }
                }

                ajListIterDel(&iter);

                if(flag)
                    continue;
            }

            if(!ajFilenameExistsRead(s) || !ajFilenameExistsExec(s))
                continue;

            t = ajStrNewC(ajStrGetPtr(s));
            ajListPushAppend(dirs,(void *)t);
        }
        else if(ajFilenameExistsRead(s))
        {
            tstr = ajStrNew();
            ajStrAssignC(&tstr,dp->d_name);
            ajListPush(plist, (void *)tstr);


        }
    }

    closedir(indir);

    ajListSort(plist, &ajStrVcmp);
    while(ajListPop(plist,(void **)&tstr))
    {
        ajFmtPrintF(outfile,"  %S\n",tstr);
        ajStrDel(&tstr);
    }

    ajListFree(&plist);

    while(ajListPop(dirs,(void **)&t))
    {
        ajDirnamePrintRecursiveIgnore(t, ignorelist, outfile);
        ajStrDel(&t);
    }

    ajStrDel(&s);
    ajStrDel(&tpath);
    ajListFree(&dirs);


    return;
}




/* @datasection [AjPlist] Lists of filenames **********************************
**
** Functions building and managing lists of filenames
**
** @nam2rule Filelist
**
******************************************************************************/




/* @section File list addition ************************************************
**
** Functions that add filenames to a list
**
** @nam3rule Add Adds names to a list
** @nam4rule Path Directory path provided
** @nam5rule Wild Wildcard filename provided
** @suffix Recursive Process subdirectories
** @suffix Ignore List of subdirectories to be ignored
** @suffix Directory Directory object provided
** @suffix Dir Include directories in the list
** @nam4rule Listname List of files or lists
**
** @argrule * list [AjPList] Filename list
** @argrule Listname listname [const AjPStr] Commandline list of filenames
** @argrule Directory dir [const AjPDir] Directory object
** @argrule Path path [const AjPStr] Pathname of directory
** @argrule Wild wildname [const AjPStr] Wildcard filename to add
** @argrule Ignore ignorelist [AjPList] Subdirectory name list to ignore
**
** @valrule * [ajint] Number of entries added to the list
**
** @fcategory modify
**
** @fdata [AjPlist]
**
******************************************************************************/




/* @func ajFilelistAddDirectory ***********************************************
**
** Scan through a directory object returning all filenames that are
** not directories. Uses the file extension and any other attributes
** of the directory object. An empty string as a file extension accepts
** only files that have no extension.
**
** @param [u] list [AjPList] List for matching entries
** @param [r] dir [const AjPDir] Directory to scan
**
** @return [ajint] number of entries in list
**
** @release 6.3.0
** @@
******************************************************************************/

ajint ajFilelistAddDirectory(AjPList list,
                             const AjPDir dir)
{
    ajulong oldsize;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr f = NULL;
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjPStr e = NULL;

    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    AjBool doprefix = AJFALSE;
    AjBool doextension = AJFALSE;

    if(!dir)
        return 0;

    if(dir->Prefix)
        doprefix = ajTrue;
    if(dir->Extension)
    {
        if(ajStrGetCharFirst(dir->Extension) == '.')
            e = ajStrNewS(dir->Extension);
        else if(ajStrGetLen(dir->Extension))
            ajFmtPrintS(&e, ".%S", dir->Extension);

        doextension = ajTrue;
    }

    oldsize = ajListGetLength(list);

    tpath = ajStrNewS(dir->Name);

    ajDebug("ajFilelistAddDir '%S' oldsize:%Lu\n",
            tpath, oldsize);

    if(!ajDirnameFixExists(&tpath))
    {
        ajDebug("... not a directory '%S'\n", tpath);
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajDebug("... failed to open directory '%S'\n", tpath);
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();
    f = ajStrNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */

        ajStrAssignC(&f,dp->d_name);

        if(doprefix && !ajStrPrefixS(f, dir->Prefix))
            continue;

        if(doextension)
        {
            if(e)
            {
                if(!ajStrSuffixS(f, e))
                    continue;
            }
            else
            {
                if(ajStrFindAnyK(f,'.') >= 0)
                    continue;
            }
        }

        ajStrAssignS(&s,tpath);
        ajStrAppendS(&s,f);

        if(ajFilenameExistsDir(s))
            continue;

        ajDebug("... add to list '%S'\n",
                s);

        t = ajStrNewS(s);
        ajListPushAppend(list,(void *)t);

    }
    closedir(indir);


    ajStrDel(&e);
    ajStrDel(&f);
    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}




/* @func ajFilelistAddListname ************************************************
**
** Adds to a list of files that match a comma-separated string of
** filenames which can include wildcards or listfiles
**
** @param [u] list [AjPList] List of filenames
** @param [r] listname [const AjPStr] comma-separated filename list
**
** @return [ajint] Number of entries added to list
**
** @release 6.0.0
** @@
******************************************************************************/

ajint ajFilelistAddListname(AjPList list, const AjPStr listname)
{
    ajulong oldsize;
    AjPStr *fstr = NULL;
    ajint  ncl;
    ajint  i;
    ajint  rlevel = 0;

    oldsize = ajListGetLength(list);
    ncl = ajArrCommaList(listname,&fstr);

    for(i=0;id_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        ajStrAppendC(&s,dp->d_name);

        ajDebug("... testing '%S'\n",
                s);

        if(ajFilenameExistsDir(s))
        {
            ajDebug("... rejected: directory '%S'\n",
                    s);
            continue;
        }

        ajDebug("... add to list '%S'\n",
                s);

        t = ajStrNewS(s);
        ajListPushAppend(list,(void *)t);

    }

    closedir(indir);


    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}




/* @func ajFilelistAddPathDir *************************************************
**
** Scan through a directory returning all filenames and directory names
** except '.' and '..', including directories
**
** @param [u] list [AjPList] List for matching entries
** @param [r] path [const AjPStr] Directory to scan
**
** @return [ajint] number of entries in list
**
** @release 6.0.0
** @@
******************************************************************************/

ajint ajFilelistAddPathDir(AjPList list,
                           const AjPStr path)
{
    ajulong oldsize;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    oldsize = ajListGetLength(list);

    tpath = ajStrNewS(path);

    ajDebug("ajFilelistAddPath '%S' oldsize:%Lu\n",
            path, oldsize);

    if(!ajDirnameFixExists(&tpath))
    {
        ajDebug("... not a directory '%S'\n", tpath);
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajDebug("... failed to open directory '%S'\n", tpath);
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        ajStrAppendC(&s,dp->d_name);

        ajDebug("... add to list '%S'\n",
                s);

        t = ajStrNewS(s);
        ajListPushAppend(list,(void *)t);

    }
    closedir(indir);


    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}




/* @func ajFilelistAddPathWild ************************************************
**
** Scan through a directory returning all filenames matching a
** wildcard filename
**
** @param [w] list [AjPList] List for matching entries
** @param [r] path [const AjPStr] Directory to scan
** @param [r] wildname [const AjPStr] Filename to search for
**
** @return [ajint] number of entries in list
**
** @release 6.0.0
** @@
******************************************************************************/

ajint ajFilelistAddPathWild(AjPList list,
                            const AjPStr path,
                            const AjPStr wildname)
{
    ajulong oldsize;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    oldsize = ajListGetLength(list);

    tpath = ajStrNew();
    ajStrAssignS(&tpath,path);


    if(!ajDirnameFixExists(&tpath))
    {
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        ajStrAppendC(&s,dp->d_name);

        if(ajFilenameExistsDir(s))
            continue;

        if(ajCharMatchWildS(dp->d_name,wildname))
        {
            t = ajStrNewS(s);
            ajListPushAppend(list,(void *)t);
        }
    }
    closedir(indir);


    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}




/* @func ajFilelistAddPathWildDir *********************************************
**
** Scan through a directory returning all filenames matching a
** wildcard filename, including directories
**
** @param [w] list [AjPList] List for matching entries
** @param [r] path [const AjPStr] Directory to scan
** @param [r] wildname [const AjPStr] Filename to search for
**
** @return [ajint] number of entries in list
**
** @release 6.0.0
** @@
******************************************************************************/

ajint ajFilelistAddPathWildDir(AjPList list,
                               const AjPStr path,
                               const AjPStr wildname)
{
    ajulong oldsize;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    oldsize = ajListGetLength(list);

    tpath = ajStrNew();
    ajStrAssignS(&tpath,path);


    if(!ajDirnameFixExists(&tpath))
    {
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        ajStrAppendC(&s,dp->d_name);

        if(ajCharMatchWildS(dp->d_name,wildname))
        {
            t = ajStrNewS(s);
            ajListPushAppend(list,(void *)t);
        }
    }

    closedir(indir);


    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}




/* @funcstatic fileListRecurs  ************************************************
**
** Add a filename, expanded wildcard filenames and list file contents to
** a list
**
** @param [r] srcfile [const AjPStr] filename, wild filename or list filename
** @param [u] list [AjPList] result filename list
** @param [u] recurs [ajint *] recursion level counter
**
** @return [void]
**
** @release 2.5.0
** @@
******************************************************************************/

static void fileListRecurs(const AjPStr srcfile, AjPList list, ajint *recurs)
{
    char c;
    AjPStr ptr = NULL;
    AjPStr dir = NULL;
    char   *p;
    AjPList dlist;
    AjPFile inf;
    AjPStr  line = NULL;
    AjPStr file  = NULL;

    ++(*recurs);

    if(*recurs > FILERECURSLV)
        ajFatal("Filelist maximum recursion level reached");

    ajStrAssignS(&file, srcfile);
    ajStrTrimWhite(&file);
    c = *ajStrGetPtr(file);

    dir   = ajStrNew();
    line  = ajStrNew();
    dlist = ajListNew();


    if(ajStrIsWild(file))
    {
        if(!(p=strrchr(ajStrGetPtr(file),(int)SLASH_CHAR)))
            ajStrAssignC(&dir,CURRENT_DIR);
        else
            ajStrAssignSubC(&dir,ajStrGetPtr(file),0,p-ajStrGetPtr(file));

        ajFilelistAddPathWild(dlist, dir, file);

        while(ajListPop(dlist,(void **)&ptr))
        {
            if(ajStrPrefixC(ptr,CURRENT_DIR))
                ajStrCutStart(&ptr,2);

            ajListPushAppend(list,(void *)ptr);
        }
    }
    else if(c=='@')
    {
        if((inf=ajFileNewInNameC(ajStrGetPtr(file)+1)))
            while(ajReadlineTrim(inf,&line))
                fileListRecurs(line,list,recurs);
        if(inf)
            ajFileClose(&inf);
    }
    else if(ajStrPrefixC(file,"list::"))
    {
        if((inf=ajFileNewInNameC(ajStrGetPtr(file)+6)))
            while(ajReadlineTrim(inf,&line))
                fileListRecurs(line,list,recurs);
        if(inf)
            ajFileClose(&inf);
    }
    else
    {
        ptr = ajStrNewC(ajStrGetPtr(file));
        ajListPushAppend(list,(void *)ptr);
    }


    ajListFree(&dlist);
    ajStrDel(&dir);
    ajStrDel(&line);
    ajStrDel(&file);

    --(*recurs);

    return;
}




/* @func ajFilelistAddPathWildRecursiveIgnore *********************************
**
** Recursively scan through a directory
**
** @param [u] list [AjPList] List for matching filenames
** @param [r] path [const AjPStr] Directory to scan
** @param [r] wildname [const AjPStr] Filename to search for (or NULL)
** @param [u] ignorelist [AjPList] List of directories to ignore
**
** @return [ajint] number of new entries in list
**
** @release 6.0.0
** @@
******************************************************************************/

ajint ajFilelistAddPathWildRecursiveIgnore(AjPList list,
                                           const AjPStr path,
                                           const AjPStr wildname,
                                           AjPList ignorelist)
{
    ajulong oldsize;
    AjPList dirs = NULL;
    AjIList iter = NULL;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjBool flag;
    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    oldsize = ajListGetLength(list);

    tpath = ajStrNew();
    ajStrAssignS(&tpath,path);

    if(!ajDirnameFixExists(&tpath))
    {
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();
    dirs = ajListNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        /*      ajStrAppendC(&s,SLASH_STRING);*/
        ajStrAppendC(&s,dp->d_name);

        /* Its a directory */
        if(ajFilenameExistsDir(s))
        {
            /* Ignore selected directories */
            if(ajListGetLength(ignorelist))
            {
                flag = ajFalse;
                iter = ajListIterNewread(ignorelist);

                while(!ajListIterDone(iter))
                {
                    t = ajListIterGet(iter);

                    if(!strcmp(ajStrGetPtr(t),dp->d_name))
                    {
                        flag = ajTrue;
                        break;
                    }
                }

                ajListIterDel(&iter);

                if(flag)
                    continue;
            }

            if(!ajFilenameExistsRead(s) || !ajFilenameExistsExec(s))
                continue;

            t = ajStrNewC(ajStrGetPtr(s));
            ajListPushAppend(dirs,(void *)t);
        }
        else if(ajFilenameExistsRead(s))
        {
            if(ajStrGetLen(wildname))
                if(ajCharMatchWildS(dp->d_name,wildname))
                {
                    t = ajStrNewS(s);
                    ajListPushAppend(list,(void *)t);
                }

            /*ajDebug("  %s\n",dp->d_name);*/
        }
    }

    closedir(indir);

    while(ajListPop(dirs,(void **)&t))
    {
        ajFilelistAddPathWildRecursiveIgnore(list, t,wildname, ignorelist);
        ajStrDel(&t);
    }

    ajStrDel(&s);
    ajStrDel(&tpath);
    ajListFree(&dirs);

    return (ajuint) (ajListGetLength(list) - oldsize);
}




/* @datasection [none] File internal values ***********************************
**
** Functions returning internal values and system parameters
**
** @nam2rule File
**
******************************************************************************/




/* @section Internal values ***************************************************
**
** Internal values and system parameters
**
** @nam3rule Value Return a value
** @nam4rule Buffsize Internal buffer size default
** @nam4rule Cwd Current working directory
** @nam4rule Redirect Test whether a system file is redirected to
**                    a file object
** @nam5rule RedirectStderr Test redirection of standard error
** @nam5rule RedirectStdin  Test redirection of standard input
** @nam5rule RedirectStdout Test redirection of standard output
**
** @valrule *Cwd [const AjPStr] True on success
** @valrule *Buffsize [ajuint] Buffer size
** @valrule Redirect [AjBool] True if file is redirected.
**
** @fcategory misc
**
** @fdata [none]
**
******************************************************************************/




/* @func ajFileValueBuffsize **************************************************
**
** Returns the default buffer size for a file
**
** @return [ajuint] Buffer size default value
**
** @release 6.0.0
******************************************************************************/

ajuint ajFileValueBuffsize(void)
{
    return fileBuffSize;
}




/* @func ajFileValueCwd *******************************************************
**
** Returns the current directory
**
** @return [const AjPStr] Directory name.
**
** @release 6.0.0
** @@
******************************************************************************/

const AjPStr ajFileValueCwd(void)
{
    char cwd[PATH_MAX+1];

    if(!getcwd(cwd,PATH_MAX))
    {
        ajStrAssignClear(&fileCwd);

        return fileCwd;
    }


    ajStrAssignC(&fileCwd, cwd);

    if(!ajStrSuffixC(fileCwd, SLASH_STRING))
        ajStrAppendC(&fileCwd, SLASH_STRING);

    return fileCwd;
}




/* @func ajFileValueRedirectStderr ********************************************
**
** Tests whether stderr is in use by an internal file
**
** @return [AjBool] ajTrue if the file matches stderr.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFileValueRedirectStderr(void)
{
    return fileUsedStderr;
}




/* @func ajFileValueRedirectStdin *********************************************
**
** Tests whether stdin is in use by an internal file
**
** @return [AjBool] ajTrue if the file matches stdin.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFileValueRedirectStdin(void)
{
    return fileUsedStdin;
}




/* @func ajFileValueRedirectStdout ********************************************
**
** Tests whether stdout is in use by an internal file
**
** @return [AjBool] ajTrue if the file matches stdout.
**
** @release 6.0.0
** @@
******************************************************************************/

AjBool ajFileValueRedirectStdout(void)
{
    return fileUsedStdout;
}




/* @funcstatic filePrintname **************************************************
**
** Create a printable version of a filename
**
** @param [r] name [const AjPStr] File name
** @param [w] Pprintname [AjPStr*] Printable filename
** @return [void]
**
** @release 6.4.0
******************************************************************************/

static void filePrintname(const AjPStr name, AjPStr* Pprintname)
{

    ajStrAssignS(Pprintname, name);
#ifdef WIN32
    ajStrExchangeKK(Pprintname, '/', '\\');
    if(ajStrPrefixC(*Pprintname, ".\\"))
        ajStrCutStart(Pprintname, 2);
#else /* !WIN32 */
    ajStrExchangeCC(Pprintname, "//", "/");
#endif /* WIN32 */

    return;
}




#ifdef AJ_COMPILE_DEPRECATED_BOOK
#endif




#ifdef AJ_COMPILE_DEPRECATED
/* @obsolete ajDirOutNewSS
** @remove Use ajDiroutNewPathExt (prefix not relevant)
*/
__deprecated AjPDir ajDirOutNewSS(const AjPStr name,
                                  const AjPStr prefix, const AjPStr ext)
{
    (void) name;
    (void) prefix;
    (void) ext;

    return NULL;
}




/* @obsolete ajDirNew
** @rename ajDirNewPath
*/
__deprecated AjPDir ajDirNew(const AjPStr name)
{
    return ajDirNewPath(name);
}




/* @obsolete ajDirNewS
** @rename ajDirNewPathExt
*/
__deprecated AjPDir ajDirNewS(const AjPStr name, const AjPStr ext)
{
    return ajDirNewPathExt(name, ext);
}




/* @obsolete ajDirNewSS
** @rename ajDirNewPathPreExt
*/
__deprecated AjPDir ajDirNewSS(const AjPStr name,
                               const AjPStr prefix, const AjPStr ext)
{
    return ajDirNewPathPreExt(name,prefix,ext);
}




/* @obsolete ajDirExt
** @rename ajDirGetExt
*/
__deprecated const AjPStr ajDirExt(const AjPDir thys)
{
    return ajDirGetExt(thys);
}




/* @obsolete ajDirName
** @rename ajDirGetPath
*/
__deprecated const AjPStr ajDirName(const AjPDir thys)
{
    return ajDirGetPath(thys);
}




/* @obsolete ajDirOutNew
** @remove Use ajDiroutNewPath
*/
__deprecated AjPDir ajDirOutNew(const AjPStr name)
{
    (void)name;
    return NULL;
}




/* @obsolete ajDirOutNewS
** @remove Use ajDiroutNewPathExt
*/
__deprecated AjPDir ajDirOutNewS(const AjPStr name, const AjPStr ext)
{
    (void)name;
    (void)ext;

    return NULL;
}




/* @obsolete ajFileStat
** @remove Use ajFilenameExists ajFileNameExistsRead ajFilenameExistsWrite
**             or ajFileNameExistsExec
*/
__deprecated AjBool ajFileStat(const AjPStr fname, ajint mode)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(fname), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(fname), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & mode)
            return ajTrue;

    return ajFalse;
}




/* @obsolete ajFileNameValid
** @rename ajFilenameExistsRead
*/
__deprecated AjBool ajFileNameValid (const AjPStr fname)
{
    return ajFilenameExistsRead(fname);
}




/* @obsolete ajFileLength
** @rename ajFilenameGetSize
*/
__deprecated ajlong ajFileLength(const AjPStr fname)
{
    return ajFilenameGetSize(fname);
}




/* @obsolete ajFileHasDir
** @rename ajFilenameHasPath
*/
__deprecated AjBool ajFileHasDir(const AjPStr name)
{
    return ajFilenameHasPath(name);
}




/* @obsolete ajFileTestSkip
** @remove Use ajFilenameTestExclude ajFilenameTestInclude and *Path
*/
__deprecated AjBool ajFileTestSkip(const AjPStr fullname,
                                   const AjPStr exc, const AjPStr inc,
                                   AjBool keep, AjBool ignoredirectory)
{
    if(ignoredirectory)
    {
        if(keep)
            return ajFilenameTestInclude(fullname, exc, inc);
        else
            return ajFilenameTestExclude(fullname, exc, inc);
    }

    if(keep)
        return ajFilenameTestIncludePath(fullname, exc, inc);

    return ajFilenameTestExcludePath(fullname, exc, inc);
}




/* @obsolete ajFileNameExtC
** @rename ajFilenameReplaceExtC
*/
__deprecated AjBool ajFileNameExtC(AjPStr* filename, const char* extension)
{
    return ajFilenameReplaceExtC(filename, extension);
}




/* @obsolete ajFileNameExt
** @rename ajFilenameReplaceExtS
*/
__deprecated AjBool ajFileNameExt(AjPStr* filename, const AjPStr extension)
{
    return ajFilenameReplaceExtS(filename, extension);
}




/* @obsolete ajFileNameDirSetC
** @rename ajFilenameReplacePathC
*/
__deprecated AjBool ajFileNameDirSetC(AjPStr* filename, const char* dir)
{
    return ajFilenameReplacePathC(filename, dir);
}




/* @obsolete ajFileNameDirSet
** @rename ajFilenameReplacePathS
*/
__deprecated AjBool ajFileNameDirSet(AjPStr* filename, const AjPStr dir)
{
    return ajFilenameReplacePathS(filename, dir);
}




/* @obsolete ajFileSetDir
** @rename ajFilenameReplacePathS
*/
__deprecated AjBool ajFileSetDir (AjPStr *pname, const AjPStr dir)

{
    return ajFilenameReplacePathS(pname, dir);
}




/* @obsolete ajFileNewF
** @rename ajFileNewFromCfile
*/
__deprecated AjPFile ajFileNewF(FILE* file)
{
    return ajFileNewFromCfile(file);
}




/* @obsolete ajFileTempName
** @remove Use ajFilenameSetTempname or ajFilenameSetTempnamePath
*/
__deprecated const char* ajFileTempName(const char *dir)
{
    AjPStr tmpstr = NULL;

    (void) dir;
    ajFilenameSetTempname(&tmpstr);

    return ajStrGetPtr(tmpstr);
}




/* @obsolete ajFileNameShorten
** @rename ajFilenameTrimAll
*/
__deprecated AjBool ajFileNameShorten(AjPStr* fname)
{
    return ajFilenameTrimAll(fname);
}




/* @obsolete ajFileExtnTrim
** @rename ajFilenameTrimExt
*/
__deprecated AjBool ajFileExtnTrim(AjPStr* name)
{
    return ajFilenameTrimExt(name);
}




/* @obsolete ajFileNew
** @remove Use a constructor that opens a real file
*/

__deprecated AjPFile ajFileNew(void)
{
    return fileNew();
}




/* @obsolete ajFileNewInC
** @rename ajFileNewInNameC
*/

__deprecated AjPFile ajFileNewInC(const char *name)
{
    return ajFileNewInNameC(name);
}




/* @obsolete ajFileNewIn
** @rename ajFileNewInNameS
*/
__deprecated AjPFile ajFileNewIn(const AjPStr name)
{

    return ajFileNewInNameS(name);
}




/* @obsolete ajFileNewDC
** @remove Use ajFileNewInNamePathS
*/
__deprecated AjPFile ajFileNewDC(const AjPStr dir, const char* filename)
{
    ajStrAssignC(&fileNameStrTmp, filename);
    return ajFileNewInNamePathS(fileNameStrTmp, dir);
}




/* @obsolete ajFileNewDF
** @replace ajFileNewInNamePathS (1,2/2,1)
*/
__deprecated AjPFile ajFileNewDF(const AjPStr dir, const AjPStr filename)
{
    return ajFileNewInNamePathS(filename, dir);
}




/* @obsolete ajFileNewInList
** @rename ajFileNewListinList
*/

__deprecated AjPFile ajFileNewInList(AjPList list)
{
    return ajFileNewListinList(list);
}




/* @obsolete ajFileNewDirF
** @rename ajFileNewListinDirPre
*/
__deprecated AjPFile ajFileNewDirF(const AjPDir dir, const AjPStr filename)
{
    return ajFileNewListinDirPre(dir,filename);
}




/* @obsolete ajFileNewDW
** @rename ajFileNewListinPathWild
*/

__deprecated AjPFile ajFileNewDW(const AjPStr dir, const AjPStr wildfile)
{
    return ajFileNewListinPathWild(dir, wildfile);
}




/* @obsolete ajFileNewDWE
** @rename ajFileNewListinPathWildExclude
*/
__deprecated AjPFile ajFileNewDWE(const AjPStr dir, const AjPStr wildfile,
                                  const AjPStr exclude)
{
    return ajFileNewListinPathWildExclude(dir, wildfile, exclude);
}




/* @obsolete ajFileNewOut
** @rename ajFileNewOutNameS
*/
__deprecated AjPFile ajFileNewOut(const AjPStr name)
{
    return ajFileNewOutNameS(name);
}




/* @obsolete ajFileNewOutDir
** @replace ajFileNewOutNameDirS (1,2/2,1)
*/
__deprecated AjPFile ajFileNewOutDir(const AjPDirout dir, const AjPStr name)
{
    return ajFileNewOutNameDirS(name, dir);
}




/* @obsolete ajFileNewOutD
** @replace ajFileNewOutNamePathS (1,2/2,1)
*/
__deprecated AjPFile ajFileNewOutD(const AjPStr dir, const AjPStr name)
{
    return ajFileNewOutNamePathS(name, dir);
}




/* @obsolete ajFileNewApp
** @rename ajFileNewOutappendNameS
*/
__deprecated AjPFile ajFileNewApp(const AjPStr name)
{

    return ajFileNewOutappendNameS(name);
}




/* @obsolete ajFileReopen
** @rename ajFileReopenName
*/
__deprecated FILE* ajFileReopen(AjPFile thys, const AjPStr name)
{
    ajFileReopenName(thys, name);

    return thys->fp;
}




/* @obsolete ajFileNext
** @rename ajFileReopenNext
*/
__deprecated AjBool ajFileNext(AjPFile thys)
{
    return ajFileReopenNext(thys);
}




/* @obsolete ajFileTell
** @rename ajFileResetPos
*/
__deprecated ajlong ajFileTell(AjPFile file)
{
    return ajFileResetPos(file);
}




/* @obsolete ajFileUnbuffer
** @rename ajFileSetUnbuffer
*/
__deprecated void ajFileUnbuffer(AjPFile thys)
{
    ajFileSetUnbuffer(thys);

    return;
}




/* @obsolete ajFileFp
** @rename ajFileGetFileptr
*/
__deprecated FILE* ajFileFp(const AjPFile thys)
{
    return ajFileGetFileptr(thys);
}




/* @obsolete ajFileName
** @rename ajFileGetNameC
*/
__deprecated const char* ajFileName(const AjPFile file)
{
    return ajFileGetNameC(file);
}




/* @obsolete ajFileNameS
** @rename ajFileGetNameS
*/
__deprecated const AjPStr ajFileNameS(const AjPFile file)
{
    return ajFileGetNameS(file);
}




/* @obsolete ajFileGetName
** @rename ajFileGetNameS
*/
__deprecated const AjPStr ajFileGetName(const AjPFile file)
{
    return ajFileGetNameS(file);
}




/* @obsolete ajFileGetApp
** @rename ajFileIsAppend
*/
__deprecated AjBool ajFileGetApp(const AjPFile thys)
{
    return ajFileIsAppend(thys);
}




/* @obsolete ajFileEof
** @rename ajFileIsEof
*/
__deprecated AjBool ajFileEof(const AjPFile thys)
{
    return ajFileIsEof(thys);
}




/* @obsolete ajFileStderr
** @rename ajFileIsStderr
*/
__deprecated AjBool ajFileStderr(const AjPFile file)
{
    return ajFileIsStderr(file);
}




/* @obsolete ajFileStdin
** @rename ajFileIsStdin
*/
__deprecated AjBool ajFileStdin(const AjPFile file)
{
    return ajFileIsStdin(file);
}





/* @obsolete ajFileStdout
** @rename ajFileIsStdout
*/
__deprecated AjBool ajFileStdout(const AjPFile file)
{
    return ajFileIsStdout(file);
}




/* @obsolete ajFileBuffNewF
** @rename ajFilebuffNewFromCfile
*/
__deprecated AjPFilebuff ajFileBuffNewF(FILE* fp)
{
    return ajFilebuffNewFromCfile(fp);
}




/* @obsolete ajFileBuffNewFile
** @rename ajFilebuffNewFromFile
*/
__deprecated AjPFilebuff ajFileBuffNewFile(AjPFile file)
{
    return ajFilebuffNewFromFile(file);
}




/* @obsolete ajFileBuffNewS
** @rename ajFilebuffNewLine
*/
__deprecated AjPFilebuff ajFileBuffNewS(const AjPStr data)
{
    return ajFilebuffNewLine(data);
}




/* @obsolete ajFileBuffNewList
** @rename ajFilebuffNewListinList
*/

__deprecated AjPFilebuff ajFileBuffNewList(AjPList list)
{
    return ajFilebuffNewListinList(list);
}




/* @obsolete ajFileBuffNewIn
** @rename ajFilebuffNewNameS
*/
__deprecated AjPFilebuff ajFileBuffNewIn(const AjPStr name)
{
    return ajFilebuffNewNameS(name);
}




/* @obsolete ajFileBuffNewDC
** @replace ajFileBuffNewNamePathC (1,2/2,1)
*/
__deprecated AjPFilebuff ajFileBuffNewDC(const AjPStr dir,
                                         const char* filename)
{
    return ajFilebuffNewNamePathC(filename, dir);
}




/* @obsolete ajFileBuffNewDF
** @replace ajFilebuffNewNamePathS (1,2/2,1)
*/
__deprecated AjPFilebuff ajFileBuffNewDF(const AjPStr dir,
                                         const AjPStr filename)
{
    return ajFilebuffNewNamePathS(filename, dir);
}





/* @obsolete ajFileBuffNew
** @rename ajFilebuffNewNofile
*/
__deprecated AjPFilebuff ajFileBuffNew(void)
{
    return ajFilebuffNewNofile();
}




/* @obsolete ajFileBuffNewDW
** @rename ajFilebuffNewPathWild
*/

__deprecated AjPFilebuff ajFileBuffNewDW(const AjPStr dir,
                                         const AjPStr wildfile)
{
    return ajFilebuffNewPathWild(dir, wildfile);
}




/* @obsolete ajFileBuffNewDWE
** @rename ajFilebuffNewPathWildExclude
*/

__deprecated AjPFilebuff ajFileBuffNewDWE(const AjPStr dir,
                                          const AjPStr wildfile,
                                          const AjPStr exclude)
{
    return ajFilebuffNewPathWildExclude(dir, wildfile, exclude);
}




/* @obsolete ajFileBuffDel
** @rename ajFilebuffDel
*/
__deprecated void ajFileBuffDel(AjPFilebuff* Pbuff)
{
    ajFilebuffDel(Pbuff);
}




/* @obsolete ajFileBuffSetFile
** @remove Use ajFilebuffReopenFile or ajFilebuffClear
*/
__deprecated AjBool ajFileBuffSetFile(AjPFilebuff* pthys,
                                      AjPFile file, AjBool samefile)
{
    if(samefile)
        ajFilebuffClear(*pthys, -1);
    else
        ajFilebuffReopenFile(pthys, file);

    return ajTrue;
}




/* @obsolete ajFileBuffClear
** @rename ajFilebuffClear
*/
__deprecated void ajFileBuffClear(AjPFilebuff buff, ajint lines)
{
    ajFilebuffClear(buff, lines);
}




/* @obsolete ajFileBuffClearStore
** @rename ajFilebuffClearStore
*/
__deprecated void ajFileBuffClearStore(AjPFilebuff buff, ajint lines,
                                       const AjPStr rdline, AjBool store, AjPStr *astr)
{
    ajFilebuffClearStore(buff, lines, rdline, store, astr);

    return;
}




/* @obsolete ajFileBuffFix
** @rename ajFilebuffFix
*/
__deprecated void ajFileBuffFix(AjPFilebuff buff)
{
    ajFilebuffFix(buff);

    return;
}




/* @obsolete ajFileBuffReset
** @rename ajFilebuffReset
*/
__deprecated void ajFileBuffReset(AjPFilebuff buff)
{
    ajFilebuffReset(buff);

    return;
}




/* @obsolete ajFileBuffResetPos
** @rename ajFilebuffResetPos
*/
__deprecated void ajFileBuffResetPos(AjPFilebuff buff)
{
    ajFilebuffResetPos(buff);

    return;
}




/* @obsolete ajFileBuffResetStore
** @rename ajFilebuffResetStore
*/
__deprecated void ajFileBuffResetStore(AjPFilebuff buff,
                                       AjBool store, AjPStr *astr)
{
    ajFilebuffResetStore(buff, store, astr);

    return;
}




/* @obsolete ajFileBuffBuff
** @rename ajFilebuffSetBuffered
*/
__deprecated AjBool ajFileBuffBuff(AjPFilebuff buff)
{
    return ajFilebuffSetBuffered(buff);
}




/* @obsolete ajFileBuffNobuff
** @rename ajFilebuffSetUnbuffered
*/
__deprecated AjBool ajFileBuffNobuff(AjPFilebuff buff)
{
    return ajFilebuffSetUnbuffered(buff);
}




/* @obsolete ajFileBuffStripHtmlPre
** @rename ajFileBuffHtmlPre
*/
__deprecated AjBool ajFileBuffStripHtmlPre(AjPFilebuff buff)
{
    return ajFilebuffHtmlPre(buff);
}




/* @obsolete ajFileBuffStripHtml
** @rename ajFilebuffHtmlStrip
*/
__deprecated void ajFileBuffStripHtml(AjPFilebuff buff)
{
    ajFilebuffHtmlStrip(buff);

    return;
}




/* @obsolete ajFileBuffLoadC
** @rename ajFilebuffLoadC
*/
__deprecated void ajFileBuffLoadC(AjPFilebuff buff, const char* line)
{
    ajFilebuffLoadC(buff, line);

    return;
}




/* @obsolete ajFileBuffLoadS
** @rename ajFilebuffLoadS
*/
__deprecated void ajFileBuffLoadS(AjPFilebuff buff, const AjPStr line)
{
    ajFilebuffLoadS(buff, line);

    return;
}




/* @obsolete ajFileBuffLoad
** @rename ajFilebuffLoadAll
*/
__deprecated void ajFileBuffLoad(AjPFilebuff buff)
{
    ajFilebuffLoadAll(buff);
}




/* @obsolete ajFileBuffFile
** @rename ajFilebuffGetFile
*/
__deprecated AjPFile ajFileBuffFile(const AjPFilebuff buff)
{
    return ajFilebuffGetFile(buff);
}




/* @obsolete ajFileBuffSize
** @rename ajFileValueBuffsize
*/
__deprecated ajint ajFileBuffSize(void)
{
    return ajFileValueBuffsize();
}




/* @obsolete ajFileBuffFp
** @rename ajFilebuffGetFileptr
*/
__deprecated FILE* ajFileBuffFp(const AjPFilebuff buff)
{
    return ajFilebuffGetFileptr(buff);
}




/* @obsolete ajFileBuffIsBuffered
** @rename ajFilebuffIsBuffered
*/
__deprecated AjBool ajFileBuffIsBuffered(const AjPFilebuff buff)
{
    return ajFilebuffIsBuffered(buff);
}




/* @obsolete ajFileBuffEmpty
** @rename ajFilebuffIsEmpty
*/
__deprecated AjBool ajFileBuffEmpty(const AjPFilebuff buff)
{
    return ajFilebuffIsEmpty(buff);
}




/* @obsolete ajFileBuffEnd
** @rename ajFilebuffIsEnded
*/
__deprecated AjBool ajFileBuffEnd(const AjPFilebuff buff)
{
    return ajFilebuffIsEnded(buff);
}




/* @obsolete ajFileBuffEof
** @rename ajFilebuffIsEof
*/
__deprecated AjBool ajFileBuffEof(const AjPFilebuff buff)
{
    return ajFilebuffIsEof(buff);
}




/* @obsolete ajFileBuffTrace
** @rename ajFilebuffTrace
*/
__deprecated void ajFileBuffTrace(const AjPFilebuff buff)
{
    ajFilebuffTrace(buff);

    return;
}




/* @obsolete ajFileBuffTraceFull
** @rename ajFilebuffTraceFull
*/
__deprecated void ajFileBuffTraceFull(const AjPFilebuff buff, size_t nlines,
                                      size_t nfree)
{
    ajFilebuffTraceFull(buff, nlines, nfree);

    return;
}




/* @obsolete ajFileBuffPrint
** @rename ajFilebuffTraceTitle
*/
__deprecated void ajFileBuffPrint(const AjPFilebuff buff, const char* title)
{
    ajFilebuffTraceTitle(buff, title);

    return;
}




/* @obsolete ajOutfileNew
** @rename ajOutfileNewNameS
*/
__deprecated AjPOutfile ajOutfileNew(const AjPStr name)
{
    return ajOutfileNewNameS(name);
}




/* @obsolete ajOutfileDel
** @rename ajOutfileClose
*/
__deprecated void ajOutfileDel(AjPOutfile* pthis)
{
    ajOutfileClose(pthis);
    return;
}




/* @obsolete ajOutfileFile
** @rename ajOutfileGetFile
*/
__deprecated AjPFile ajOutfileFile (const AjPOutfile thys)
{
    return thys->File;
}




/* @obsolete ajOutfileFp
** @rename ajOutfileGetFileptr
*/
__deprecated FILE* ajOutfileFp (const AjPOutfile thys)
{
    return ajOutfileGetFileptr(thys);
}




/* @obsolete ajOutfileFormat
** @rename ajOutfileGetFormat
*/
__deprecated AjPStr ajOutfileFormat (const AjPOutfile thys)
{
    return thys->Formatstr;
}




/* @obsolete ajFileNameTrim
** @rename ajFilenameTrimPath
*/
__deprecated AjBool ajFileNameTrim(AjPStr* fname)
{
    char *p;

    if((p = strrchr(ajStrGetPtr(*fname),(ajint)SLASH_CHAR)))
    {
        ajStrAssignC(&fileTmpStr,p+1);
        ajStrAssignS(fname,fileTmpStr);
    }

    return ajTrue;
}




/* @obsolete ajFileDirTrim
** @rename ajFilenameTrimPath
*/
__deprecated AjBool ajFileDirTrim(AjPStr* name)
{
    return ajFilenameTrimPath(name);
}




/* @obsolete ajFileDirExtnTrim
** @rename ajFilenameTrimPathExt
*/
__deprecated AjBool ajFileDirExtnTrim(AjPStr* name)
{
    return ajFilenameTrimPathExt(name);
}




/* @obsolete ajFileDirPath
** @rename ajDirnameFillPath
*/
__deprecated AjBool ajFileDirPath(AjPStr* dir)
{
    return ajDirnameFillPath(dir);
}




/* @obsolete ajFileDirFix
** @rename ajDirnameFix
*/
__deprecated void ajFileDirFix(AjPStr* dir)
{
    ajDirnameFix(dir);

    return;
}




/* @obsolete ajFileDir
** @rename ajDirnameFixExists
*/
__deprecated AjBool ajFileDir(AjPStr* dir)
{
    return ajDirnameFixExists(dir);
}




/* @obsolete ajFileDirUp
** @rename ajDirnameUp
*/
__deprecated AjBool ajFileDirUp(AjPStr* dir)
{
    return ajDirnameUp(dir);
}




/* @obsolete ajFileScan
** @remove Use ajFilelistAddPathWild and others
*/
__deprecated ajint ajFileScan(const AjPStr path,
                              const AjPStr filename, AjPList *result,
                              AjBool show, AjBool dolist, AjPList *list,
                              AjPList rlist, AjBool recurs, AjPFile outf)
{
    (void)dolist;
    (void)list;

    if(show)
    {
        ajDirnamePrintRecursiveIgnore(path, rlist, outf);

        return 0;
    }

    if(recurs)
        return ajFilelistAddPathWildRecursiveIgnore(*result,
                                                    path, filename, rlist);

    return ajFilelistAddPathWild(*result, path, filename);
}




/* @obsolete ajDirScan
** @remove Use ajFilelistAddPathWildDir
*/
__deprecated ajint ajDirScan(const AjPStr path,
                             const AjPStr filename, AjPList *result)
{
    return ajFilelistAddPathWildDir(*result, path, filename);
}




/* @obsolete ajFileGetwd
** @remove Use ajFileValueCwd
*/
__deprecated AjBool ajFileGetwd(AjPStr* dir)
{
    ajStrAssignS(dir, ajFileValueCwd());

    return ajTrue;
}

#endif