Skip to content

Instantly share code, notes, and snippets.

@miyaokamarina
Last active June 8, 2021 22:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miyaokamarina/01d54eb89c73e46fef3578f0fd336403 to your computer and use it in GitHub Desktop.
Save miyaokamarina/01d54eb89c73e46fef3578f0fd336403 to your computer and use it in GitHub Desktop.
Sorta kinda UNIX chmod command permissions notation parser.
{
const MASK = 0;
const COPY = 1;
const ADD = 0;
const SUB = 1;
const SET = 2;
const Symbolic = (s, o, v) => {
s ??= { class: 0o7, umasked: true };
if (v.$ === COPY) {
return {
$: COPY,
umasked: s.umasked,
operator: o,
from: v.class,
to: s.class,
};
} else {
let c = s.class;
let f = v.flags;
// When operator is `-`, `x` and `X` flags have the same effect:
if (o === SUB && (f & 0b100_000)) {
f |= 0b000_001; // Imply `x` flag.
f &= 0b011_111; // Unset `X` flag.
}
let a = f & 7; // Unconditional flags for a class.
let b = 0b000; // Additional flags to set, if at least one class has the `x` bit.
// When the `X` flags is set:
if (f & 0b100_000) {
b = 0b001; // Define the conditional `x` bit, applying if at least one class has the `x` bit.
}
let x = 0; // Unconditional bit mask.
let y = 0; // Additional mask to set, if at least one class has the `x` bit.
// Owner permissions:
if (c & 0b100) {
x |= a << 6;
y |= b << 6;
}
// Group permissions:
if (c & 0b010) {
x |= a << 3;
y |= b << 3;
}
// Other permissions:
if (c & 0b001) {
x |= a;
y |= b;
}
// Sticky bit:
if (f & 0b001_000) x |= 0o1000;
// SUID/SGID:
if (f & 0b010_000) {
if (c & 0b100) x |= 0o4000;
if (c & 0b010) x |= 0o2000;
}
let add = 0;
let sub = 0;
if (o !== SUB) add = x;
if (o === SUB) sub = x;
if (o === SET) sub = 0o7777;
return {
$: MASK,
umasked: s.umasked,
add,
sub,
exe: y,
};
}
};
const Octal = (o, d) => {
if (o === ADD) return { $: MASK, umasked: false, add: d, sub: 0, exe: 0 };
if (o === SUB) return { $: MASK, umasked: false, add: 0, sub: d, exe: 0 };
return { $: MASK, umasked: false, add: d, sub: 0o7777, exe: 0 };
};
const Operator = o => {
return (
o === '+' ? ADD :
o === '-' ? SUB :
SET
);
};
const Digits = (a, b, c, d) => {
let x = a << 6 | b << 3 | c;
if (d !== null) {
x <<= 3;
x |= d;
}
return x;
};
const Classes = c => {
let x = 0;
for (let i = 0, l = c.length; i < l; i++) {
x |= c[i];
}
return x;
};
const Class = c => {
if (c === 'u') return 0b100;
if (c === 'g') return 0b010;
if (c === 'o') return 0b001;
return 0;
};
const Flags = f => {
let x = 0;
for (let i = 0, l = f.length; i < l; i++) {
x |= f[i];
}
if ((x & 0b100_001) === 0b100_001) {
return x & 0b011_111;
} else {
return x;
}
};
const Flag = f => {
return (
f === 'r' ? 0b000_100 :
f === 'w' ? 0b000_010 :
f === 'x' ? 0b000_001 :
f === 'X' ? 0b100_000 :
f === 's' ? 0b010_000 :
f === 't' ? 0b001_000 :
0b000_000
);
};
}
Root = _ l:List? _ { return l ?? [] }
//
List = h:Head t:Tail { return [h, ...t] }
Head = Change
Tail = (___ c:Change { return c })*
//
Change = Octal / Symbolic
Symbolic = s:Scope? o:Operator v:Value { return Symbolic(s, o, v) }
Octal = o:Operator? d:Digits { return Octal(o, d) }
//
Scope = ClassScope / EveryScope
ClassScope = c:Classes { return { umasked: false, class: c } }
EveryScope = 'a' { return { umasked: false, class: 7 } }
Operator = o:[+=-] { return Operator(o) }
Value = ClassValue / DigitValue / FlagsValue
ClassValue = c:Class { return { $: COPY, class: c } }
DigitValue = d:Digit { return { $: MASK, flags: d } }
FlagsValue = f:Flags { return { $: MASK, flags: f } }
Digits = a:Digit b:Digit c:Digit d:Digit? { return Digits(a, b, c, d) }
Digit = d:[0-7] { return d.charCodeAt() & 0o7 }
//
Classes = c:Class+ { return Classes(c) }
Class = c:[ugo] { return Class(c) }
Flags = f:Flag* { return Flags(f) }
Flag = f:[rwxXst] { return Flag(f) }
//
___ = _ ',' _ / __
__ = [ \t\r\n]+
_ = __?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment