time-to-botec/squiggle/node_modules/@stdlib/process/umask/lib/from_symbolic.js

218 lines
5.8 KiB
JavaScript
Raw Normal View History

/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
// MODULES //
var lpad = require( '@stdlib/string/left-pad' );
// VARIABLES //
// Regular expression to parse a mask expression:
var RE_MASK_EXPRESSION = /^(u{0,1}g{0,1}o{0,1}a{0,1}|)([+\-=])(r{0,1}w{0,1}x{0,1})$/;
// Table of permission bit mask offsets:
var PERMS = {
'r': 2, // read
'w': 1, // write
'x': 0 // execute
};
// Table of class indices in the octal format (e.g., `0o0077`):
var WHO = {
's': 0, // special mode (ignored; see http://man7.org/linux/man-pages/man2/umask.2.html)
'u': 1, // user
'g': 2, // group
'o': 3 // other/non-group
};
// FUNCTIONS //
/**
* Returns a bit mask.
*
* @private
* @param {NonNegativeInteger} offset - bit offset (right-to-left)
* @returns {NonNegativeInteger} bit mask
*
* @example
* var y = bitMask( 3 );
* // returns 8
*/
function bitMask( offset ) {
return ( 1 << offset )>>>0; // asm type annotation
}
/**
* Sets a bit.
*
* @private
* @param {NonNegativeInteger} value - value
* @param {NonNegativeInteger} offset - bit offset (right-to-left)
* @returns {NonNegativeInteger} updated value
*
* @example
* var y = setBit( 8, 2 );
*/
function setBit( value, offset ) {
return ( value | bitMask( offset ) )>>>0; // asm type annotation
}
/**
* Clears a bit.
*
* @private
* @param {NonNegativeInteger} value - value
* @param {NonNegativeInteger} offset - bit offset (right-to-left)
* @returns {NonNegativeInteger} updated value
*/
function clearBit( value, offset ) {
return ( value & ~bitMask( offset ) )>>>0; // asm type annotation
}
// MAIN //
/**
* Converts a mask expression in symbolic notation to an integer.
*
* @private
* @param {NonNegativeInteger} mask - current mask
* @param {string} expr - mask expression
* @returns {(NonNegativeInteger|Error)} integer mask or parse error
*/
function fromSymbolic( mask, expr ) {
var digits;
var parts;
var perm;
var who;
var tmp;
var idx;
var op;
var w;
var o;
var i;
var j;
var k;
// Split the mask into octal digits (e.g., [ '0', '0', '7', '7' ]):
digits = lpad( mask.toString( 8 ), 4, '0' ).split( '' );
// Convert each octal digit to an integer value:
for ( i = 0; i < digits.length; i++ ) {
digits[ i ] = parseInt( digits[ i ], 10 );
}
// See if we can easily split the mask into separate mask expressions (e.g., `u+x,g=rw,o=` => [ 'u+x', 'g=rw', 'o=' ] ):
parts = expr.split( ',' );
// For each expression, split into "class", "operator", and "symbols" and update the mask octal digits:
for ( i = 0; i < parts.length; i++ ) {
tmp = parts[ i ].match( RE_MASK_EXPRESSION );
if ( tmp === null ) {
return new Error( 'invalid argument. Unable to parse mask expression. Ensure the expression is properly formatted, only uses the class letters "u", "g", "o", and "a", only uses the operators "+", "-", and "=", and only uses the permission symbols "r", "w", and "x". Value: `' + expr + '`.' );
}
// Extract the expression parts:
who = tmp[ 1 ];
if ( who === '' ) {
// If a user class is not specified (e.g., `+x`), "ugo" (user, group, other) is implied...
who = 'ugo';
} else {
// Replace `a` (all) user class letter with "ugo" (user, group, other) equivalent...
w = '';
for ( k = 0; k < who.length; k++ ) {
if ( who[ k ] === 'a' ) {
w += 'ugo';
} else {
w += who[ k ];
}
}
who = w;
}
op = tmp[ 2 ];
perm = tmp[ 3 ];
// NOTE: the algorithm below is from the perspective of the mask. If implemented for, say, `chmod`, the "disabling"/"enabling" logic would be reversed. Recall that a "1" in the mask, serves to **disable** a permission setting, not enable.
// Disable permissions...
if ( op === '-' ) {
if ( perm === '' ) {
// The `-` operation by itself does not change any bits...
continue;
}
for ( j = 0; j < perm.length; j++ ) {
o = PERMS[ perm[j] ];
for ( k = 0; k < who.length; k++ ) {
idx = WHO[ who[k] ];
digits[ idx ] = setBit( digits[ idx ], o ); // to disable, we flip on mask bits
}
}
}
// Enable permissions...
else if ( op === '+' ) {
if ( perm === '' ) {
// The `+` operation by itself does not change any bits...
continue;
}
for ( j = 0; j < perm.length; j++ ) {
o = PERMS[ perm[j] ];
for ( k = 0; k < who.length; k++ ) {
idx = WHO[ who[k] ];
digits[ idx ] = clearBit( digits[ idx ], o ); // to enable, we clear mask bits
}
}
}
// Disable all permissions by flipping on all permission mask bits...
else if ( perm === '' ) { // op === '='
for ( k = 0; k < who.length; k++ ) {
idx = WHO[ who[k] ];
digits[ idx ] = 7;
}
}
// Explicitly set permissions...
else { // op === '='
// First, disable all permissions by flipping on all permission mask bits...
for ( k = 0; k < who.length; k++ ) {
idx = WHO[ who[k] ];
digits[ idx ] = 7;
}
// Then, explicitly enable permissions by clearing mask bits...
for ( j = 0; j < perm.length; j++ ) {
o = PERMS[ perm[j] ];
for ( k = 0; k < who.length; k++ ) {
idx = WHO[ who[k] ];
digits[ idx ] = clearBit( digits[ idx ], o );
}
}
}
}
// Convert the digits to an integer value...
for ( i = 0; i < digits.length; i++ ) {
digits[ i ] = digits[ i ].toString();
}
return parseInt( digits.join( '' ), 8 );
}
// EXPORTS //
module.exports = fromSymbolic;