# Support the usual (i.e., bash compatible) `umask` UI. This reports or modifies the magic global
# `umask` variable which is monitored by the fish process.

# This table is indexed by the base umask value to be modified. Each digit represents the new umask
# value when the permissions to add are applied to the base umask value.
set __fish_umask_add_table 0101010 2002200 2103210 4440000 4541010 6442200 6543210

function __fish_umask_add
    set -l mask_digit $argv[1]
    set -l to_add $argv[2]

    set -l mask_table 0000000
    if test $mask_digit -gt 0
        set mask_table $__fish_umask_add_table[$mask_digit]
    end
    set -l new_vals (string split '' $mask_table)
    echo $new_vals[$to_add]
end

# This table is indexed by the base umask value to be modified. Each digit represents the new umask
# value when the permissions to remove are applied to the base umask value.
set __fish_umask_remove_table 1335577 3236767 3337777 5674567 5775577 7676767 7777777

function __fish_umask_remove
    set -l mask_digit $argv[1]
    set -l to_remove $argv[2]

    set -l mask_table 1234567
    if test $mask_digit -gt 0
        set mask_table $__fish_umask_remove_table[$mask_digit]
    end
    set -l new_vals (string split '' $mask_table)
    echo $new_vals[$to_remove]
end

# This returns the mask corresponding to allowing the permissions to allow. In other words it
# returns the inverse of the mask passed in.
set __fish_umask_set_table 6 5 4 3 2 1 0
function __fish_umask_set
    set -l to_set $argv[1]
    if test $to_set -eq 0
        return 7
    end
    echo $__fish_umask_set_table[$to_set]
end

# Given a umask string, possibly in symbolic mode, return an octal value with leading zeros.
# This function expects to be called with a single value.
function __fish_umask_parse
    # Test if already a valid octal mask. If so pad it with zeros and return it.
    # Note that umask values are always base 8 so they don't require a leading zero.
    if string match -qr '^0?[0-7]{1,3}$' -- $argv
        string sub -s -4 0000$argv
        return 0
    end

    # Test if argument is a valid symbolic mask. Note that the basic pattern allows one illegal
    # pattern: who and perms without a mode such as "urw". We test for that below after using the
    # pattern to split the rights then testing for that invalid combination.
    set -l basic_pattern '([ugoa]*)([=+-]?)([rwx]*)'
    if not string match -qr "^$basic_pattern(,$basic_pattern)*\$" -- $argv
        printf (_ "%s: Invalid mask '%s'\n") umask $argv >&2
        return 1
    end

    # Split umask into individual digits. We erase the first one because it should always be zero.
    set -l res (string split '' $umask)
    set -e res[1]

    for rights in (string split , $argv)
        set -l match (string match -r "^$basic_pattern\$" $rights)
        set -l scope $match[2]
        set -l mode $match[3]
        set -l perms $match[4]
        if test -n "$scope" -a -z "$mode"
            printf (_ "%s: Invalid mask '%s'\n") umask $argv >&2
            return 1
        end
        if test -z "$scope"
            set scope a
        end
        if test -z "$mode"
            set mode =
        end

        set -l scopes_to_modify
        string match -q '*u*' $scope
        and set scopes_to_modify 1
        string match -q '*g*' $scope
        and set -a scopes_to_modify 2
        string match -q '*o*' $scope
        and set -a scopes_to_modify 3
        string match -q '*a*' $scope
        and set scopes_to_modify 1 2 3

        set -l val 0
        if string match -q '*r*' $perms
            set val 4
        end
        if string match -q '*w*' $perms
            set val (math $val + 2)
        end
        if string match -q '*x*' $perms
            set val (math $val + 1)
        end

        for j in $scopes_to_modify
            switch $mode
                case '='
                    set res[$j] (__fish_umask_set $val)

                case '+'
                    set res[$j] (__fish_umask_add $res[$j] $val)

                case -
                    set res[$j] (__fish_umask_remove $res[$j] $val)
            end
        end
    end

    echo 0$res[1]$res[2]$res[3]
    return 0
end

function __fish_umask_print_symbolic
    set -l val
    set -l res ""
    set -l letter a u g o

    for i in 2 3 4
        set res $res,$letter[$i]=
        set val (echo $umask|cut -c $i)

        if contains $val 0 1 2 3
            set res {$res}r
        end

        if contains $val 0 1 4 5
            set res {$res}w
        end

        if contains $val 0 2 4 6
            set res {$res}x
        end

    end

    echo (string split -m 1 '' -- $res)[2]
end

function umask --description "Set default file permission mask"
    set -l options h/help p/as-command S/symbolic
    argparse -n umask $options -- $argv
    or return

    if set -q _flag_help
        __fish_print_help umask
        return 0
    end

    switch (count $argv)
        case 0
            set -q umask
            or set -g umask 113

            if set -q _flag_as_command
                echo umask $umask
            else if set -q _flag_symbolic
                __fish_umask_print_symbolic $umask
            else
                echo $umask
            end

        case 1
            if set -l parsed (__fish_umask_parse $argv)
                set -g umask $parsed
                return 0
            end
            return 1

        case '*'
            printf (_ '%s: Too many arguments\n') umask >&2
            return 1
    end
end
