feat: 초기 프로젝트 설정 및 룰.md 파일 추가

This commit is contained in:
2025-07-28 09:53:31 +09:00
commit 09a4d38512
8165 changed files with 1021855 additions and 0 deletions

247
api.hyungi.net/node_modules/pm2/lib/tools/Config.js generated vendored Normal file
View File

@@ -0,0 +1,247 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var util = require('util');
/**
* Validator of configured file / commander options.
*/
var Config = module.exports = {
_errMsgs: {
'require': '"%s" is required',
'type' : 'Expect "%s" to be a typeof %s, but now is %s',
'regex' : 'Verify "%s" with regex failed, %s',
'max' : 'The maximum of "%s" is %s, but now is %s',
'min' : 'The minimum of "%s" is %s, but now is %s'
},
/**
* Schema definition.
* @returns {exports|*}
*/
get schema(){
// Cache.
if (this._schema) {
return this._schema;
}
// Render aliases.
this._schema = require('../API/schema');
for (var k in this._schema) {
if (k.indexOf('\\') > 0) {
continue;
}
var aliases = [
k.split('_').map(function(n, i){
if (i != 0 && n && n.length > 1) {
return n[0].toUpperCase() + n.slice(1);
}
return n;
}).join('')
];
if (this._schema[k].alias && Array.isArray(this._schema[k].alias)) {
// If multiple aliases, merge
this._schema[k].alias.forEach(function(alias) {
aliases.splice(0, 0, alias);
});
}
else if (this._schema[k].alias)
aliases.splice(0, 0, this._schema[k].alias);
this._schema[k].alias = aliases;
}
return this._schema;
}
};
/**
* Filter / Alias options
*/
Config.filterOptions = function(cmd) {
var conf = {};
var schema = this.schema;
for (var key in schema) {
var aliases = schema[key].alias;
aliases && aliases.forEach(function(alias){
if (typeof(cmd[alias]) !== 'undefined') {
conf[key] || (conf[key] = cmd[alias]);
}
});
}
return conf;
};
/**
* Verify JSON configurations.
* @param {Object} json
* @returns {{errors: Array, config: {}}}
*/
Config.validateJSON = function(json){
// clone config
var conf = Object.assign({}, json),
res = {};
this._errors = [];
var regexKeys = {}, defines = this.schema;
for (var sk in defines) {
// Pick up RegExp keys.
if (sk.indexOf('\\') >= 0) {
regexKeys[sk] = false;
continue;
}
var aliases = defines[sk].alias;
aliases && aliases.forEach(function(alias){
conf[sk] || (conf[sk] = json[alias]);
})
var val = conf[sk];
delete conf[sk];
// Validate key-value pairs.
if (val === undefined ||
val === null ||
((val = this._valid(sk, val)) === null)) {
// If value is not defined
// Set default value (via schema.json)
if (typeof(defines[sk].default) !== 'undefined')
res[sk] = defines[sk].default;
continue;
}
//console.log(sk, val, val === null, val === undefined);
res[sk] = val;
}
// Validate RegExp values.
var hasRegexKey = false;
for (var k in regexKeys) {
hasRegexKey = true;
regexKeys[k] = new RegExp(k);
}
if (hasRegexKey) {
for (var k in conf) {
for (var rk in regexKeys) {
if (regexKeys[rk].test(k))
if (this._valid(k, conf[k], defines[rk])) {
res[k] = conf[k];
delete conf[k];
}
}
}
}
return {errors: this._errors, config: res};
};
/**
* Validate key-value pairs by specific schema
* @param {String} key
* @param {Mixed} value
* @param {Object} sch
* @returns {*}
* @private
*/
Config._valid = function(key, value, sch){
var sch = sch || this.schema[key],
scht = typeof sch.type == 'string' ? [sch.type] : sch.type;
// Required value.
var undef = typeof value == 'undefined';
if(this._error(sch.require && undef, 'require', key)){
return null;
}
// If undefined, make a break.
if (undef) {
return null;
}
// Wrap schema types.
scht = scht.map(function(t){
return '[object ' + t[0].toUpperCase() + t.slice(1) + ']'
});
// Typeof value.
var type = Object.prototype.toString.call(value), nt = '[object Number]';
// Auto parse Number
if (type != '[object Boolean]' && scht.indexOf(nt) >= 0 && !isNaN(value)) {
value = parseFloat(value);
type = nt;
}
// Verify types.
if (this._error(!~scht.indexOf(type), 'type', key, scht.join(' / '), type)) {
return null;
}
// Verify RegExp if exists.
if (this._error(type == '[object String]' && sch.regex && !(new RegExp(sch.regex)).test(value),
'regex', key, sch.desc || ('should match ' + sch.regex))) {
return null;
}
// Verify maximum / minimum of Number value.
if (type == '[object Number]') {
if (this._error(typeof sch.max != 'undefined' && value > sch.max, 'max', key, sch.max, value)) {
return null;
}
if (this._error(typeof sch.min != 'undefined' && value < sch.min, 'min', key, sch.min, value)) {
return null;
}
}
// If first type is Array, but current is String, try to split them.
if(scht.length > 1 && type != scht[0] && type == '[object String]'){
if(scht[0] == '[object Array]') {
// unfortunately, js does not support lookahead RegExp (/(?<!\\)\s+/) now (until next ver).
value = value.split(/([\w\-]+\="[^"]*")|([\w\-]+\='[^']*')|"([^"]*)"|'([^']*)'|\s/)
.filter(function(v){
return v && v.trim();
});
}
}
// Custom types: sbyte && stime.
if(sch.ext_type && type == '[object String]' && value.length >= 2) {
var seed = {
'sbyte': {
'G': 1024 * 1024 * 1024,
'M': 1024 * 1024,
'K': 1024
},
'stime': {
'h': 60 * 60 * 1000,
'm': 60 * 1000,
's': 1000
}
}[sch.ext_type];
if(seed){
value = parseFloat(value.slice(0, -1)) * (seed[value.slice(-1)]);
}
}
return value;
};
/**
* Wrap errors.
* @param {Boolean} possible A value indicates whether it is an error or not.
* @param {String} type
* @returns {*}
* @private
*/
Config._error = function(possible, type){
if (possible) {
var args = Array.prototype.slice.call(arguments);
args.splice(0, 2, this._errMsgs[type]);
this._errors && this._errors.push(util.format.apply(null, args));
}
return possible;
}

View File

@@ -0,0 +1,20 @@
'use strict';
function posix(path) {
return path.charAt(0) === '/';
}
function win32(path) {
// https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56
var splitDeviceRe = /^([a-zA-Z]:|[\\/]{2}[^\\/]+[\\/]+[^\\/]+)?([\\/])?([\s\S]*?)$/;
var result = splitDeviceRe.exec(path);
var device = result[1] || '';
var isUnc = Boolean(device && device.charAt(1) !== ':');
// UNC paths are always absolute
return Boolean(result[2] || isUnc);
}
module.exports = process.platform === 'win32' ? win32 : posix;
module.exports.posix = posix;
module.exports.win32 = win32;

View File

@@ -0,0 +1,101 @@
var fs = require('fs');
var path = require('path');
/*
options: {
utimes: false, // Boolean | Object, keep utimes if true
mode: false, // Boolean | Number, keep file mode if true
cover: true, // Boolean, cover if file exists
filter: true, // Boolean | Function, file filter
}
*/
function copydirSync(from, to, options) {
if (typeof options === 'function') {
options = {
filter: options
};
}
if(typeof options === 'undefined') options = {};
if(typeof options.cover === 'undefined') {
options.cover = true;
}
options.filter = typeof options.filter === 'function' ? options.filter : function(state, filepath, filename) {
return options.filter;
};
var stats = fs.lstatSync(from);
var statsname = stats.isDirectory() ? 'directory' :
stats.isFile() ? 'file' :
stats.isSymbolicLink() ? 'symbolicLink' :
'';
var valid = options.filter(statsname, from, path.dirname(from), path.basename(from));
if (statsname === 'directory' || statsname === 'symbolicLink') {
// Directory or SymbolicLink
if(valid) {
try {
fs.statSync(to);
} catch(err) {
if(err.code === 'ENOENT') {
fs.mkdirSync(to);
options.debug && console.log('>> ' + to);
} else {
throw err;
}
}
rewriteSync(to, options, stats);
if (statsname != 'symbolicLink')
listDirectorySync(from, to, options);
}
} else if(stats.isFile()) {
// File
if(valid) {
if(options.cover) {
writeFileSync(from, to, options, stats);
} else {
try {
fs.statSync(to);
} catch(err) {
if(err.code === 'ENOENT') {
writeFileSync(from, to, options, stats);
} else {
throw err;
}
}
}
}
} else {
throw new Error('stats invalid: '+ from);
}
};
function listDirectorySync(from, to, options) {
var files = fs.readdirSync(from);
copyFromArraySync(files, from, to, options);
}
function copyFromArraySync(files, from, to, options) {
if(files.length === 0) return true;
var f = files.shift();
copydirSync(path.join(from, f), path.join(to, f), options);
copyFromArraySync(files, from, to, options);
}
function writeFileSync(from, to, options, stats) {
fs.writeFileSync(to, fs.readFileSync(from, 'binary'), 'binary');
options.debug && console.log('>> ' + to);
rewriteSync(to, options, stats);
}
function rewriteSync(f, options, stats, callback) {
if(options.cover) {
var mode = options.mode === true ? stats.mode : options.mode;
var utimes = options.utimes === true ? {
atime: stats.atime,
mtime: stats.mtime
} : options.utimes;
mode && fs.chmodSync(f, mode);
utimes && fs.utimesSync(f, utimes.atime, utimes.mtime);
}
return true;
}
module.exports = copydirSync;

View File

@@ -0,0 +1,19 @@
const fs = require('fs');
const Path = require('path');
const deleteFolderRecursive = function(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach((file, index) => {
const curPath = Path.join(path, file);
if (fs.lstatSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
};
module.exports = deleteFolderRecursive

View File

@@ -0,0 +1,74 @@
'use strict';
var path = require('path')
, fs = require('fs');
/**
* Attempt to somewhat safely parse the JSON.
*
* @param {String} data JSON blob that needs to be parsed.
* @returns {Object|false} Parsed JSON or false.
* @api private
*/
function parse(data) {
data = data.toString('utf-8');
//
// Remove a possible UTF-8 BOM (byte order marker) as this can lead to parse
// values when passed in to the JSON.parse.
//
if (data.charCodeAt(0) === 0xFEFF) data = data.slice(1);
try { return JSON.parse(data); }
catch (e) { return false; }
}
/**
* Find package.json files.
*
* @param {String|Object} root The root directory we should start searching in.
* @returns {Object} Iterator interface.
* @api public
*/
module.exports = function find(root) {
root = root || process.cwd();
if (typeof root !== "string") {
if (typeof root === "object" && typeof root.filename === 'string') {
root = root.filename;
} else {
throw new Error("Must pass a filename string or a module object to finder");
}
}
return {
/**
* Return the parsed package.json that we find in a parent folder.
*
* @returns {Object} Value, filename and indication if the iteration is done.
* @api public
*/
next: function next() {
if (root.match(/^(\w:\\|\/)$/)) return {
value: undefined,
filename: undefined,
done: true
};
var file = path.join(root, 'package.json')
, data;
root = path.resolve(root, '..');
if (fs.existsSync(file) && (data = parse(fs.readFileSync(file)))) {
data.__path = file;
return {
value: data,
filename: file,
done: false
};
}
return next();
}
};
};

72
api.hyungi.net/node_modules/pm2/lib/tools/fmt.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
// --------------------------------------------------------------------------------------------------------------------
//
// fmt.js - Command line output formatting.
//
// Copyright (c) 2012 Andrew Chilton - http://chilts.org/
// Written by Andrew Chilton <andychilton@gmail.com>
//
// License: http://opensource.org/licenses/MIT
//
// --------------------------------------------------------------------------------------------------------------------
var util = require('util');
// --------------------------------------------------------------------------------------------------------------------
var sep = '===============================================================================';
var line = '-------------------------------------------------------------------------------';
var field = ' ';
// --------------------------------------------------------------------------------------------------------------------
// separator
module.exports.separator = function() {
console.log(sep);
};
// alias the above
module.exports.sep = module.exports.separator;
// line
module.exports.line = function() {
console.log(line);
};
// title
module.exports.title = function(title) {
var out = '--- ' + title + ' ';
out += line.substr(out.length);
console.log(out);
};
// field
module.exports.field = function(key, value) {
console.log('' + key + field.substr(key.length) + ' : ' + value);
};
// subfield
module.exports.subfield = function(key, value) {
console.log('- ' + key + field.substr(key.length + 2) + ' : ' + value);
};
// list item
module.exports.li = function(msg) {
console.log('* ' + msg);
};
// dump
module.exports.dump = function(data, name) {
if ( name ) {
console.log(name + ' :', util.inspect(data, false, null, true));
}
else {
console.log(util.inspect(data, false, null, true));
}
};
// msg
module.exports.msg = function(msg) {
console.log(msg);
};
// --------------------------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,94 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var fs = require('fs');
var max_bytes = 512;
module.exports = function(bytes, size) {
// Read the file with no encoding for raw buffer access.
if (size === undefined) {
var file = bytes;
try {
if(!fs.statSync(file).isFile()) return false;
} catch (err) {
// otherwise continue on
}
var descriptor = fs.openSync(file, 'r');
try {
bytes = Buffer.alloc(max_bytes);
size = fs.readSync(descriptor, bytes, 0, bytes.length, 0);
} finally {
fs.closeSync(descriptor);
}
}
// async version has a function instead of a `size`
else if (typeof size === "function") {
var file = bytes, callback = size;
fs.stat(file, function(err, stat) {
if (err || !stat.isFile()) return callback(null, false);
fs.open(file, 'r', function(err, descriptor){
if (err) return callback(err);
var bytes = Buffer.alloc(max_bytes);
// Read the file with no encoding for raw buffer access.
fs.read(descriptor, bytes, 0, bytes.length, 0, function(err, size, bytes){
fs.close(descriptor, function(err2){
if (err || err2)
return callback(err || err2);
return callback(null, isBinaryCheck(bytes, size));
});
});
});
});
}
return isBinaryCheck(bytes, size);
}
function isBinaryCheck(bytes, size) {
if (size === 0)
return false;
var suspicious_bytes = 0;
var total_bytes = Math.min(size, max_bytes);
if (size >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
// UTF-8 BOM. This isn't binary.
return false;
}
for (var i = 0; i < total_bytes; i++) {
if (bytes[i] === 0) { // NULL byte--it's binary!
return true;
}
else if ((bytes[i] < 7 || bytes[i] > 14) && (bytes[i] < 32 || bytes[i] > 127)) {
// UTF-8 detection
if (bytes[i] > 193 && bytes[i] < 224 && i + 1 < total_bytes) {
i++;
if (bytes[i] > 127 && bytes[i] < 192) {
continue;
}
}
else if (bytes[i] > 223 && bytes[i] < 240 && i + 2 < total_bytes) {
i++;
if (bytes[i] > 127 && bytes[i] < 192 && bytes[i + 1] > 127 && bytes[i + 1] < 192) {
i++;
continue;
}
}
suspicious_bytes++;
// Read at least 32 bytes before making a decision
if (i > 32 && (suspicious_bytes * 100) / total_bytes > 10) {
return true;
}
}
}
if ((suspicious_bytes * 100) / total_bytes > 10) {
return true;
}
return false;
}

752
api.hyungi.net/node_modules/pm2/lib/tools/json5.js generated vendored Normal file
View File

@@ -0,0 +1,752 @@
// json5.js
// Modern JSON. See README.md for details.
//
// This file is based directly off of Douglas Crockford's json_parse.js:
// https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js
var JSON5 = (typeof exports === 'object' ? exports : {});
JSON5.parse = (function () {
"use strict";
// This is a function that can parse a JSON5 text, producing a JavaScript
// data structure. It is a simple, recursive descent parser. It does not use
// eval or regular expressions, so it can be used as a model for implementing
// a JSON5 parser in other languages.
// We are defining the function inside of another function to avoid creating
// global variables.
var at, // The index of the current character
ch, // The current character
escapee = {
"'": "'",
'"': '"',
'\\': '\\',
'/': '/',
'\n': '', // Replace escaped newlines in strings w/ empty string
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t'
},
ws = [
' ',
'\t',
'\r',
'\n',
'\v',
'\f',
'\xA0',
'\uFEFF'
],
text,
error = function (m) {
// Call error when something is wrong.
var error = new SyntaxError();
error.message = m;
error.at = at;
error.text = text;
throw error;
},
next = function (c) {
// If a c parameter is provided, verify that it matches the current character.
if (c && c !== ch) {
error("Expected '" + c + "' instead of '" + ch + "'");
}
// Get the next character. When there are no more characters,
// return the empty string.
ch = text.charAt(at);
at += 1;
return ch;
},
peek = function () {
// Get the next character without consuming it or
// assigning it to the ch varaible.
return text.charAt(at);
},
identifier = function () {
// Parse an identifier. Normally, reserved words are disallowed here, but we
// only use this for unquoted object keys, where reserved words are allowed,
// so we don't check for those here. References:
// - http://es5.github.com/#x7.6
// - https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Variables
// - http://docstore.mik.ua/orelly/webprog/jscript/ch02_07.htm
var key = ch;
// Identifiers must start with a letter, _ or $.
if ((ch !== '_' && ch !== '$') &&
(ch < 'a' || ch > 'z') &&
(ch < 'A' || ch > 'Z')) {
error("Bad identifier");
}
// Subsequent characters can contain digits.
while (next() && (
ch === '_' || ch === '$' ||
(ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9'))) {
key += ch;
}
return key;
},
number = function () {
// Parse a number value.
var number,
sign = '',
string = '',
base = 10;
if (ch === '-' || ch === '+') {
sign = ch;
next(ch);
}
// support for Infinity (could tweak to allow other words):
if (ch === 'I') {
number = word();
if (typeof number !== 'number' || isNaN(number)) {
error('Unexpected word for number');
}
return (sign === '-') ? -number : number;
}
// support for NaN
if (ch === 'N' ) {
number = word();
if (!isNaN(number)) {
error('expected word to be NaN');
}
// ignore sign as -NaN also is NaN
return number;
}
if (ch === '0') {
string += ch;
next();
if (ch === 'x' || ch === 'X') {
string += ch;
next();
base = 16;
} else if (ch >= '0' && ch <= '9') {
error('Octal literal');
}
}
switch (base) {
case 10:
while (ch >= '0' && ch <= '9' ) {
string += ch;
next();
}
if (ch === '.') {
string += '.';
while (next() && ch >= '0' && ch <= '9') {
string += ch;
}
}
if (ch === 'e' || ch === 'E') {
string += ch;
next();
if (ch === '-' || ch === '+') {
string += ch;
next();
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
}
break;
case 16:
while (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {
string += ch;
next();
}
break;
}
if(sign === '-') {
number = -string;
} else {
number = +string;
}
if (!isFinite(number)) {
error("Bad number");
} else {
return number;
}
},
string = function () {
// Parse a string value.
var hex,
i,
string = '',
delim, // double quote or single quote
uffff;
// When parsing for string values, we must look for ' or " and \ characters.
if (ch === '"' || ch === "'") {
delim = ch;
while (next()) {
if (ch === delim) {
next();
return string;
} else if (ch === '\\') {
next();
if (ch === 'u') {
uffff = 0;
for (i = 0; i < 4; i += 1) {
hex = parseInt(next(), 16);
if (!isFinite(hex)) {
break;
}
uffff = uffff * 16 + hex;
}
string += String.fromCharCode(uffff);
} else if (ch === '\r') {
if (peek() === '\n') {
next();
}
} else if (typeof escapee[ch] === 'string') {
string += escapee[ch];
} else {
break;
}
} else if (ch === '\n') {
// unescaped newlines are invalid; see:
// https://github.com/aseemk/json5/issues/24
// invalid unescaped chars?
break;
} else {
string += ch;
}
}
}
error("Bad string");
},
inlineComment = function () {
// Skip an inline comment, assuming this is one. The current character should
// be the second / character in the // pair that begins this inline comment.
// To finish the inline comment, we look for a newline or the end of the text.
if (ch !== '/') {
error("Not an inline comment");
}
do {
next();
if (ch === '\n' || ch === '\r') {
next();
return;
}
} while (ch);
},
blockComment = function () {
// Skip a block comment, assuming this is one. The current character should be
// the * character in the /* pair that begins this block comment.
// To finish the block comment, we look for an ending */ pair of characters,
// but we also watch for the end of text before the comment is terminated.
if (ch !== '*') {
error("Not a block comment");
}
do {
next();
while (ch === '*') {
next('*');
if (ch === '/') {
next('/');
return;
}
}
} while (ch);
error("Unterminated block comment");
},
comment = function () {
// Skip a comment, whether inline or block-level, assuming this is one.
// Comments always begin with a / character.
if (ch !== '/') {
error("Not a comment");
}
next('/');
if (ch === '/') {
inlineComment();
} else if (ch === '*') {
blockComment();
} else {
error("Unrecognized comment");
}
},
white = function () {
// Skip whitespace and comments.
// Note that we're detecting comments by only a single / character.
// This works since regular expressions are not valid JSON(5), but this will
// break if there are other valid values that begin with a / character!
while (ch) {
if (ch === '/') {
comment();
} else if (ws.indexOf(ch) >= 0) {
next();
} else {
return;
}
}
},
word = function () {
// true, false, or null.
switch (ch) {
case 't':
next('t');
next('r');
next('u');
next('e');
return true;
case 'f':
next('f');
next('a');
next('l');
next('s');
next('e');
return false;
case 'n':
next('n');
next('u');
next('l');
next('l');
return null;
case 'I':
next('I');
next('n');
next('f');
next('i');
next('n');
next('i');
next('t');
next('y');
return Infinity;
case 'N':
next( 'N' );
next( 'a' );
next( 'N' );
return NaN;
}
error("Unexpected '" + ch + "'");
},
value, // Place holder for the value function.
array = function () {
// Parse an array value.
var array = [];
if (ch === '[') {
next('[');
white();
while (ch) {
if (ch === ']') {
next(']');
return array; // Potentially empty array
}
// ES5 allows omitting elements in arrays, e.g. [,] and
// [,null]. We don't allow this in JSON5.
if (ch === ',') {
error("Missing array element");
} else {
array.push(value());
}
white();
// If there's no comma after this value, this needs to
// be the end of the array.
if (ch !== ',') {
next(']');
return array;
}
next(',');
white();
}
}
error("Bad array");
},
object = function () {
// Parse an object value.
var key,
object = {};
if (ch === '{') {
next('{');
white();
while (ch) {
if (ch === '}') {
next('}');
return object; // Potentially empty object
}
// Keys can be unquoted. If they are, they need to be
// valid JS identifiers.
if (ch === '"' || ch === "'") {
key = string();
} else {
key = identifier();
}
white();
next(':');
object[key] = value();
white();
// If there's no comma after this pair, this needs to be
// the end of the object.
if (ch !== ',') {
next('}');
return object;
}
next(',');
white();
}
}
error("Bad object");
};
value = function () {
// Parse a JSON value. It could be an object, an array, a string, a number,
// or a word.
white();
switch (ch) {
case '{':
return object();
case '[':
return array();
case '"':
case "'":
return string();
case '-':
case '+':
case '.':
return number();
default:
return ch >= '0' && ch <= '9' ? number() : word();
}
};
// Return the json_parse function. It will have access to all of the above
// functions and variables.
return function (source, reviver) {
var result;
text = String(source);
at = 0;
ch = ' ';
result = value();
white();
if (ch) {
error("Syntax error");
}
// If there is a reviver function, we recursively walk the new structure,
// passing each name/value pair to the reviver function for possible
// transformation, starting with a temporary root object that holds the result
// in an empty key. If there is not a reviver function, we simply return the
// result.
return typeof reviver === 'function' ? (function walk(holder, key) {
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}({'': result}, '')) : result;
};
}());
// JSON5 stringify will not quote keys where appropriate
JSON5.stringify = function (obj, replacer, space) {
if (replacer && (typeof(replacer) !== "function" && !isArray(replacer))) {
throw new Error('Replacer must be a function or an array');
}
var getReplacedValueOrUndefined = function(holder, key, isTopLevel) {
var value = holder[key];
// Replace the value with its toJSON value first, if possible
if (value && value.toJSON && typeof value.toJSON === "function") {
value = value.toJSON();
}
// If the user-supplied replacer if a function, call it. If it's an array, check objects' string keys for
// presence in the array (removing the key/value pair from the resulting JSON if the key is missing).
if (typeof(replacer) === "function") {
return replacer.call(holder, key, value);
} else if(replacer) {
if (isTopLevel || isArray(holder) || replacer.indexOf(key) >= 0) {
return value;
} else {
return undefined;
}
} else {
return value;
}
};
function isWordChar(char) {
return (char >= 'a' && char <= 'z') ||
(char >= 'A' && char <= 'Z') ||
(char >= '0' && char <= '9') ||
char === '_' || char === '$';
}
function isWordStart(char) {
return (char >= 'a' && char <= 'z') ||
(char >= 'A' && char <= 'Z') ||
char === '_' || char === '$';
}
function isWord(key) {
if (typeof key !== 'string') {
return false;
}
if (!isWordStart(key[0])) {
return false;
}
var i = 1, length = key.length;
while (i < length) {
if (!isWordChar(key[i])) {
return false;
}
i++;
}
return true;
}
// export for use in tests
JSON5.isWord = isWord;
// polyfills
function isArray(obj) {
if (Array.isArray) {
return Array.isArray(obj);
} else {
return Object.prototype.toString.call(obj) === '[object Array]';
}
}
function isDate(obj) {
return Object.prototype.toString.call(obj) === '[object Date]';
}
isNaN = isNaN || function(val) {
return typeof val === 'number' && val !== val;
};
var objStack = [];
function checkForCircular(obj) {
for (var i = 0; i < objStack.length; i++) {
if (objStack[i] === obj) {
throw new TypeError("Converting circular structure to JSON");
}
}
}
function makeIndent(str, num, noNewLine) {
if (!str) {
return "";
}
// indentation no more than 10 chars
if (str.length > 10) {
str = str.substring(0, 10);
}
var indent = noNewLine ? "" : "\n";
for (var i = 0; i < num; i++) {
indent += str;
}
return indent;
}
var indentStr;
if (space) {
if (typeof space === "string") {
indentStr = space;
} else if (typeof space === "number" && space >= 0) {
indentStr = makeIndent(" ", space, true);
} else {
// ignore space parameter
}
}
// Copied from Crokford's implementation of JSON
// See https://github.com/douglascrockford/JSON-js/blob/e39db4b7e6249f04a195e7dd0840e610cc9e941e/json2.js#L195
// Begin
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
};
function escapeString(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ?
c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
// End
function internalStringify(holder, key, isTopLevel) {
var buffer, res;
// Replace the value, if necessary
var obj_part = getReplacedValueOrUndefined(holder, key, isTopLevel);
if (obj_part && !isDate(obj_part)) {
// unbox objects
// don't unbox dates, since will turn it into number
obj_part = obj_part.valueOf();
}
switch(typeof obj_part) {
case "boolean":
return obj_part.toString();
case "number":
if (isNaN(obj_part) || !isFinite(obj_part)) {
return "null";
}
return obj_part.toString();
case "string":
return escapeString(obj_part.toString());
case "object":
if (obj_part === null) {
return "null";
} else if (isArray(obj_part)) {
checkForCircular(obj_part);
buffer = "[";
objStack.push(obj_part);
for (var i = 0; i < obj_part.length; i++) {
res = internalStringify(obj_part, i, false);
buffer += makeIndent(indentStr, objStack.length);
if (res === null || typeof res === "undefined") {
buffer += "null";
} else {
buffer += res;
}
if (i < obj_part.length-1) {
buffer += ",";
} else if (indentStr) {
buffer += "\n";
}
}
objStack.pop();
buffer += makeIndent(indentStr, objStack.length, true) + "]";
} else {
checkForCircular(obj_part);
buffer = "{";
var nonEmpty = false;
objStack.push(obj_part);
for (var prop in obj_part) {
if (obj_part.hasOwnProperty(prop)) {
var value = internalStringify(obj_part, prop, false);
isTopLevel = false;
if (typeof value !== "undefined" && value !== null) {
buffer += makeIndent(indentStr, objStack.length);
nonEmpty = true;
var key = isWord(prop) ? prop : escapeString(prop);
buffer += key + ":" + (indentStr ? ' ' : '') + value + ",";
}
}
}
objStack.pop();
if (nonEmpty) {
buffer = buffer.substring(0, buffer.length-1) + makeIndent(indentStr, objStack.length) + "}";
} else {
buffer = '{}';
}
}
return buffer;
default:
// functions and undefined should be ignored
return undefined;
}
}
// special case...when undefined is used inside of
// a compound object/array, return null.
// but when top-level, return undefined
var topLevelHolder = {"":obj};
if (obj === undefined) {
return getReplacedValueOrUndefined(topLevelHolder, '', true);
}
return internalStringify(topLevelHolder, '', true);
};

63
api.hyungi.net/node_modules/pm2/lib/tools/open.js generated vendored Normal file
View File

@@ -0,0 +1,63 @@
var exec = require('child_process').exec
, path = require('path')
;
/**
* open a file or uri using the default application for the file type.
*
* @return {ChildProcess} - the child process object.
* @param {string} target - the file/uri to open.
* @param {string} appName - (optional) the application to be used to open the
* file (for example, "chrome", "firefox")
* @param {function(Error)} callback - called with null on success, or
* an error object that contains a property 'code' with the exit
* code of the process.
*/
module.exports = open;
function open(target, appName, callback) {
var opener;
if (typeof(appName) === 'function') {
callback = appName;
appName = null;
}
switch (process.platform) {
case 'darwin':
if (appName) {
opener = 'open -a "' + escape(appName) + '"';
} else {
opener = 'open';
}
break;
case 'win32':
// if the first parameter to start is quoted, it uses that as the title
// so we pass a blank title so we can quote the file we are opening
if (appName) {
opener = 'start "" "' + escape(appName) + '"';
} else {
opener = 'start ""';
}
break;
default:
if (appName) {
opener = escape(appName);
} else {
// use Portlands xdg-open everywhere else
opener = path.join(__dirname, './xdg-open');
}
break;
}
if (process.env.SUDO_USER) {
opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener;
}
return exec(opener + ' "' + escape(target) + '"', callback);
}
function escape(s) {
return s.replace(/"/g, '\\\"');
}

58
api.hyungi.net/node_modules/pm2/lib/tools/passwd.js generated vendored Normal file
View File

@@ -0,0 +1,58 @@
var fs = require('fs')
var getUsers = function() {
return fs.readFileSync('/etc/passwd')
.toString()
.split('\n')
.filter(function (user) {
return user.length && user[0] != '#';
})
.reduce(function(map, user) {
var fields = user.split(':');
map[fields[0]] = map[fields[2]] = {
username : fields[0],
password : fields[1],
userId : fields[2],
groupId : fields[3],
name : fields[4].split(',')[0],
homedir : fields[5],
shell : fields[6]
};
return map
}, {})
}
var getGroups = function(cb) {
var groups
try {
groups = fs.readFileSync('/etc/group')
} catch(e) {
return e
}
return groups
.toString()
.split('\n')
.filter(function (group) {
return group.length && group[0] != '#';
})
.reduce(function(map, group) {
var fields = group.split(':');
map[fields[0]] = map[fields[2]] = {
name : fields[0],
password : fields[1],
id : fields[2],
members : fields[3].split(',')
};
return map;
}, {})
}
module.exports = {
getUsers,
getGroups
}

View File

@@ -0,0 +1 @@
!function(e){function n(){}function t(e,n){return function(){e.apply(n,arguments)}}function o(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],s(e,this)}function i(e,n){for(;3===e._state;)e=e._value;return 0===e._state?void e._deferreds.push(n):(e._handled=!0,void o._immediateFn(function(){var t=1===e._state?n.onFulfilled:n.onRejected;if(null===t)return void(1===e._state?r:u)(n.promise,e._value);var o;try{o=t(e._value)}catch(e){return void u(n.promise,e)}r(n.promise,o)}))}function r(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var i=n.then;if(n instanceof o)return e._state=3,e._value=n,void f(e);if("function"==typeof i)return void s(t(i,n),e)}e._state=1,e._value=n,f(e)}catch(n){u(e,n)}}function u(e,n){e._state=2,e._value=n,f(e)}function f(e){2===e._state&&0===e._deferreds.length&&o._immediateFn(function(){e._handled||o._unhandledRejectionFn(e._value)});for(var n=0,t=e._deferreds.length;n<t;n++)i(e,e._deferreds[n]);e._deferreds=null}function c(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}function s(e,n){var t=!1;try{e(function(e){t||(t=!0,r(n,e))},function(e){t||(t=!0,u(n,e))})}catch(e){if(t)return;t=!0,u(n,e)}}var a=setTimeout;o.prototype.catch=function(e){return this.then(null,e)},o.prototype.then=function(e,t){var o=new this.constructor(n);return i(this,new c(e,t,o)),o},o.all=function(e){var n=Array.prototype.slice.call(e);return new o(function(e,t){function o(r,u){try{if(u&&("object"==typeof u||"function"==typeof u)){var f=u.then;if("function"==typeof f)return void f.call(u,function(e){o(r,e)},t)}n[r]=u,0===--i&&e(n)}catch(e){t(e)}}if(0===n.length)return e([]);for(var i=n.length,r=0;r<n.length;r++)o(r,n[r])})},o.resolve=function(e){return e&&"object"==typeof e&&e.constructor===o?e:new o(function(n){n(e)})},o.reject=function(e){return new o(function(n,t){t(e)})},o.race=function(e){return new o(function(n,t){for(var o=0,i=e.length;o<i;o++)e[o].then(n,t)})},o._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){a(e,0)},o._unhandledRejectionFn=function(e){"undefined"!=typeof console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)},o._setImmediateFn=function(e){o._immediateFn=e},o._setUnhandledRejectionFn=function(e){o._unhandledRejectionFn=e},"undefined"!=typeof module&&module.exports?module.exports=o:e.Promise||(e.Promise=o)}(this);

55
api.hyungi.net/node_modules/pm2/lib/tools/sexec.js generated vendored Normal file
View File

@@ -0,0 +1,55 @@
var path = require('path');
var fs = require('fs');
var child = require('child_process');
var DEFAULT_MAXBUFFER_SIZE = 20 * 1024 * 1024;
function _exec(command, options, callback) {
options = options || {};
if (typeof options === 'function') {
callback = options;
}
if (typeof options === 'object' && typeof callback === 'function') {
options.async = true;
}
if (!command) {
try {
console.error('[sexec] must specify command');
} catch (e) {
return;
}
}
options = Object.assign({
silent: false,
cwd: path.resolve(process.cwd()).toString(),
env: process.env,
maxBuffer: DEFAULT_MAXBUFFER_SIZE,
encoding: 'utf8',
}, options);
var c = child.exec(command, options, function (err, stdout, stderr) {
if (callback) {
if (!err) {
callback(0, stdout, stderr);
} else if (err.code === undefined) {
// See issue #536
/* istanbul ignore next */
callback(1, stdout, stderr);
} else {
callback(err.code, stdout, stderr);
}
}
});
if (!options.silent) {
c.stdout.pipe(process.stdout);
c.stderr.pipe(process.stderr);
}
}
module.exports = _exec;

113
api.hyungi.net/node_modules/pm2/lib/tools/treeify.js generated vendored Normal file
View File

@@ -0,0 +1,113 @@
// treeify.js
// Luke Plaster <notatestuser@gmail.com>
// https://github.com/notatestuser/treeify.js
// do the universal module definition dance
(function (root, factory) {
if (typeof exports === 'object') {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
} else {
root.treeify = factory();
}
}(this, function() {
function makePrefix(key, last) {
var str = (last ? '└' : '├');
if (key) {
str += '─ ';
} else {
str += '──┐';
}
return str;
}
function filterKeys(obj, hideFunctions) {
var keys = [];
for (var branch in obj) {
// always exclude anything in the object's prototype
if (!obj.hasOwnProperty(branch)) {
continue;
}
// ... and hide any keys mapped to functions if we've been told to
if (hideFunctions && ((typeof obj[branch])==="function")) {
continue;
}
keys.push(branch);
}
return keys;
}
function growBranch(key, root, last, lastStates, showValues, hideFunctions, callback) {
var line = '', index = 0, lastKey, circular, lastStatesCopy = lastStates.slice(0);
if (lastStatesCopy.push([ root, last ]) && lastStates.length > 0) {
// based on the "was last element" states of whatever we're nested within,
// we need to append either blankness or a branch to our line
lastStates.forEach(function(lastState, idx) {
if (idx > 0) {
line += (lastState[1] ? ' ' : '│') + ' ';
}
if ( ! circular && lastState[0] === root) {
circular = true;
}
});
// the prefix varies based on whether the key contains something to show and
// whether we're dealing with the last element in this collection
line += makePrefix(key, last) + key;
// append values and the circular reference indicator
showValues && (typeof root !== 'object' || root instanceof Date) && (line += ': ' + root);
circular && (line += ' (circular ref.)');
callback(line);
}
// can we descend into the next item?
if ( ! circular && typeof root === 'object') {
var keys = filterKeys(root, hideFunctions);
keys.forEach(function(branch){
// the last key is always printed with a different prefix, so we'll need to know if we have it
lastKey = ++index === keys.length;
// hold your breath for recursive action
growBranch(branch, root[branch], lastKey, lastStatesCopy, showValues, hideFunctions, callback);
});
}
};
// --------------------
var Treeify = {};
// Treeify.asLines
// --------------------
// Outputs the tree line-by-line, calling the lineCallback when each one is available.
Treeify.asLines = function(obj, showValues, hideFunctions, lineCallback) {
/* hideFunctions and lineCallback are curried, which means we don't break apps using the older form */
var hideFunctionsArg = typeof hideFunctions !== 'function' ? hideFunctions : false;
growBranch('.', obj, false, [], showValues, hideFunctionsArg, lineCallback || hideFunctions);
};
// Treeify.asTree
// --------------------
// Outputs the entire tree, returning it as a string with line breaks.
Treeify.asTree = function(obj, showValues, hideFunctions) {
var tree = '';
growBranch('.', obj, false, [], showValues, hideFunctions, function(line) {
tree += line + '\n';
});
return tree;
};
// --------------------
return Treeify;
}));

120
api.hyungi.net/node_modules/pm2/lib/tools/which.js generated vendored Normal file
View File

@@ -0,0 +1,120 @@
var fs = require('fs');
var path = require('path');
var cst = require('../../constants.js')
// XP's system default value for `PATHEXT` system variable, just in case it's not
// set on Windows.
var XP_DEFAULT_PATHEXT = '.com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh';
// For earlier versions of NodeJS that doesn't have a list of constants (< v6)
var FILE_EXECUTABLE_MODE = 1;
function statFollowLinks() {
return fs.statSync.apply(fs, arguments);
}
function isWindowsPlatform() {
return cst.IS_WINDOWS;
}
// Cross-platform method for splitting environment `PATH` variables
function splitPath(p) {
return p ? p.split(path.delimiter) : [];
}
// Tests are running all cases for this func but it stays uncovered by codecov due to unknown reason
/* istanbul ignore next */
function isExecutable(pathName) {
try {
// TODO(node-support): replace with fs.constants.X_OK once remove support for node < v6
fs.accessSync(pathName, FILE_EXECUTABLE_MODE);
} catch (err) {
return false;
}
return true;
}
function checkPath(pathName) {
return fs.existsSync(pathName) && !statFollowLinks(pathName).isDirectory()
&& (isWindowsPlatform() || isExecutable(pathName));
}
//@
//@ ### which(command)
//@
//@ Examples:
//@
//@ ```javascript
//@ var nodeExec = which('node');
//@ ```
//@
//@ Searches for `command` in the system's `PATH`. On Windows, this uses the
//@ `PATHEXT` variable to append the extension if it's not already executable.
//@ Returns a [ShellString](#shellstringstr) containing the absolute path to
//@ `command`.
function _which(cmd) {
if (!cmd) console.error('must specify command');
var options = {}
var isWindows = isWindowsPlatform();
var pathArray = splitPath(process.env.PATH);
var queryMatches = [];
// No relative/absolute paths provided?
if (cmd.indexOf('/') === -1) {
// Assume that there are no extensions to append to queries (this is the
// case for unix)
var pathExtArray = [''];
if (isWindows) {
// In case the PATHEXT variable is somehow not set (e.g.
// child_process.spawn with an empty environment), use the XP default.
var pathExtEnv = process.env.PATHEXT || XP_DEFAULT_PATHEXT;
pathExtArray = splitPath(pathExtEnv.toUpperCase());
}
// Search for command in PATH
for (var k = 0; k < pathArray.length; k++) {
// already found it
if (queryMatches.length > 0 && !options.all) break;
var attempt = path.resolve(pathArray[k], cmd);
if (isWindows) {
attempt = attempt.toUpperCase();
}
var match = attempt.match(/\.[^<>:"/|?*.]+$/);
if (match && pathExtArray.indexOf(match[0]) >= 0) { // this is Windows-only
// The user typed a query with the file extension, like
// `which('node.exe')`
if (checkPath(attempt)) {
queryMatches.push(attempt);
break;
}
} else { // All-platforms
// Cycle through the PATHEXT array, and check each extension
// Note: the array is always [''] on Unix
for (var i = 0; i < pathExtArray.length; i++) {
var ext = pathExtArray[i];
var newAttempt = attempt + ext;
if (checkPath(newAttempt)) {
queryMatches.push(newAttempt);
break;
}
}
}
}
} else if (checkPath(cmd)) { // a valid absolute or relative path
queryMatches.push(path.resolve(cmd));
}
if (queryMatches.length > 0) {
return options.all ? queryMatches : queryMatches[0];
}
return options.all ? [] : null;
}
module.exports = _which;

861
api.hyungi.net/node_modules/pm2/lib/tools/xdg-open generated vendored Normal file
View File

@@ -0,0 +1,861 @@
#!/bin/sh
#---------------------------------------------
# xdg-open
#
# Utility script to open a URL in the registered default application.
#
# Refer to the usage() function below for usage.
#
# Copyright 2009-2010, Fathi Boudra <fabo@freedesktop.org>
# Copyright 2009-2010, Rex Dieter <rdieter@fedoraproject.org>
# Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
# Copyright 2006, Jeremy White <jwhite@codeweavers.com>
#
# LICENSE:
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
#---------------------------------------------
manualpage()
{
cat << _MANUALPAGE
Name
xdg-open - opens a file or URL in the user's preferred
application
Synopsis
xdg-open { file | URL }
xdg-open { --help | --manual | --version }
Description
xdg-open opens a file or URL in the user's preferred
application. If a URL is provided the URL will be opened in the
user's preferred web browser. If a file is provided the file
will be opened in the preferred application for files of that
type. xdg-open supports file, ftp, http and https URLs.
xdg-open is for use inside a desktop session only. It is not
recommended to use xdg-open as root.
Options
--help
Show command synopsis.
--manual
Show this manual page.
--version
Show the xdg-utils version information.
Exit Codes
An exit code of 0 indicates success while a non-zero exit code
indicates failure. The following failure codes can be returned:
1
Error in command line syntax.
2
One of the files passed on the command line did not
exist.
3
A required tool could not be found.
4
The action failed.
Examples
xdg-open 'http://www.freedesktop.org/'
Opens the freedesktop.org website in the user's default
browser.
xdg-open /tmp/foobar.png
Opens the PNG image file /tmp/foobar.png in the user's default
image viewing application.
_MANUALPAGE
}
usage()
{
cat << _USAGE
xdg-open - opens a file or URL in the user's preferred
application
Synopsis
xdg-open { file | URL }
xdg-open { --help | --manual | --version }
_USAGE
}
#@xdg-utils-common@
#----------------------------------------------------------------------------
# Common utility functions included in all XDG wrapper scripts
#----------------------------------------------------------------------------
DEBUG()
{
[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
[ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;
shift
echo "$@" >&2
}
# This handles backslashes but not quote marks.
first_word()
{
read first rest
echo "$first"
}
#-------------------------------------------------------------
# map a binary to a .desktop file
binary_to_desktop_file()
{
search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
binary="`which "$1"`"
binary="`readlink -f "$binary"`"
base="`basename "$binary"`"
IFS=:
for dir in $search; do
unset IFS
[ "$dir" ] || continue
[ -d "$dir/applications" ] || [ -d "$dir/applnk" ] || continue
for file in "$dir"/applications/*.desktop "$dir"/applications/*/*.desktop "$dir"/applnk/*.desktop "$dir"/applnk/*/*.desktop; do
[ -r "$file" ] || continue
# Check to make sure it's worth the processing.
grep -q "^Exec.*$base" "$file" || continue
# Make sure it's a visible desktop file (e.g. not "preferred-web-browser.desktop").
grep -Eq "^(NoDisplay|Hidden)=true" "$file" && continue
command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
command="`which "$command"`"
if [ x"`readlink -f "$command"`" = x"$binary" ]; then
# Fix any double slashes that got added path composition
echo "$file" | sed -e 's,//*,/,g'
return
fi
done
done
}
#-------------------------------------------------------------
# map a .desktop file to a binary
## FIXME: handle vendor dir case
desktop_file_to_binary()
{
search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
desktop="`basename "$1"`"
IFS=:
for dir in $search; do
unset IFS
[ "$dir" ] && [ -d "$dir/applications" ] || continue
file="$dir/applications/$desktop"
[ -r "$file" ] || continue
# Remove any arguments (%F, %f, %U, %u, etc.).
command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
command="`which "$command"`"
readlink -f "$command"
return
done
}
#-------------------------------------------------------------
# Exit script on successfully completing the desired operation
exit_success()
{
if [ $# -gt 0 ]; then
echo "$@"
echo
fi
exit 0
}
#-----------------------------------------
# Exit script on malformed arguments, not enough arguments
# or missing required option.
# prints usage information
exit_failure_syntax()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
echo "Try 'xdg-open --help' for more information." >&2
else
usage
echo "Use 'man xdg-open' or 'xdg-open --manual' for additional info."
fi
exit 1
}
#-------------------------------------------------------------
# Exit script on missing file specified on command line
exit_failure_file_missing()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 2
}
#-------------------------------------------------------------
# Exit script on failure to locate necessary tool applications
exit_failure_operation_impossible()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 3
}
#-------------------------------------------------------------
# Exit script on failure returned by a tool application
exit_failure_operation_failed()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 4
}
#------------------------------------------------------------
# Exit script on insufficient permission to read a specified file
exit_failure_file_permission_read()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 5
}
#------------------------------------------------------------
# Exit script on insufficient permission to write a specified file
exit_failure_file_permission_write()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 6
}
check_input_file()
{
if [ ! -e "$1" ]; then
exit_failure_file_missing "file '$1' does not exist"
fi
if [ ! -r "$1" ]; then
exit_failure_file_permission_read "no permission to read file '$1'"
fi
}
check_vendor_prefix()
{
file_label="$2"
[ -n "$file_label" ] || file_label="filename"
file=`basename "$1"`
case "$file" in
[[:alpha:]]*-*)
return
;;
esac
echo "xdg-open: $file_label '$file' does not have a proper vendor prefix" >&2
echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
echo "Use --novendor to override or 'xdg-open --manual' for additional info." >&2
exit 1
}
check_output_file()
{
# if the file exists, check if it is writeable
# if it does not exist, check if we are allowed to write on the directory
if [ -e "$1" ]; then
if [ ! -w "$1" ]; then
exit_failure_file_permission_write "no permission to write to file '$1'"
fi
else
DIR=`dirname "$1"`
if [ ! -w "$DIR" ] || [ ! -x "$DIR" ]; then
exit_failure_file_permission_write "no permission to create file '$1'"
fi
fi
}
#----------------------------------------
# Checks for shared commands, e.g. --help
check_common_commands()
{
while [ $# -gt 0 ] ; do
parm="$1"
shift
case "$parm" in
--help)
usage
echo "Use 'man xdg-open' or 'xdg-open --manual' for additional info."
exit_success
;;
--manual)
manualpage
exit_success
;;
--version)
echo "xdg-open 1.1.0 rc3"
exit_success
;;
esac
done
}
check_common_commands "$@"
[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
# Be silent
xdg_redirect_output=" > /dev/null 2> /dev/null"
else
# All output to stderr
xdg_redirect_output=" >&2"
fi
#--------------------------------------
# Checks for known desktop environments
# set variable DE to the desktop environments name, lowercase
detectDE()
{
# see https://bugs.freedesktop.org/show_bug.cgi?id=34164
unset GREP_OPTIONS
if [ -n "${XDG_CURRENT_DESKTOP}" ]; then
case "${XDG_CURRENT_DESKTOP}" in
ENLIGHTENMENT)
DE=enlightenment;
;;
GNOME)
DE=gnome;
;;
KDE)
DE=kde;
;;
LXDE)
DE=lxde;
;;
MATE)
DE=mate;
;;
XFCE)
DE=xfce
;;
esac
fi
if [ x"$DE" = x"" ]; then
# classic fallbacks
if [ x"$KDE_FULL_SESSION" != x"" ]; then DE=kde;
elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
elif [ x"$MATE_DESKTOP_SESSION_ID" != x"" ]; then DE=mate;
elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;
elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce
elif echo $DESKTOP | grep -q '^Enlightenment'; then DE=enlightenment;
fi
fi
if [ x"$DE" = x"" ]; then
# fallback to checking $DESKTOP_SESSION
case "$DESKTOP_SESSION" in
gnome)
DE=gnome;
;;
LXDE|Lubuntu)
DE=lxde;
;;
MATE)
DE=mate;
;;
xfce|xfce4|'Xfce Session')
DE=xfce;
;;
esac
fi
if [ x"$DE" = x"" ]; then
# fallback to uname output for other platforms
case "$(uname 2>/dev/null)" in
Darwin)
DE=darwin;
;;
esac
fi
if [ x"$DE" = x"gnome" ]; then
# gnome-default-applications-properties is only available in GNOME 2.x
# but not in GNOME 3.x
which gnome-default-applications-properties > /dev/null 2>&1 || DE="gnome3"
fi
}
#----------------------------------------------------------------------------
# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
# It also always returns 1 in KDE 3.4 and earlier
# Simply return 0 in such case
kfmclient_fix_exit_code()
{
version=`LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE'`
major=`echo $version | sed 's/KDE.*: \([0-9]\).*/\1/'`
minor=`echo $version | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/'`
release=`echo $version | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
test "$major" -gt 3 && return $1
test "$minor" -gt 5 && return $1
test "$release" -gt 4 && return $1
return 0
}
# This handles backslashes but not quote marks.
last_word()
{
read first rest
echo "$rest"
}
# Get the value of a key in a desktop file's Desktop Entry group.
# Example: Use get_key foo.desktop Exec
# to get the values of the Exec= key for the Desktop Entry group.
get_key()
{
local file="${1}"
local key="${2}"
local desktop_entry=""
IFS_="${IFS}"
IFS=""
while read line
do
case "$line" in
"[Desktop Entry]")
desktop_entry="y"
;;
# Reset match flag for other groups
"["*)
desktop_entry=""
;;
"${key}="*)
# Only match Desktop Entry group
if [ -n "${desktop_entry}" ]
then
echo "${line}" | cut -d= -f 2-
fi
esac
done < "${file}"
IFS="${IFS_}"
}
open_darwin()
{
open "$1"
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_kde()
{
if [ -n "${KDE_SESSION_VERSION}" ]; then
case "${KDE_SESSION_VERSION}" in
4)
kde-open "$1"
;;
5)
kde-open${KDE_SESSION_VERSION} "$1"
;;
esac
else
kfmclient exec "$1"
kfmclient_fix_exit_code $?
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_gnome()
{
if gvfs-open --help 2>/dev/null 1>&2; then
gvfs-open "$1"
else
gnome-open "$1"
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_mate()
{
if gvfs-open --help 2>/dev/null 1>&2; then
gvfs-open "$1"
else
mate-open "$1"
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_xfce()
{
exo-open "$1"
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_enlightenment()
{
enlightenment_open "$1"
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
#-----------------------------------------
# Recursively search .desktop file
search_desktop_file()
{
local default="$1"
local dir="$2"
local target="$3"
local file=""
# look for both vendor-app.desktop, vendor/app.desktop
if [ -r "$dir/$default" ]; then
file="$dir/$default"
elif [ -r "$dir/`echo $default | sed -e 's|-|/|'`" ]; then
file="$dir/`echo $default | sed -e 's|-|/|'`"
fi
if [ -r "$file" ] ; then
command="$(get_key "${file}" "Exec" | first_word)"
command_exec=`which $command 2>/dev/null`
icon="$(get_key "${file}" "Icon")"
# FIXME: Actually LC_MESSAGES should be used as described in
# http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s04.html
localised_name="$(get_key "${file}" "Name")"
set -- $(get_key "${file}" "Exec" | last_word)
# We need to replace any occurrence of "%f", "%F" and
# the like by the target file. We examine each
# argument and append the modified argument to the
# end then shift.
local args=$#
local replaced=0
while [ $args -gt 0 ]; do
case $1 in
%[c])
replaced=1
arg="${localised_name}"
shift
set -- "$@" "$arg"
;;
%[fFuU])
replaced=1
arg="$target"
shift
set -- "$@" "$arg"
;;
%[i])
replaced=1
shift
set -- "$@" "--icon" "$icon"
;;
*)
arg="$1"
shift
set -- "$@" "$arg"
;;
esac
args=$(( $args - 1 ))
done
[ $replaced -eq 1 ] || set -- "$@" "$target"
"$command_exec" "$@"
if [ $? -eq 0 ]; then
exit_success
fi
fi
for d in $dir/*/; do
[ -d "$d" ] && search_desktop_file "$default" "$d" "$target"
done
}
open_generic_xdg_mime()
{
filetype="$2"
default=`xdg-mime query default "$filetype"`
if [ -n "$default" ] ; then
xdg_user_dir="$XDG_DATA_HOME"
[ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
xdg_system_dirs="$XDG_DATA_DIRS"
[ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
DEBUG 3 "$xdg_user_dir:$xdg_system_dirs"
for x in `echo "$xdg_user_dir:$xdg_system_dirs" | sed 's/:/ /g'`; do
search_desktop_file "$default" "$x/applications/" "$1"
done
fi
}
open_generic_xdg_file_mime()
{
filetype=`xdg-mime query filetype "$1" | sed "s/;.*//"`
open_generic_xdg_mime "$1" "$filetype"
}
open_generic_xdg_x_scheme_handler()
{
scheme="`echo $1 | sed -n 's/\(^[[:alnum:]+\.-]*\):.*$/\1/p'`"
if [ -n $scheme ]; then
filetype="x-scheme-handler/$scheme"
open_generic_xdg_mime "$1" "$filetype"
fi
}
open_generic()
{
# Paths or file:// URLs
if (echo "$1" | grep -q '^file://' ||
! echo "$1" | egrep -q '^[[:alpha:]+\.\-]+:'); then
local file="$1"
# Decode URLs
if echo "$file" | grep -q '^file:///'; then
file=${file#file://}
file="$(printf "$(echo "$file" | sed -e 's@%\([a-f0-9A-F]\{2\}\)@\\x\1@g')")"
fi
file_check=${file%%#*}
file_check=${file_check%%\?*}
check_input_file "$file_check"
filetype=`xdg-mime query filetype "$file_check" | sed "s/;.*//"`
open_generic_xdg_mime "$file" "$filetype"
if which run-mailcap 2>/dev/null 1>&2; then
run-mailcap --action=view "$file"
if [ $? -eq 0 ]; then
exit_success
fi
fi
if mimeopen -v 2>/dev/null 1>&2; then
mimeopen -L -n "$file"
if [ $? -eq 0 ]; then
exit_success
fi
fi
fi
open_generic_xdg_x_scheme_handler "$1"
IFS=":"
for browser in $BROWSER; do
if [ x"$browser" != x"" ]; then
browser_with_arg=`printf "$browser" "$1" 2>/dev/null`
if [ $? -ne 0 ]; then
browser_with_arg=$browser;
fi
if [ x"$browser_with_arg" = x"$browser" ]; then
eval '$browser "$1"'$xdg_redirect_output;
else eval '$browser_with_arg'$xdg_redirect_output;
fi
if [ $? -eq 0 ]; then
exit_success;
fi
fi
done
exit_failure_operation_impossible "no method available for opening '$1'"
}
open_lxde()
{
# pcmanfm only knows how to handle file:// urls and filepaths, it seems.
if (echo "$1" | grep -q '^file://' ||
! echo "$1" | egrep -q '^[[:alpha:]+\.\-]+:')
then
local file="$1"
# handle relative paths
if ! echo "$file" | egrep -q '^(file://)?/'; then
file="$(pwd)/$file"
fi
pcmanfm "$file"
else
open_generic "$1"
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
[ x"$1" != x"" ] || exit_failure_syntax
url=
while [ $# -gt 0 ] ; do
parm="$1"
shift
case "$parm" in
-*)
exit_failure_syntax "unexpected option '$parm'"
;;
*)
if [ -n "$url" ] ; then
exit_failure_syntax "unexpected argument '$parm'"
fi
url="$parm"
;;
esac
done
if [ -z "${url}" ] ; then
exit_failure_syntax "file or URL argument missing"
fi
detectDE
if [ x"$DE" = x"" ]; then
DE=generic
fi
DEBUG 2 "Selected DE $DE"
# sanitize BROWSER (avoid caling ourselves in particular)
case "${BROWSER}" in
*:"xdg-open"|"xdg-open":*)
BROWSER=$(echo $BROWSER | sed -e 's|:xdg-open||g' -e 's|xdg-open:||g')
;;
"xdg-open")
BROWSER=
;;
esac
# if BROWSER variable is not set, check some well known browsers instead
if [ x"$BROWSER" = x"" ]; then
BROWSER=links2:elinks:links:lynx:w3m
if [ -n "$DISPLAY" ]; then
BROWSER=x-www-browser:firefox:seamonkey:mozilla:epiphany:konqueror:chromium-browser:google-chrome:$BROWSER
fi
fi
case "$DE" in
kde)
open_kde "$url"
;;
gnome*)
open_gnome "$url"
;;
mate)
open_mate "$url"
;;
xfce)
open_xfce "$url"
;;
lxde)
open_lxde "$url"
;;
enlightenment)
open_enlightenment "$url"
;;
generic)
open_generic "$url"
;;
*)
exit_failure_operation_impossible "no method available for opening '$url'"
;;
esac