HEX
Server: Apache/2.4.41 (Ubuntu)
System: Linux ip-172-31-42-149 5.15.0-1084-aws #91~20.04.1-Ubuntu SMP Fri May 2 07:00:04 UTC 2025 aarch64
User: ubuntu (1000)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //proc/self/root/usr/local/lib/node_modules/@ionic/cli/node_modules/ssh-config/index.js
'use strict'

const glob = require('./lib/glob')

const RE_SPACE = /\s/
const RE_LINE_BREAK = /\r|\n/
const RE_SECTION_DIRECTIVE = /^(Host|Match)$/i
const RE_QUOTED = /^(")(.*)\1$/

const DIRECTIVE = 1
const COMMENT = 2


class SSHConfig extends Array {
  /**
   * Query ssh config by host.
   *
   * @return {Object} The applied options of current Host
   */
  compute(host) {
    let obj = {}
    let setProperty = (name, value) => {
      if (name === 'IdentityFile') {
        let list = obj[name] || (obj[name] = [])
        list.push(value)
      }
      else if (obj[name] === undefined) {
        obj[name] = value
      }
    }

    for (let i = 0; i < this.length; i++) {
      let line = this[i]

      if (line.type !== DIRECTIVE) {
        continue
      }
      else if (line.param === 'Host') {
        if (glob(line.value, host)) {
          setProperty(line.param, line.value)

          line.config
            .filter(line => line.type === DIRECTIVE)
            .forEach(line => setProperty(line.param, line.value))
        }
      }
      else if (line.param === 'Match') {
        // TODO
      }
      else {
        setProperty(line.param, line.value)
      }
    }

    return obj
  }


  /**
   * find section by Host or Match
   */
  find(opts) {
    let result = this.constructor.find(this, opts)
    return result ? result[0] : null
  }


  /**
   * Remove section
   */
  remove(opts) {
    let result = this.constructor.find(this, opts)

    if (result) {
      return this.splice(result[1], 1)
    }
  }


  /**
   * toString()
   */
  toString() {
    return this.constructor.stringify(this)
  }


  /**
   * Append new section to existing ssh config.
   */
  append(opts) {
    let indent = '  '

    outer:
    for (const line of this) {
      if (RE_SECTION_DIRECTIVE.test(line.param)) {
        for (const subline of line.config) {
          if (subline.before) {
            indent = subline.before
            break outer
          }
        }
      }
    }

    if (this.length > 0) {
      let lastLine = this[this.length - 1]
      if (lastLine.config) lastLine = lastLine.config[lastLine.config.length - 1]
      if (!lastLine.after.trim()) {
        lastLine.after = '\n'
      }
    }

    let config = this
    let configWas = this

    for (const param in opts) {
      const line = {
        type: DIRECTIVE,
        param,
        separator: ' ',
        value: opts[param],
        before: '',
        after: '\n'
      }

      if (RE_SECTION_DIRECTIVE.test(param)) {
        config = configWas
        config.push(line)
        config = line.config = new SSHConfig()
      } else {
        line.before = config === configWas ? '' : indent
        config.push(line)
      }
    }

    return configWas
  }


  static find(config, opts) {
    if (!(opts && ('Host' in opts || 'Match' in opts))) {
      throw new Error('Can only find by Host or Match')
    }

    for (let i = 0; i < config.length; i++) {
      const line = config[i]

      if (line.type === DIRECTIVE &&
          RE_SECTION_DIRECTIVE.test(line.param) &&
          line.param in opts &&
          opts[line.param] === line.value) {
        return [line, i]
      }
    }

    return null
  }


  /**
   * Stringify structured object into ssh config text
   */
  static stringify(config) {
    let str = ''

    let format = line => {
      str += line.before

      if (line.type === COMMENT) {
        str += line.content
      }
      else if (line.type === DIRECTIVE) {
        str += line.quoted || (line.param == 'IdentityFile' && RE_SPACE.test(line.value))
          ? `${line.param}${line.separator}"${line.value}"`
          : `${line.param}${line.separator}${line.value}`
      }

      str += line.after

      if (line.config) {
        line.config.forEach(format)
      }
    }

    config.forEach(format)

    return str
  }


  static get DIRECTIVE() {
    return DIRECTIVE
  }


  static get COMMENT() {
    return COMMENT
  }


  /**
   * Parse ssh config text into structured object.
   */
  static parse(str) {
    let i = 0
    let chr = next()
    let config = new SSHConfig()
    let configWas = config

    function next() {
      return str[i++]
    }

    function space() {
      let spaces = ''

      while (RE_SPACE.test(chr)) {
        spaces += chr
        chr = next()
      }

      return spaces
    }

    function linebreak() {
      let breaks = ''

      while (RE_LINE_BREAK.test(chr)) {
        breaks += chr
        chr = next()
      }

      return breaks
    }

    function option() {
      let opt = ''

      while (chr && chr !== ' ' && chr !== '=') {
        opt += chr
        chr = next()
      }

      return opt
    }

    function separator() {
      let sep = space()

      if (chr === '=') {
        sep += chr
        chr = next()
      }

      return sep + space()
    }

    function value() {
      let val = ''

      while (chr && !RE_LINE_BREAK.test(chr)) {
        val += chr
        chr = next()
      }

      return val.trim()
    }

    function comment() {
      let type = COMMENT
      let content = ''

      while (chr && !RE_LINE_BREAK.test(chr)) {
        content += chr
        chr = next()
      }

      return { type, content }
    }

    function directive() {
      let type = DIRECTIVE

      return {
        type,
        param: option(),
        separator: separator(),
        value: value()
      }
    }

    function line() {
      let before = space()
      let node = chr === '#' ? comment() : directive()
      let after = linebreak()

      node.before = before
      node.after = after

      if (RE_QUOTED.test(node.value)) {
        node.value = node.value.replace(RE_QUOTED, '$2')
        node.quoted = true
      }

      return node
    }


    while (chr) {
      let node = line()

      if (node.type === DIRECTIVE && RE_SECTION_DIRECTIVE.test(node.param)) {
        config = configWas
        config.push(node)
        config = node.config = new SSHConfig()
      }
      else {
        config.push(node)
      }
    }

    return configWas
  }
}


module.exports = SSHConfig