Source code for xonsh.xoreutils.umask

"""Implements a umask command for xonsh."""

import os
import re

import xonsh.lib.lazyasd as xl


@xl.lazyobject
def symbolic_matcher():
    return re.compile(r"([ugo]*|a)([+-=])([^\s,]*)")


order = "rwx"
name_to_value = {"x": 1, "w": 2, "r": 4}
value_to_name = {v: k for k, v in name_to_value.items()}

class_to_loc = {"u": 6, "g": 3, "o": 0}  # how many bits to shift this class by
loc_to_class = {v: k for k, v in class_to_loc.items()}

function_map = {
    "+": lambda orig, new: orig | new,  # add the given permission
    "-": lambda orig, new: orig & ~new,  # remove the given permission
    "=": lambda orig, new: new,  # set the permissions exactly
}


[docs] def current_mask(): out = os.umask(0) os.umask(out) return out
[docs] def invert(perms): return 0o777 - perms
[docs] def get_oct_digits(mode): """ Separate a given integer into its three components """ if not 0 <= mode <= 0o777: raise ValueError("expected a value between 000 and 777") return {"u": (mode & 0o700) >> 6, "g": (mode & 0o070) >> 3, "o": mode & 0o007}
[docs] def from_oct_digits(digits): o = 0 for c, m in digits.items(): o |= m << class_to_loc[c] return o
[docs] def get_symbolic_rep_single(digit): """ Given a single octal digit, return the appropriate string representation. For example, 6 becomes "rw". """ o = "" for sym in "rwx": num = name_to_value[sym] if digit & num: o += sym digit -= num return o
[docs] def get_symbolic_rep(number): digits = get_oct_digits(number) return ",".join( f"{class_}={get_symbolic_rep_single(digits[class_])}" for class_ in "ugo" )
[docs] def get_numeric_rep_single(rep): """ Given a string representation, return the appropriate octal digit. For example, "rw" becomes 6. """ o = 0 for sym in set(rep): o += name_to_value[sym] return o
[docs] def single_symbolic_arg(arg, old=None): # we'll assume this always operates in the "forward" direction (on the # current permissions) rather than on the mask directly. if old is None: old = invert(current_mask()) match = symbolic_matcher.match(arg) if not match: raise ValueError(f"could not parse argument {arg!r}") class_, op, mask = match.groups() if class_ == "a": class_ = "ugo" invalid_chars = [i for i in mask if i not in name_to_value] if invalid_chars: raise ValueError(f"invalid mask {mask!r}") digits = get_oct_digits(old) new_num = get_numeric_rep_single(mask) for c in set(class_): digits[c] = function_map[op](digits[c], new_num) return from_oct_digits(digits)
[docs] def valid_numeric_argument(x): try: return len(x) == 3 and all(0 <= int(i) <= 7 for i in x) except: return False
[docs] def umask(args, stdin, stdout, stderr): if "-h" in args: print(UMASK_HELP, file=stdout) return 0 symbolic = False while "-S" in args: symbolic = True args.remove("-S") cur = current_mask() if len(args) == 0: # just print the current mask if symbolic: to_print = get_symbolic_rep(invert(cur)) else: to_print = oct(cur)[2:] while len(to_print) < 3: to_print = f"0{to_print}" print(to_print, file=stdout) return 0 else: num = [valid_numeric_argument(i) for i in args] if any(num): if not all(num): print("error: can't mix numeric and symbolic arguments", file=stderr) return 1 if len(num) != 1: print("error: can't have more than one numeric argument", file=stderr) return 1 for arg, isnum in zip(args, num): if isnum: cur = int(arg, 8) else: # this mode operates not on the mask, but on the current # _permissions_. so invert first, operate, then invert back. cur = invert(cur) for subarg in arg.split(","): try: cur = single_symbolic_arg(subarg, cur) except: print( f"error: could not parse argument: {subarg!r}", file=stderr ) return 1 cur = invert(cur) os.umask(cur)
UMASK_HELP = """Usage: umask [-S] [mode]... View or set the file creation mask. -S when printing, show output in symbolic format -h --help display this message and exit This version of umask was written in Python for tako: https://takoshell.org Based on the umask command from Bash: https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html""" if __name__ == "__main__": import sys umask(sys.argv, sys.stdin, sys.stdout, sys.stderr)