/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-â
âvi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â Copyright 2022 Justine Alexandra Roberts Tunney                              â
â                                                                              â
â Permission to use, copy, modify, and/or distribute this software for         â
â any purpose with or without fee is hereby granted, provided that the         â
â above copyright notice and this permission notice appear in all copies.      â
â                                                                              â
â THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                â
â WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                â
â WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             â
â AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         â
â DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        â
â PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               â
â TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             â
â PERFORMANCE OF THIS SOFTWARE.                                                â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#include "libc/dce.h"
#include "libc/str/path.h"
#include "libc/str/str.h"

/**
 * Classifies file path name.
 *
 * For the purposes of this function, we always consider backslash
 * interchangeable with forward slash, even though the underlying
 * operating system might not. Therefore, for the sake of clarity,
 * remaining documentation will only use the forward slash.
 *
 * This function behaves the same on all platforms. For instance, this
 * function will categorize `C:/FOO.BAR` as a DOS path, even if you're
 * running on UNIX rather than DOS.
 *
 * If you wish to check if a pathname is absolute, in a manner that's
 * inclusive of DOS drive paths, DOS rooted paths, in addition to the
 * New Technology UNC paths, then you may do the following:
 *
 *     if (_classifypath(str) & _kPathAbs) { ... }
 *
 * To check if path is a relative path:
 *
 *     if (~_classifypath(str) & _kPathAbs) { ... }
 *
 * Please note the above check includes rooted paths such as `\foo`
 * which is considered absolute by MSDN and we consider it absolute
 * although, it's technically relative to the current drive letter.
 *
 * Please note that `/foo/bar` is an absolute path on Windows, even
 * though it's actually a rooted path that's considered relative to
 * current drive by WIN32.
 *
 * @return integer value that's one of following:
 *     - `0` if non-weird relative path e.g. `c`
 *     - `_kPathAbs` if absolute (or rooted dos) path e.g. `/â¯`
 *     - `_kPathDos` if `c:`, `d:foo` i.e. drive-relative path
 *     - `_kPathAbs|_kPathDos` if proper dos path e.g. `c:/foo`
 *     - `_kPathDos|_kPathDev` if dos device path e.g. `nul`, `conin$`
 *     - `_kPathAbs|_kPathWin` if `//c`, `//?c`, etc.
 *     - `_kPathAbs|_kPathWin|_kPathDev` if `//./â¯`, `//?/â¯`
 *     - `_kPathAbs|_kPathWin|_kPathDev|_kPathRoot` if `//.` or `//?`
 *     - `_kPathAbs|_kPathNt` e.g. `\??\\â¯` (undoc. strict backslash)
 * @see "The Definitive Guide on Win32 to NT Path Conversion", James
 *     Forshaw, Google Project Zero Blog, 2016-02-29
 * @see "Naming Files, Paths, and Namespaces", MSDN 01/04/2021
 */
int _classifypath(const char *s) {
  if (s) {
    switch (s[0]) {
      case 0:  // ""
        return 0;
      default:
        if (!SupportsWindows()) {
          return 0;
        }
        if (((((s[0] == 'a' || s[0] == 'A') &&    // aux
               (s[1] == 'u' || s[1] == 'U') &&    //
               (s[2] == 'x' || s[2] == 'X')) ||   //
              ((s[0] == 'p' || s[0] == 'P') &&    // prn
               (s[1] == 'r' || s[1] == 'R') &&    //
               (s[2] == 'n' || s[2] == 'N')) ||   //
              ((s[0] == 'n' || s[0] == 'N') &&    // nul
               (s[1] == 'u' || s[1] == 'U') &&    //
               (s[2] == 'l' || s[2] == 'L')) ||   //
              ((s[0] == 'c' || s[0] == 'C') &&    // con
               (s[1] == 'o' || s[1] == 'O') &&    //
               (s[2] == 'n' || s[2] == 'N'))) &&  //
             !s[3]) ||
            ((((s[0] == 'l' || s[0] == 'L') &&    // lpt
               (s[1] == 'p' || s[1] == 'P') &&    //
               (s[2] == 't' || s[2] == 'T')) ||   //
              ((s[0] == 'c' || s[0] == 'C') &&    // com
               (s[1] == 'o' || s[1] == 'O') &&    //
               (s[2] == 'm' || s[2] == 'M'))) &&  //
             ('1' <= s[3] && s[3] <= '9') &&      //
             !s[4])) {
          return _kPathDos | _kPathDev;
        }
        switch (s[1]) {
          case ':':
            switch (s[2]) {
              case 0:   // c:
              default:  // c:wutâ¯
                return _kPathDos;
              case '/':   // c:/â¯
              case '\\':  // c:\â¯
                return _kPathAbs | _kPathDos;
            }
          default:
            return 0;
        }
      case '\\':
        if (SupportsWindows()) {
          if (s[1] == '?' && s[2] == '?') {
            if (!s[3]) {
              return _kPathAbs | _kPathNt | _kPathRoot;  // \??\â¯
            } else if (s[3] == '\\') {
              return _kPathAbs | _kPathNt;  // \??\â¯
            }
          }
        }
        // fallthrough
      case '/':
        if (!SupportsWindows()) {
          return _kPathAbs;
        }
        switch (s[1]) {
          case 0:   // /
          default:  // /â¯
            return _kPathAbs;
          case '/':
          case '\\':
            switch (s[2]) {
              case 0:   // //
              default:  // //â¯
                return _kPathAbs | _kPathWin;
              case '.':
              case '?':
                switch (s[3]) {
                  case 0:  // //? or //.
                    return _kPathAbs | _kPathWin | _kPathDev | _kPathRoot;
                  default:  // //?â¯ or //.â¯
                    return _kPathAbs | _kPathWin;
                  case '/':
                  case '\\':  // //?/â¯ or //./â¯
                    return _kPathAbs | _kPathWin | _kPathDev;
                }
            }
        }
    }
  } else {
    return 0;
  }
}
