/********************************************************************************
*                                                                               *
*                  P a t h   N a m e   M a n i p u l a t i o n                  *
*                                                                               *
*********************************************************************************
* Copyright (C) 2000,2025 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* 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 3 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 program.  If not, see <http://www.gnu.org/licenses/>          *
********************************************************************************/
#ifndef FXPATH_H
#define FXPATH_H

namespace FX {

namespace FXPath {

  /// Options for FXPath::match
  enum {
    PathName   = 1,        /// No wildcard can ever match "/'
    NoEscape   = 2,        /// Backslashes don't quote special chars
    DotFile    = 4,        /// Leading "." is matched only explicitly
    LeadDir    = 8,        /// Ignore "/..." after a match
    CaseFold   = 16        /// Compare without regard to case
    };

  /**
  * Return root of absolute path; on Unix, this is just "/".
  * On Windows, this is of the form "C:\", "\\Server\Share\",
  * or "\", for paths starting with these strings.
  * The empty string is returned if the path is not absolute.
  */
  extern FXAPI FXString root(const FXString& file);

  /**
  * Return share name part from Windows UNC filename.
  * For example, the share() of "\\Server\Share\Document.doc"
  * yields "\\Server\Share".
  * The empty string is returned if path is not a network share.
  */
  extern FXAPI FXString share(const FXString& file);

  /**
  * Return server part from Windows UNC filename.
  * For example, the server() of "\\Server\Share\Document.doc"
  * yields "\\Server".
  * The empty string is returned if path is not a network share.
  */
  extern FXAPI FXString server(const FXString& file);

  /**
  * Return the drive letter in front of file name.
  * Returns empty string if no drive letter present.
  */
  extern FXAPI FXString drive(const FXString& file);

  /**
  * Return the directory part of the path name.
  * Note that directory() of "/bla/bla/") is "/bla/bla" and
  * NOT "/bla".
  * However, directory("/bla/bla") is "/bla" as we expect!
  * The "\\Server\Share\", "C:\" or "\" ("/" for Linux) are the
  * top of that path's file system, and will not be dropped.
  */
  extern FXAPI FXString directory(const FXString& file);

  /**
  * Return name and extension part of the path name.
  * Note that name("/bla/bla/") is "" and NOT "bla".
  * However, name("/bla/bla") is "bla" as we expect!
  */
  extern FXAPI FXString name(const FXString& file);

  /**
  * Return file stem, i.e. file name without the file
  * extension.  The extension is defined as the part past
  * the last '.' of the last path-component, if the '.' is
  * not the first character of the file name.
  */
  extern FXAPI FXString stem(const FXString& file);

  /**
  * Return extension part of the file name.
  * This returns the string after the last '.' in the last
  * path-component of the file, provided that the '.' was not
  * the first character of the path-component.
  * For example, extension() of "/usr/share/icons/folder.png"
  * returns "png", but extension() of "../path.name/.bashrc"
  * yields "".
  */
  extern FXAPI FXString extension(const FXString& file);

  /**
  * Return file name less the extension.
  */
  extern FXAPI FXString stripExtension(const FXString& file);

  /**
  * Perform tilde or environment variable expansion.
  * A prefix of the form "~" or "~user" is expanded to the (current) user's
  * home directory.
  * Environment variables of the form $VAR or ${VAR} (or %VAR% on Windows)
  * are expanded by substituting the value of the variable, recusively up to
  * given maximum level.
  * On Windows, only environment variables are expanded.
  */
  extern FXAPI FXString expand(const FXString& file,FXint level=4);

  /**
  * Convert a UNIX path to Windows conventions.
  *
  *  - Replace "/" with "\" for path separator.
  *  - Replace "${ENVVAR}" or "$ENVVAR" with "%ENVVAR%".
  *
  * Also, if path starting with a single "/", prefix it by:
  *
  *  - Drive letter "C:" from the current working directory.
  *  - Share name "\\Server\Share" from the current working directory.
  */
  extern FXAPI FXString convertToWindows(const FXString& path);

  /**
  * Convert Windows path to UNIX conventions:
  *
  *  - Replace "\" with "/" for path separator.
  *  - Replace "%ENVVAR%" with "${ENVVAR}".
  *
  * If prefixed by drive letter "C:" or share name "\\Server\Share":
  *
  *  - Drop drive letter "C:" from the front.
  *  - Drop share name "\\Server\Share" from the front.
  */
  extern FXAPI FXString convertFromWindows(const FXString& path);

  /**
  * Convert a foreign path to local conventions, changing path
  * separators "/" and "\", %VAR% and $VAR or ${VAR}.
  */
  extern FXAPI FXString convert(const FXString& path);
  
  /**
  * Convert a foreign path-list to local conventions, changing path
  * separators "/" and "\", %VAR% and $VAR or ${VAR}, and path-list
  * separators such as ":" and ";".
  */
  extern FXAPI FXString convertPathList(const FXString& path);

  /**
  * Contract path based on user name and environment variable.
  * The contract() function is in some sense the reverse of expand():
  * directory-part may be changed to "~" or "~user" when a username is
  * given [on Unix systems only], while other parts between path-separators
  * may be replaced by $var when a environment variable name is given.
  */
  extern FXAPI FXString contract(const FXString& file,const FXString& user=FXString::null,const FXString& var=FXString::null);

  /**
  * Simplify a file path; the path will remain relative if it was relative,
  * or absolute if it was absolute.  Also, a trailing "/" will be preserved
  * as this is important in other functions.  Finally, returned path should
  * be non-empty unless the input path was empty, and pathological paths
  * will be fixed.
  * The result is a shorter, canonical form of the original path.
  */
  extern FXAPI FXString simplify(const FXString& file);

  /// Return absolute path from current directory and file name
  extern FXAPI FXString absolute(const FXString& file);

  /// Return absolute path from base directory and file name
  extern FXAPI FXString absolute(const FXString& base,const FXString& file);

  /// Return relative path of file to the current directory
  extern FXAPI FXString relative(const FXString& file);

  /// Return relative path of file to given absolute base directory
  extern FXAPI FXString relative(const FXString& base,const FXString& file);

  /// Return path to directory above input directory name
  extern FXAPI FXString upLevel(const FXString& file);

  /// Return true if file positively inside base directory
  extern FXAPI FXbool isInside(const FXString& base,const FXString& file);

  /// Return true if file name is absolute
  extern FXAPI FXbool isAbsolute(const FXString& file);

  /// Return true if file name is relative
  extern FXAPI FXbool isRelative(const FXString& file);

  /// Return true if input directory is a top-level directory
  extern FXAPI FXbool isTopDirectory(const FXString& file);

  /**
  * Return true if input path is a file share of the form
  * "\\server\share".
  */
  extern FXAPI FXbool isShare(const FXString& file);

  /**
  * Return true if input path is a file server of the form
  * "\\server".
  */
  extern FXAPI FXbool isServer(const FXString& file);

  /**
  * Return true if input path is a hidden file or directory.
  */
  extern FXAPI FXbool isHidden(const FXString& file);

  /**
  * Return valid part of absolute path, i.e. leading
  * part of the path up to the first non-existing component.
  */
  extern FXAPI FXString validPath(const FXString& file);

  /**
  * Return true if absolute path is valid, i.e. all components
  * of the path exist.
  */
  extern FXAPI FXbool isValidPath(const FXString& file);

  /**
  * Enquote filename to make safe for shell.
  * If force is true, the return value will be quoted; otherwise,
  * quoting will be performed if file contains spaces or tabs, or
  * if the file is empty.
  * If not quoted, some special characters may need to be escaped.
  * The quoting rules on Windows are byzantine, and output may
  * look complicated.
  */
  extern FXAPI FXString enquote(const FXString& file,FXbool force=false);

  /**
  * Dequote filename to get original again, and previously escaped
  * characters will be unescaped.
  */
  extern FXAPI FXString dequote(const FXString& file);

  /**
  * Parse command string to argc and argv.
  * The input string may contain single- or double-quoted segments
  * or escape characters, as per shell rules.
  * Returns argc, the number of arguments identified in the input,
  * if successful.  If input is empty (NULL pointer or only whitespace),
  * or syntax errors are encountered, or system is out of memory, the
  * function returns 0.
  * Memory at argv may be released by a single call to freeElms().
  */
  extern FXAPI FXint parseArgs(FXchar**& argv,const FXchar* command);

  /**
  * Parse command string to argc and argv.
  * This is a convenience version for the parseArgv() above.
  */
  extern FXAPI FXint parseArgs(FXchar**& argv,const FXString& command);

  /**
  * Perform match of a string against a wildcard pattern.
  * The wildcard pattern may comprise ordinary characters or special
  * matching characters, as given below:
  *
  *  ?           Any single character.
  *  *           Zero or more of any character.
  *  [abc]       Any character from the set {a,b,c}.
  *  [^abc]      Any character BUT the ones from the set {a,b,c}.
  *  [!abc]      Any character BUT the ones from the set {a,b,c}.
  *  [a-zA-Z]    Matches single character, which must be one of a-z or A-Z.
  *  [^a-zA-Z]   Matches single character, which must be anything BUT a-z or A-Z.
  *  [!a-zA-Z]   Matches single character, which must be anything BUT a-z or A-Z.
  *  pat1|pat2   Match sub-pattern pat1 or pat2.
  *  pat1,pat2   Match sub-pattern pat1 or pat2.
  *  (pat1|pat2) Match sub-pattern pat1 or pat2; patterns may be nested.
  *  (pat1,pat2) Match sub-pattern pat1 or pat2; patterns may be nested.
  *
  * The special characters may be escaped to treat them as ordinary characters.
  * Matching may be influenced by a number of flags:
  *
  *  PathName      No wildcard can ever match /
  *  NoEscape      Backslashes don't quote special chars
  *  DotFile       Leading . is matched only explicitly, and not against wildcard
  *  LeadDir       Ignore /... after a match
  *  CaseFold      Compare without regard to case
  */
  extern FXAPI FXbool match(const FXchar* string,const FXchar* pattern="*",FXuint flags=(NoEscape|PathName));

  /**
  * Perform match of a string against a wildcard pattern.
  */
  extern FXAPI FXbool match(const FXString& string,const FXchar* pattern="*",FXuint flags=(NoEscape|PathName));

  /**
  * Perform match of a string against a wildcard pattern.
  */
  extern FXAPI FXbool match(const FXchar* string,const FXString& pattern,FXuint flags=(NoEscape|PathName));

  /**
  * Perform match of a string against a wildcard pattern.
  */
  extern FXAPI FXbool match(const FXString& string,const FXString& pattern,FXuint flags=(NoEscape|PathName));

  /**
  * Generate unique filename of the form pathnameXXX.ext, where
  * pathname.ext is the original input file, and XXX is a number,
  * possibly empty, that makes the file unique.
  */
  extern FXAPI FXString unique(const FXString& file);

  /**
  * Search path list for this file, return full path name for first occurrence.
  * Each path in the pathlist is expanded first.
  */
  extern FXAPI FXString search(const FXString& pathlist,const FXString& file);

  /**
  * Given search path list, return shortest relative path name that still
  * uniquely resolves to the given file name.
  */
  extern FXAPI FXString relativize(const FXString& pathlist,const FXString& file);

  /**
  * Check if the file has an extension from the list of known executable
  * extensions (Windows)
  */
  extern FXAPI FXbool hasExecExtension(const FXString& file);

  /**
  * Check if given name is controversial, i.e. reserved for other devices (Windows).
  */
  extern FXAPI FXbool isReservedName(const FXString& file);

  }

}

#endif
