time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

from_symbolic.js (5920B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 'use strict';
     20 
     21 // MODULES //
     22 
     23 var lpad = require( '@stdlib/string/left-pad' );
     24 
     25 
     26 // VARIABLES //
     27 
     28 // Regular expression to parse a mask expression:
     29 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})$/;
     30 
     31 // Table of permission bit mask offsets:
     32 var PERMS = {
     33 	'r': 2, // read
     34 	'w': 1, // write
     35 	'x': 0  // execute
     36 };
     37 
     38 // Table of class indices in the octal format (e.g., `0o0077`):
     39 var WHO = {
     40 	's': 0, // special mode (ignored; see http://man7.org/linux/man-pages/man2/umask.2.html)
     41 	'u': 1, // user
     42 	'g': 2, // group
     43 	'o': 3  // other/non-group
     44 };
     45 
     46 
     47 // FUNCTIONS //
     48 
     49 /**
     50 * Returns a bit mask.
     51 *
     52 * @private
     53 * @param {NonNegativeInteger} offset - bit offset (right-to-left)
     54 * @returns {NonNegativeInteger} bit mask
     55 *
     56 * @example
     57 * var y = bitMask( 3 );
     58 * // returns 8
     59 */
     60 function bitMask( offset ) {
     61 	return ( 1 << offset )>>>0; // asm type annotation
     62 }
     63 
     64 /**
     65 * Sets a bit.
     66 *
     67 * @private
     68 * @param {NonNegativeInteger} value - value
     69 * @param {NonNegativeInteger} offset - bit offset (right-to-left)
     70 * @returns {NonNegativeInteger} updated value
     71 *
     72 * @example
     73 * var y = setBit( 8, 2 );
     74 */
     75 function setBit( value, offset ) {
     76 	return ( value | bitMask( offset ) )>>>0; // asm type annotation
     77 }
     78 
     79 /**
     80 * Clears a bit.
     81 *
     82 * @private
     83 * @param {NonNegativeInteger} value - value
     84 * @param {NonNegativeInteger} offset - bit offset (right-to-left)
     85 * @returns {NonNegativeInteger} updated value
     86 */
     87 function clearBit( value, offset ) {
     88 	return ( value & ~bitMask( offset ) )>>>0; // asm type annotation
     89 }
     90 
     91 
     92 // MAIN //
     93 
     94 /**
     95 * Converts a mask expression in symbolic notation to an integer.
     96 *
     97 * @private
     98 * @param {NonNegativeInteger} mask - current mask
     99 * @param {string} expr - mask expression
    100 * @returns {(NonNegativeInteger|Error)} integer mask or parse error
    101 */
    102 function fromSymbolic( mask, expr ) {
    103 	var digits;
    104 	var parts;
    105 	var perm;
    106 	var who;
    107 	var tmp;
    108 	var idx;
    109 	var op;
    110 	var w;
    111 	var o;
    112 	var i;
    113 	var j;
    114 	var k;
    115 
    116 	// Split the mask into octal digits (e.g., [ '0', '0', '7', '7' ]):
    117 	digits = lpad( mask.toString( 8 ), 4, '0' ).split( '' );
    118 
    119 	// Convert each octal digit to an integer value:
    120 	for ( i = 0; i < digits.length; i++ ) {
    121 		digits[ i ] = parseInt( digits[ i ], 10 );
    122 	}
    123 
    124 	// See if we can easily split the mask into separate mask expressions (e.g., `u+x,g=rw,o=` => [ 'u+x', 'g=rw', 'o=' ] ):
    125 	parts = expr.split( ',' );
    126 
    127 	// For each expression, split into "class", "operator", and "symbols" and update the mask octal digits:
    128 	for ( i = 0; i < parts.length; i++ ) {
    129 		tmp = parts[ i ].match( RE_MASK_EXPRESSION );
    130 		if ( tmp === null ) {
    131 			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 + '`.' );
    132 		}
    133 		// Extract the expression parts:
    134 		who = tmp[ 1 ];
    135 		if ( who === '' ) {
    136 			// If a user class is not specified (e.g., `+x`), "ugo" (user, group, other) is implied...
    137 			who = 'ugo';
    138 		} else {
    139 			// Replace `a` (all) user class letter with "ugo" (user, group, other) equivalent...
    140 			w = '';
    141 			for ( k = 0; k < who.length; k++ ) {
    142 				if ( who[ k ] === 'a' ) {
    143 					w += 'ugo';
    144 				} else {
    145 					w += who[ k ];
    146 				}
    147 			}
    148 			who = w;
    149 		}
    150 		op = tmp[ 2 ];
    151 		perm = tmp[ 3 ];
    152 
    153 		// 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.
    154 
    155 		// Disable permissions...
    156 		if ( op === '-' ) {
    157 			if ( perm === '' ) {
    158 				// The `-` operation by itself does not change any bits...
    159 				continue;
    160 			}
    161 			for ( j = 0; j < perm.length; j++ ) {
    162 				o = PERMS[ perm[j] ];
    163 				for ( k = 0; k < who.length; k++ ) {
    164 					idx = WHO[ who[k] ];
    165 					digits[ idx ] = setBit( digits[ idx ], o ); // to disable, we flip on mask bits
    166 				}
    167 			}
    168 		}
    169 		// Enable permissions...
    170 		else if ( op === '+' ) {
    171 			if ( perm === '' ) {
    172 				// The `+` operation by itself does not change any bits...
    173 				continue;
    174 			}
    175 			for ( j = 0; j < perm.length; j++ ) {
    176 				o = PERMS[ perm[j] ];
    177 				for ( k = 0; k < who.length; k++ ) {
    178 					idx = WHO[ who[k] ];
    179 					digits[ idx ] = clearBit( digits[ idx ], o ); // to enable, we clear mask bits
    180 				}
    181 			}
    182 		}
    183 		// Disable all permissions by flipping on all permission mask bits...
    184 		else if ( perm === '' ) { // op === '='
    185 			for ( k = 0; k < who.length; k++ ) {
    186 				idx = WHO[ who[k] ];
    187 				digits[ idx ] = 7;
    188 			}
    189 		}
    190 		// Explicitly set permissions...
    191 		else { // op === '='
    192 			// First, disable all permissions by flipping on all permission mask bits...
    193 			for ( k = 0; k < who.length; k++ ) {
    194 				idx = WHO[ who[k] ];
    195 				digits[ idx ] = 7;
    196 			}
    197 			// Then, explicitly enable permissions by clearing mask bits...
    198 			for ( j = 0; j < perm.length; j++ ) {
    199 				o = PERMS[ perm[j] ];
    200 				for ( k = 0; k < who.length; k++ ) {
    201 					idx = WHO[ who[k] ];
    202 					digits[ idx ] = clearBit( digits[ idx ], o );
    203 				}
    204 			}
    205 		}
    206 	}
    207 	// Convert the digits to an integer value...
    208 	for ( i = 0; i < digits.length; i++ ) {
    209 		digits[ i ] = digits[ i ].toString();
    210 	}
    211 	return parseInt( digits.join( '' ), 8 );
    212 }
    213 
    214 
    215 // EXPORTS //
    216 
    217 module.exports = fromSymbolic;