UNPKG

curlconverter

Version:

convert curl commands to Python, JavaScript, Go, PHP and more

1,347 lines (1,280 loc) 52.2 kB
import { CCError, has } from "../utils.js"; import { Word, eq, firstShellToken } from "../shell/Word.js"; import { warnf, underlineNode, type Warnings } from "../Warnings.js"; import { CURLAUTH_BASIC, CURLAUTH_DIGEST, CURLAUTH_NEGOTIATE, CURLAUTH_NTLM, CURLAUTH_NTLM_WB, CURLAUTH_BEARER, CURLAUTH_AWS_SIGV4, CURLAUTH_ANY, } from "./auth.js"; import type { DataType } from "../Request.js"; export type FormType = "string" | "form"; export interface LongShort { type: "string" | "bool"; name: string; removed?: string; expand?: boolean; } export interface LongOpts { [key: string]: LongShort | null; } export interface ShortOpts { [key: string]: string; } // prettier-ignore export const curlLongOpts = { // BEGIN EXTRACTED OPTIONS "url": { type: "string", name: "url" }, "dns-ipv4-addr": { type: "string", name: "dns-ipv4-addr" }, "dns-ipv6-addr": { type: "string", name: "dns-ipv6-addr" }, "random-file": { type: "string", name: "random-file" }, "egd-file": { type: "string", name: "egd-file" }, "oauth2-bearer": { type: "string", name: "oauth2-bearer" }, "connect-timeout": { type: "string", name: "connect-timeout" }, "doh-url": { type: "string", name: "doh-url" }, "ciphers": { type: "string", name: "ciphers" }, "dns-interface": { type: "string", name: "dns-interface" }, "disable-epsv": { type: "bool", name: "disable-epsv" }, "no-disable-epsv": { type: "bool", name: "disable-epsv", expand: false }, "disallow-username-in-url": { type: "bool", name: "disallow-username-in-url" }, "no-disallow-username-in-url": { type: "bool", name: "disallow-username-in-url", expand: false }, "epsv": { type: "bool", name: "epsv" }, "no-epsv": { type: "bool", name: "epsv", expand: false }, "dns-servers": { type: "string", name: "dns-servers" }, "trace": { type: "string", name: "trace" }, "npn": { type: "bool", name: "npn" }, "no-npn": { type: "bool", name: "npn", expand: false }, "trace-ascii": { type: "string", name: "trace-ascii" }, "alpn": { type: "bool", name: "alpn" }, "no-alpn": { type: "bool", name: "alpn", expand: false }, "limit-rate": { type: "string", name: "limit-rate" }, "rate": { type: "string", name: "rate" }, "compressed": { type: "bool", name: "compressed" }, "no-compressed": { type: "bool", name: "compressed", expand: false }, "tr-encoding": { type: "bool", name: "tr-encoding" }, "no-tr-encoding": { type: "bool", name: "tr-encoding", expand: false }, "digest": { type: "bool", name: "digest" }, "no-digest": { type: "bool", name: "digest", expand: false }, "negotiate": { type: "bool", name: "negotiate" }, "no-negotiate": { type: "bool", name: "negotiate", expand: false }, "ntlm": { type: "bool", name: "ntlm" }, "no-ntlm": { type: "bool", name: "ntlm", expand: false }, "ntlm-wb": { type: "bool", name: "ntlm-wb" }, "no-ntlm-wb": { type: "bool", name: "ntlm-wb", expand: false }, "basic": { type: "bool", name: "basic" }, "no-basic": { type: "bool", name: "basic", expand: false }, "anyauth": { type: "bool", name: "anyauth" }, "no-anyauth": { type: "bool", name: "anyauth", expand: false }, "wdebug": { type: "bool", name: "wdebug" }, "no-wdebug": { type: "bool", name: "wdebug", expand: false }, "ftp-create-dirs": { type: "bool", name: "ftp-create-dirs" }, "no-ftp-create-dirs": { type: "bool", name: "ftp-create-dirs", expand: false }, "create-dirs": { type: "bool", name: "create-dirs" }, "no-create-dirs": { type: "bool", name: "create-dirs", expand: false }, "create-file-mode": { type: "string", name: "create-file-mode" }, "max-redirs": { type: "string", name: "max-redirs" }, "proxy-ntlm": { type: "bool", name: "proxy-ntlm" }, "no-proxy-ntlm": { type: "bool", name: "proxy-ntlm", expand: false }, "crlf": { type: "bool", name: "crlf" }, "no-crlf": { type: "bool", name: "crlf", expand: false }, "stderr": { type: "string", name: "stderr" }, "aws-sigv4": { type: "string", name: "aws-sigv4" }, "interface": { type: "string", name: "interface" }, "krb": { type: "string", name: "krb" }, "krb4": { type: "string", name: "krb" }, "haproxy-protocol": { type: "bool", name: "haproxy-protocol" }, "no-haproxy-protocol": { type: "bool", name: "haproxy-protocol", expand: false }, "haproxy-clientip": { type: "string", name: "haproxy-clientip" }, "max-filesize": { type: "string", name: "max-filesize" }, "disable-eprt": { type: "bool", name: "disable-eprt" }, "no-disable-eprt": { type: "bool", name: "disable-eprt", expand: false }, "eprt": { type: "bool", name: "eprt" }, "no-eprt": { type: "bool", name: "eprt", expand: false }, "xattr": { type: "bool", name: "xattr" }, "no-xattr": { type: "bool", name: "xattr", expand: false }, "ftp-ssl": { type: "bool", name: "ssl" }, "no-ftp-ssl": { type: "bool", name: "ssl", expand: false }, "ssl": { type: "bool", name: "ssl" }, "no-ssl": { type: "bool", name: "ssl", expand: false }, "ftp-pasv": { type: "bool", name: "ftp-pasv" }, "no-ftp-pasv": { type: "bool", name: "ftp-pasv", expand: false }, "socks5": { type: "string", name: "socks5" }, "tcp-nodelay": { type: "bool", name: "tcp-nodelay" }, "no-tcp-nodelay": { type: "bool", name: "tcp-nodelay", expand: false }, "proxy-digest": { type: "bool", name: "proxy-digest" }, "no-proxy-digest": { type: "bool", name: "proxy-digest", expand: false }, "proxy-basic": { type: "bool", name: "proxy-basic" }, "no-proxy-basic": { type: "bool", name: "proxy-basic", expand: false }, "retry": { type: "string", name: "retry" }, "retry-connrefused": { type: "bool", name: "retry-connrefused" }, "no-retry-connrefused": { type: "bool", name: "retry-connrefused", expand: false }, "retry-delay": { type: "string", name: "retry-delay" }, "retry-max-time": { type: "string", name: "retry-max-time" }, "proxy-negotiate": { type: "bool", name: "proxy-negotiate" }, "no-proxy-negotiate": { type: "bool", name: "proxy-negotiate", expand: false }, "form-escape": { type: "bool", name: "form-escape" }, "no-form-escape": { type: "bool", name: "form-escape", expand: false }, "ftp-account": { type: "string", name: "ftp-account" }, "proxy-anyauth": { type: "bool", name: "proxy-anyauth" }, "no-proxy-anyauth": { type: "bool", name: "proxy-anyauth", expand: false }, "trace-time": { type: "bool", name: "trace-time" }, "no-trace-time": { type: "bool", name: "trace-time", expand: false }, "ignore-content-length": { type: "bool", name: "ignore-content-length" }, "no-ignore-content-length": { type: "bool", name: "ignore-content-length", expand: false }, "ftp-skip-pasv-ip": { type: "bool", name: "ftp-skip-pasv-ip" }, "no-ftp-skip-pasv-ip": { type: "bool", name: "ftp-skip-pasv-ip", expand: false }, "ftp-method": { type: "string", name: "ftp-method" }, "local-port": { type: "string", name: "local-port" }, "socks4": { type: "string", name: "socks4" }, "socks4a": { type: "string", name: "socks4a" }, "ftp-alternative-to-user": { type: "string", name: "ftp-alternative-to-user" }, "ftp-ssl-reqd": { type: "bool", name: "ssl-reqd" }, "no-ftp-ssl-reqd": { type: "bool", name: "ssl-reqd", expand: false }, "ssl-reqd": { type: "bool", name: "ssl-reqd" }, "no-ssl-reqd": { type: "bool", name: "ssl-reqd", expand: false }, "sessionid": { type: "bool", name: "sessionid" }, "no-sessionid": { type: "bool", name: "sessionid", expand: false }, "ftp-ssl-control": { type: "bool", name: "ftp-ssl-control" }, "no-ftp-ssl-control": { type: "bool", name: "ftp-ssl-control", expand: false }, "ftp-ssl-ccc": { type: "bool", name: "ftp-ssl-ccc" }, "no-ftp-ssl-ccc": { type: "bool", name: "ftp-ssl-ccc", expand: false }, "ftp-ssl-ccc-mode": { type: "string", name: "ftp-ssl-ccc-mode" }, "libcurl": { type: "string", name: "libcurl" }, "raw": { type: "bool", name: "raw" }, "no-raw": { type: "bool", name: "raw", expand: false }, "post301": { type: "bool", name: "post301" }, "no-post301": { type: "bool", name: "post301", expand: false }, "keepalive": { type: "bool", name: "keepalive" }, "no-keepalive": { type: "bool", name: "keepalive", expand: false }, "socks5-hostname": { type: "string", name: "socks5-hostname" }, "keepalive-time": { type: "string", name: "keepalive-time" }, "post302": { type: "bool", name: "post302" }, "no-post302": { type: "bool", name: "post302", expand: false }, "noproxy": { type: "string", name: "noproxy" }, "socks5-gssapi-nec": { type: "bool", name: "socks5-gssapi-nec" }, "no-socks5-gssapi-nec": { type: "bool", name: "socks5-gssapi-nec", expand: false }, "proxy1.0": { type: "string", name: "proxy1.0" }, "tftp-blksize": { type: "string", name: "tftp-blksize" }, "mail-from": { type: "string", name: "mail-from" }, "mail-rcpt": { type: "string", name: "mail-rcpt" }, "ftp-pret": { type: "bool", name: "ftp-pret" }, "no-ftp-pret": { type: "bool", name: "ftp-pret", expand: false }, "proto": { type: "string", name: "proto" }, "proto-redir": { type: "string", name: "proto-redir" }, "resolve": { type: "string", name: "resolve" }, "delegation": { type: "string", name: "delegation" }, "mail-auth": { type: "string", name: "mail-auth" }, "post303": { type: "bool", name: "post303" }, "no-post303": { type: "bool", name: "post303", expand: false }, "metalink": { type: "bool", name: "metalink" }, "no-metalink": { type: "bool", name: "metalink", expand: false }, "sasl-authzid": { type: "string", name: "sasl-authzid" }, "sasl-ir": { type: "bool", name: "sasl-ir" }, "no-sasl-ir": { type: "bool", name: "sasl-ir", expand: false }, "test-event": { type: "bool", name: "test-event" }, "no-test-event": { type: "bool", name: "test-event", expand: false }, "unix-socket": { type: "string", name: "unix-socket" }, "path-as-is": { type: "bool", name: "path-as-is" }, "no-path-as-is": { type: "bool", name: "path-as-is", expand: false }, "socks5-gssapi-service": { type: "string", name: "proxy-service-name" }, "proxy-service-name": { type: "string", name: "proxy-service-name" }, "service-name": { type: "string", name: "service-name" }, "proto-default": { type: "string", name: "proto-default" }, "expect100-timeout": { type: "string", name: "expect100-timeout" }, "tftp-no-options": { type: "bool", name: "tftp-no-options" }, "no-tftp-no-options": { type: "bool", name: "tftp-no-options", expand: false }, "connect-to": { type: "string", name: "connect-to" }, "abstract-unix-socket": { type: "string", name: "abstract-unix-socket" }, "tls-max": { type: "string", name: "tls-max" }, "suppress-connect-headers": { type: "bool", name: "suppress-connect-headers" }, "no-suppress-connect-headers": { type: "bool", name: "suppress-connect-headers", expand: false }, "compressed-ssh": { type: "bool", name: "compressed-ssh" }, "no-compressed-ssh": { type: "bool", name: "compressed-ssh", expand: false }, "happy-eyeballs-timeout-ms": { type: "string", name: "happy-eyeballs-timeout-ms" }, "retry-all-errors": { type: "bool", name: "retry-all-errors" }, "no-retry-all-errors": { type: "bool", name: "retry-all-errors", expand: false }, "trace-ids": { type: "bool", name: "trace-ids" }, "no-trace-ids": { type: "bool", name: "trace-ids", expand: false }, "http1.0": { type: "bool", name: "http1.0" }, "http1.1": { type: "bool", name: "http1.1" }, "http2": { type: "bool", name: "http2" }, "http2-prior-knowledge": { type: "bool", name: "http2-prior-knowledge" }, "http3": { type: "bool", name: "http3" }, "http3-only": { type: "bool", name: "http3-only" }, "http0.9": { type: "bool", name: "http0.9" }, "no-http0.9": { type: "bool", name: "http0.9", expand: false }, "proxy-http2": { type: "bool", name: "proxy-http2" }, "no-proxy-http2": { type: "bool", name: "proxy-http2", expand: false }, "tlsv1": { type: "bool", name: "tlsv1" }, "tlsv1.0": { type: "bool", name: "tlsv1.0" }, "tlsv1.1": { type: "bool", name: "tlsv1.1" }, "tlsv1.2": { type: "bool", name: "tlsv1.2" }, "tlsv1.3": { type: "bool", name: "tlsv1.3" }, "tls13-ciphers": { type: "string", name: "tls13-ciphers" }, "proxy-tls13-ciphers": { type: "string", name: "proxy-tls13-ciphers" }, "sslv2": { type: "bool", name: "sslv2" }, "sslv3": { type: "bool", name: "sslv3" }, "ipv4": { type: "bool", name: "ipv4" }, "ipv6": { type: "bool", name: "ipv6" }, "append": { type: "bool", name: "append" }, "no-append": { type: "bool", name: "append", expand: false }, "user-agent": { type: "string", name: "user-agent" }, "cookie": { type: "string", name: "cookie" }, "alt-svc": { type: "string", name: "alt-svc" }, "hsts": { type: "string", name: "hsts" }, "use-ascii": { type: "bool", name: "use-ascii" }, "no-use-ascii": { type: "bool", name: "use-ascii", expand: false }, "cookie-jar": { type: "string", name: "cookie-jar" }, "continue-at": { type: "string", name: "continue-at" }, "data": { type: "string", name: "data" }, "data-raw": { type: "string", name: "data-raw" }, "data-ascii": { type: "string", name: "data-ascii" }, "data-binary": { type: "string", name: "data-binary" }, "data-urlencode": { type: "string", name: "data-urlencode" }, "json": { type: "string", name: "json" }, "url-query": { type: "string", name: "url-query" }, "dump-header": { type: "string", name: "dump-header" }, "referer": { type: "string", name: "referer" }, "cert": { type: "string", name: "cert" }, "cacert": { type: "string", name: "cacert" }, "cert-type": { type: "string", name: "cert-type" }, "key": { type: "string", name: "key" }, "key-type": { type: "string", name: "key-type" }, "pass": { type: "string", name: "pass" }, "engine": { type: "string", name: "engine" }, "ca-native": { type: "bool", name: "ca-native" }, "no-ca-native": { type: "bool", name: "ca-native", expand: false }, "proxy-ca-native": { type: "bool", name: "proxy-ca-native" }, "no-proxy-ca-native": { type: "bool", name: "proxy-ca-native", expand: false }, "capath": { type: "string", name: "capath" }, "pubkey": { type: "string", name: "pubkey" }, "hostpubmd5": { type: "string", name: "hostpubmd5" }, "hostpubsha256": { type: "string", name: "hostpubsha256" }, "crlfile": { type: "string", name: "crlfile" }, "tlsuser": { type: "string", name: "tlsuser" }, "tlspassword": { type: "string", name: "tlspassword" }, "tlsauthtype": { type: "string", name: "tlsauthtype" }, "ssl-allow-beast": { type: "bool", name: "ssl-allow-beast" }, "no-ssl-allow-beast": { type: "bool", name: "ssl-allow-beast", expand: false }, "ssl-auto-client-cert": { type: "bool", name: "ssl-auto-client-cert" }, "no-ssl-auto-client-cert": { type: "bool", name: "ssl-auto-client-cert", expand: false }, "proxy-ssl-auto-client-cert": { type: "bool", name: "proxy-ssl-auto-client-cert" }, "no-proxy-ssl-auto-client-cert": { type: "bool", name: "proxy-ssl-auto-client-cert", expand: false }, "pinnedpubkey": { type: "string", name: "pinnedpubkey" }, "proxy-pinnedpubkey": { type: "string", name: "proxy-pinnedpubkey" }, "cert-status": { type: "bool", name: "cert-status" }, "no-cert-status": { type: "bool", name: "cert-status", expand: false }, "doh-cert-status": { type: "bool", name: "doh-cert-status" }, "no-doh-cert-status": { type: "bool", name: "doh-cert-status", expand: false }, "false-start": { type: "bool", name: "false-start" }, "no-false-start": { type: "bool", name: "false-start", expand: false }, "ssl-no-revoke": { type: "bool", name: "ssl-no-revoke" }, "no-ssl-no-revoke": { type: "bool", name: "ssl-no-revoke", expand: false }, "ssl-revoke-best-effort": { type: "bool", name: "ssl-revoke-best-effort" }, "no-ssl-revoke-best-effort": { type: "bool", name: "ssl-revoke-best-effort", expand: false }, "tcp-fastopen": { type: "bool", name: "tcp-fastopen" }, "no-tcp-fastopen": { type: "bool", name: "tcp-fastopen", expand: false }, "proxy-tlsuser": { type: "string", name: "proxy-tlsuser" }, "proxy-tlspassword": { type: "string", name: "proxy-tlspassword" }, "proxy-tlsauthtype": { type: "string", name: "proxy-tlsauthtype" }, "proxy-cert": { type: "string", name: "proxy-cert" }, "proxy-cert-type": { type: "string", name: "proxy-cert-type" }, "proxy-key": { type: "string", name: "proxy-key" }, "proxy-key-type": { type: "string", name: "proxy-key-type" }, "proxy-pass": { type: "string", name: "proxy-pass" }, "proxy-ciphers": { type: "string", name: "proxy-ciphers" }, "proxy-crlfile": { type: "string", name: "proxy-crlfile" }, "proxy-ssl-allow-beast": { type: "bool", name: "proxy-ssl-allow-beast" }, "no-proxy-ssl-allow-beast": { type: "bool", name: "proxy-ssl-allow-beast", expand: false }, "login-options": { type: "string", name: "login-options" }, "proxy-cacert": { type: "string", name: "proxy-cacert" }, "proxy-capath": { type: "string", name: "proxy-capath" }, "proxy-insecure": { type: "bool", name: "proxy-insecure" }, "no-proxy-insecure": { type: "bool", name: "proxy-insecure", expand: false }, "proxy-tlsv1": { type: "bool", name: "proxy-tlsv1" }, "socks5-basic": { type: "bool", name: "socks5-basic" }, "no-socks5-basic": { type: "bool", name: "socks5-basic", expand: false }, "socks5-gssapi": { type: "bool", name: "socks5-gssapi" }, "no-socks5-gssapi": { type: "bool", name: "socks5-gssapi", expand: false }, "etag-save": { type: "string", name: "etag-save" }, "etag-compare": { type: "string", name: "etag-compare" }, "curves": { type: "string", name: "curves" }, "fail": { type: "bool", name: "fail" }, "no-fail": { type: "bool", name: "fail", expand: false }, "fail-early": { type: "bool", name: "fail-early" }, "no-fail-early": { type: "bool", name: "fail-early", expand: false }, "styled-output": { type: "bool", name: "styled-output" }, "no-styled-output": { type: "bool", name: "styled-output", expand: false }, "mail-rcpt-allowfails": { type: "bool", name: "mail-rcpt-allowfails" }, "no-mail-rcpt-allowfails": { type: "bool", name: "mail-rcpt-allowfails", expand: false }, "fail-with-body": { type: "bool", name: "fail-with-body" }, "no-fail-with-body": { type: "bool", name: "fail-with-body", expand: false }, "remove-on-error": { type: "bool", name: "remove-on-error" }, "no-remove-on-error": { type: "bool", name: "remove-on-error", expand: false }, "form": { type: "string", name: "form" }, "form-string": { type: "string", name: "form-string" }, "globoff": { type: "bool", name: "globoff" }, "no-globoff": { type: "bool", name: "globoff", expand: false }, "get": { type: "bool", name: "get" }, "no-get": { type: "bool", name: "get", expand: false }, "request-target": { type: "string", name: "request-target" }, "help": { type: "bool", name: "help" }, "no-help": { type: "bool", name: "help", expand: false }, "header": { type: "string", name: "header" }, "proxy-header": { type: "string", name: "proxy-header" }, "include": { type: "bool", name: "include" }, "no-include": { type: "bool", name: "include", expand: false }, "head": { type: "bool", name: "head" }, "no-head": { type: "bool", name: "head", expand: false }, "junk-session-cookies": { type: "bool", name: "junk-session-cookies" }, "no-junk-session-cookies": { type: "bool", name: "junk-session-cookies", expand: false }, "remote-header-name": { type: "bool", name: "remote-header-name" }, "no-remote-header-name": { type: "bool", name: "remote-header-name", expand: false }, "insecure": { type: "bool", name: "insecure" }, "no-insecure": { type: "bool", name: "insecure", expand: false }, "doh-insecure": { type: "bool", name: "doh-insecure" }, "no-doh-insecure": { type: "bool", name: "doh-insecure", expand: false }, "config": { type: "string", name: "config" }, "list-only": { type: "bool", name: "list-only" }, "no-list-only": { type: "bool", name: "list-only", expand: false }, "location": { type: "bool", name: "location" }, "no-location": { type: "bool", name: "location", expand: false }, "location-trusted": { type: "bool", name: "location-trusted" }, "no-location-trusted": { type: "bool", name: "location-trusted", expand: false }, "max-time": { type: "string", name: "max-time" }, "manual": { type: "bool", name: "manual" }, "no-manual": { type: "bool", name: "manual", expand: false }, "netrc": { type: "bool", name: "netrc" }, "no-netrc": { type: "bool", name: "netrc", expand: false }, "netrc-optional": { type: "bool", name: "netrc-optional" }, "no-netrc-optional": { type: "bool", name: "netrc-optional", expand: false }, "netrc-file": { type: "string", name: "netrc-file" }, "buffer": { type: "bool", name: "buffer" }, "no-buffer": { type: "bool", name: "buffer", expand: false }, "output": { type: "string", name: "output" }, "remote-name": { type: "bool", name: "remote-name" }, "no-remote-name": { type: "bool", name: "remote-name", expand: false }, "remote-name-all": { type: "bool", name: "remote-name-all" }, "no-remote-name-all": { type: "bool", name: "remote-name-all", expand: false }, "output-dir": { type: "string", name: "output-dir" }, "clobber": { type: "bool", name: "clobber" }, "no-clobber": { type: "bool", name: "clobber", expand: false }, "proxytunnel": { type: "bool", name: "proxytunnel" }, "no-proxytunnel": { type: "bool", name: "proxytunnel", expand: false }, "ftp-port": { type: "string", name: "ftp-port" }, "disable": { type: "bool", name: "disable" }, "no-disable": { type: "bool", name: "disable", expand: false }, "quote": { type: "string", name: "quote" }, "range": { type: "string", name: "range" }, "remote-time": { type: "bool", name: "remote-time" }, "no-remote-time": { type: "bool", name: "remote-time", expand: false }, "silent": { type: "bool", name: "silent" }, "no-silent": { type: "bool", name: "silent", expand: false }, "show-error": { type: "bool", name: "show-error" }, "no-show-error": { type: "bool", name: "show-error", expand: false }, "telnet-option": { type: "string", name: "telnet-option" }, "upload-file": { type: "string", name: "upload-file" }, "user": { type: "string", name: "user" }, "proxy-user": { type: "string", name: "proxy-user" }, "verbose": { type: "bool", name: "verbose" }, "no-verbose": { type: "bool", name: "verbose", expand: false }, "version": { type: "bool", name: "version" }, "no-version": { type: "bool", name: "version", expand: false }, "write-out": { type: "string", name: "write-out" }, "proxy": { type: "string", name: "proxy" }, "preproxy": { type: "string", name: "preproxy" }, "request": { type: "string", name: "request" }, "speed-limit": { type: "string", name: "speed-limit" }, "speed-time": { type: "string", name: "speed-time" }, "time-cond": { type: "string", name: "time-cond" }, "parallel": { type: "bool", name: "parallel" }, "no-parallel": { type: "bool", name: "parallel", expand: false }, "parallel-max": { type: "string", name: "parallel-max" }, "parallel-immediate": { type: "bool", name: "parallel-immediate" }, "no-parallel-immediate": { type: "bool", name: "parallel-immediate", expand: false }, "progress-bar": { type: "bool", name: "progress-bar" }, "no-progress-bar": { type: "bool", name: "progress-bar", expand: false }, "progress-meter": { type: "bool", name: "progress-meter" }, "no-progress-meter": { type: "bool", name: "progress-meter", expand: false }, "next": { type: "bool", name: "next" }, // END EXTRACTED OPTIONS // These are options that curl used to have. // Those that don't conflict with the current options are supported by curlconverter. // TODO: curl's --long-options can be shortened. // For example if curl used to only have a single option, "--blah" then // "--bla" "--bl" and "--b" all used to be valid options as well. If later // "--blaz" was added, suddenly those 3 shortened options are removed (because // they are now ambiguous). // https://github.com/curlconverter/curlconverter/pull/280#issuecomment-931241328 port: { type: "string", name: "port", removed: "7.3" }, // These are now shoretened forms of --upload-file and --continue-at //upload: { type: "bool", name: "upload", removed: "7.7" }, //continue: { type: "bool", name: "continue", removed: "7.9" }, "ftp-ascii": { type: "bool", name: "use-ascii", removed: "7.10.7" }, "3p-url": { type: "string", name: "3p-url", removed: "7.16.0" }, "3p-user": { type: "string", name: "3p-user", removed: "7.16.0" }, "3p-quote": { type: "string", name: "3p-quote", removed: "7.16.0" }, "http2.0": { type: "bool", name: "http2", removed: "7.36.0" }, "no-http2.0": { type: "bool", name: "http2", removed: "7.36.0" }, "telnet-options": { type: "string", name: "telnet-option", removed: "7.49.0" }, "http-request": { type: "string", name: "request", removed: "7.49.0" }, // --socks is now an ambiguous shortening of --socks4, --socks5 and a bunch more //socks: { type: "string", name: "socks5", removed: "7.49.0" }, "capath ": { type: "string", name: "capath", removed: "7.49.0" }, // trailing space ftpport: { type: "string", name: "ftp-port", removed: "7.49.0" }, environment: { type: "bool", name: "environment", removed: "7.54.1" }, // These never had any effect "no-tlsv1": { type: "bool", name: "tlsv1", removed: "7.54.1" }, "no-tlsv1.2": { type: "bool", name: "tlsv1.2", removed: "7.54.1" }, "no-http2-prior-knowledge": { type: "bool", name: "http2-prior-knowledge", removed: "7.54.1" }, "no-ipv6": { type: "bool", name: "ipv6", removed: "7.54.1" }, "no-ipv4": { type: "bool", name: "ipv4", removed: "7.54.1" }, "no-sslv2": { type: "bool", name: "sslv2", removed: "7.54.1" }, "no-tlsv1.0": { type: "bool", name: "tlsv1.0", removed: "7.54.1" }, "no-tlsv1.1": { type: "bool", name: "tlsv1.1", removed: "7.54.1" }, "no-sslv3": { type: "bool", name: "sslv3", removed: "7.54.1" }, "no-http1.0": { type: "bool", name: "http1.0", removed: "7.54.1" }, "no-next": { type: "bool", name: "next", removed: "7.54.1" }, "no-tlsv1.3": { type: "bool", name: "tlsv1.3", removed: "7.54.1" }, "no-environment": { type: "bool", name: "environment", removed: "7.54.1" }, "no-http1.1": { type: "bool", name: "http1.1", removed: "7.54.1" }, "no-proxy-tlsv1": { type: "bool", name: "proxy-tlsv1", removed: "7.54.1" }, "no-http2": { type: "bool", name: "http2", removed: "7.54.1" }, } as const; // curl lets you not type the full argument as long as it's unambiguous. // So --sil instead of --silent is okay, --s is not. export const curlLongOptsShortened: { [key: string]: LongShort | null } = {}; for (const [opt, val] of Object.entries(curlLongOpts)) { const expand = "expand" in val ? val.expand : true; const removed = "removed" in val ? val.removed : false; if (expand && !removed) { for (let i = 1; i < opt.length; i++) { const shortenedOpt = opt.slice(0, i); if ( !Object.prototype.hasOwnProperty.call( curlLongOptsShortened, shortenedOpt, ) ) { if (!Object.prototype.hasOwnProperty.call(curlLongOpts, shortenedOpt)) { curlLongOptsShortened[shortenedOpt] = val; } } else { // If more than one option shortens to this, it's ambiguous curlLongOptsShortened[shortenedOpt] = null; } } } } // Arguments which are supported by all generators, because they're // easy to implement or because they're handled by upstream code and // affect something that's easy to implement. export const COMMON_SUPPORTED_ARGS: string[] = [ "url", "proto-default", // Controls whether or not backslash-escaped [] {} will have the backslash removed. "globoff", // curl will exit if it finds auth credentials in the URL with this option, // we remove it from the URL and emit a warning instead. "disallow-username-in-url", // Method "request", "get", "head", "no-head", // Headers "header", // TODO: can be a file "user-agent", "referer", "range", "time-cond", "cookie", // TODO: can be a file "oauth2-bearer", // Basic Auth "user", "basic", "no-basic", // Data "data", "data-raw", "data-ascii", "data-binary", "data-urlencode", "json", "url-query", // TODO: --compressed is already the default for some runtimes, in // which case we might have to only warn that --no-compressed isn't supported. ]; export function toBoolean(opt: string): boolean { if (opt.startsWith("no-disable-")) { return true; } if (opt.startsWith("disable-") || opt.startsWith("no-")) { return false; } return true; } // prettier-ignore export const curlShortOpts: { [key: string]: keyof typeof curlLongOpts } = { // BEGIN EXTRACTED SHORT OPTIONS "0": "http1.0", "1": "tlsv1", "2": "sslv2", "3": "sslv3", "4": "ipv4", "6": "ipv6", "a": "append", "A": "user-agent", "b": "cookie", "B": "use-ascii", "c": "cookie-jar", "C": "continue-at", "d": "data", "D": "dump-header", "e": "referer", "E": "cert", "f": "fail", "F": "form", "g": "globoff", "G": "get", "h": "help", "H": "header", "i": "include", "I": "head", "j": "junk-session-cookies", "J": "remote-header-name", "k": "insecure", "K": "config", "l": "list-only", "L": "location", "m": "max-time", "M": "manual", "n": "netrc", "N": "no-buffer", "o": "output", "O": "remote-name", "p": "proxytunnel", "P": "ftp-port", "q": "disable", "Q": "quote", "r": "range", "R": "remote-time", "s": "silent", "S": "show-error", "t": "telnet-option", "T": "upload-file", "u": "user", "U": "proxy-user", "v": "verbose", "V": "version", "w": "write-out", "x": "proxy", "X": "request", "Y": "speed-limit", "y": "speed-time", "z": "time-cond", "Z": "parallel", "#": "progress-bar", ":": "next", // END EXTRACTED SHORT OPTIONS } as const; export const changedShortOpts: ShortOpts = { p: "used to be short for --port <port> (a since-deleted flag) until curl 7.3", // TODO: some of these might be renamed options t: "used to be short for --upload (a since-deleted boolean flag) until curl 7.7", c: "used to be short for --continue (a since-deleted boolean flag) until curl 7.9", // TODO: did -@ actually work? "@": "used to be short for --create-dirs until curl 7.10.7", Z: "used to be short for --max-redirs <num> until curl 7.10.7", 9: "used to be short for --crlf until curl 7.10.8", 8: "used to be short for --stderr <file> until curl 7.10.8", 7: "used to be short for --interface <name> until curl 7.10.8", 6: "used to be short for --krb <level> (which itself used to be --krb4 <level>) until curl 7.10.8", // TODO: did these short options ever actually work? 5: "used to be another way to specify the url until curl 7.10.8", "*": "used to be another way to specify the url until curl 7.49.0", "~": "used to be short for --xattr until curl 7.49.0", } as const; export type SrcFormParam = { value: Word; type: FormType }; export type SrcDataParam = [DataType, Word]; // The keys should be named the same as the curl options that // set them because they appear in error messages. export interface OperationConfig { request?: Word; // the HTTP method // Not the same name as the curl options that set it authtype: number; proxyauthtype: number; json?: boolean; // canBeList url?: Word[]; "upload-file"?: Word[]; output?: Word[]; header?: Word[]; "proxy-header"?: Word[]; form?: SrcFormParam[]; data?: SrcDataParam[]; "url-query"?: SrcDataParam[]; "mail-rcpt"?: Word[]; resolve?: Word[]; "connect-to"?: Word[]; cookie?: Word[]; quote?: Word[]; "telnet-option"?: Word[]; httpVersion?: "1.0" | "1.1" | "2" | "2-prior-knowledge" | "3" | "3-only"; tlsVersion?: "1" | "1.0" | "1.1" | "1.2" | "1.3"; netrc?: boolean; "netrc-optional"?: boolean; "netrc-file"?: Word; compressed?: boolean; head?: boolean; get?: boolean; ipv4?: boolean; ipv6?: boolean; ciphers?: Word; insecure?: boolean; cert?: Word; "cert-type"?: Word; key?: Word; "key-type"?: Word; cacert?: Word; capath?: Word; crlfile?: Word; pinnedpubkey?: Word; "random-file"?: Word; "egd-file"?: Word; hsts?: Word[]; "proto-default"?: Word; globoff?: boolean; "max-redirs"?: Word; location?: boolean; "location-trusted"?: boolean; proxy?: Word; "proxy-user"?: Word; noproxy?: Word; range?: Word; referer?: Word; "time-cond"?: Word; "user-agent"?: Word; user?: Word; "aws-sigv4"?: Word; delegation?: Word; "oauth2-bearer"?: Word; "max-time"?: Word; "connect-timeout"?: Word; "cookie-jar"?: Word; "unix-socket"?: Word; // "abstract-unix-socket"?: Word; // This needs to be updated any time a new argument is added "3p-quote"?: Word; "3p-url"?: Word; "3p-user"?: Word; "abstract-unix-socket"?: Word; alpn?: boolean; "alt-svc"?: Word; anyauth?: boolean; append?: boolean; basic?: boolean; buffer?: boolean; "ca-native"?: boolean; "cert-status"?: boolean; clobber?: boolean; "compressed-ssh"?: boolean; "continue-at"?: Word; "create-dirs"?: boolean; "create-file-mode"?: Word; crlf?: boolean; curves?: Word; "data-ascii"?: Word; "data-binary"?: Word; "data-raw"?: Word; "data-urlencode"?: Word; digest?: boolean; disable?: boolean; "disable-eprt"?: boolean; "disable-epsv"?: boolean; "disallow-username-in-url"?: boolean; "dns-interface"?: Word; "dns-ipv4-addr"?: Word; "dns-ipv6-addr"?: Word; "dns-servers"?: Word; "doh-cert-status"?: boolean; "doh-insecure"?: boolean; "doh-url"?: Word; "dump-header"?: Word; engine?: Word; environment?: boolean; eprt?: boolean; epsv?: boolean; "etag-compare"?: Word; "etag-save"?: Word; "expect100-timeout"?: Word; fail?: boolean; "fail-with-body"?: boolean; "false-start"?: boolean; "form-escape"?: boolean; "form-string"?: Word; "ftp-account"?: Word; "ftp-alternative-to-user"?: Word; "ftp-create-dirs"?: boolean; "ftp-method"?: Word; "ftp-pasv"?: boolean; "ftp-port"?: Word; "ftp-pret"?: boolean; "ftp-skip-pasv-ip"?: boolean; "ftp-ssl-ccc"?: boolean; "ftp-ssl-ccc-mode"?: Word; "ftp-ssl-control"?: boolean; "happy-eyeballs-timeout-ms"?: Word; "haproxy-clientip"?: Word; "haproxy-protocol"?: boolean; hostpubmd5?: Word; hostpubsha256?: Word; "http0.9"?: boolean; "http1.0"?: boolean; "http1.1"?: boolean; "http2-prior-knowledge"?: boolean; "ignore-content-length"?: boolean; include?: boolean; interface?: Word; "junk-session-cookies"?: boolean; keepalive?: boolean; "keepalive-time"?: Word; krb?: Word; "limit-rate"?: Word; "list-only"?: boolean; "local-port"?: Word; "login-options"?: Word; "mail-auth"?: Word; "mail-from"?: Word; "mail-rcpt-allowfails"?: boolean; manual?: boolean; "max-filesize"?: Word; metalink?: boolean; negotiate?: boolean; next?: boolean; npn?: boolean; ntlm?: boolean; "ntlm-wb"?: boolean; "output-dir"?: Word; pass?: Word; "path-as-is"?: boolean; port?: Word; post301?: boolean; post302?: boolean; post303?: boolean; preproxy?: Word; proto?: Word; "proto-redir"?: Word; "proxy-anyauth"?: boolean; "proxy-basic"?: boolean; "proxy-cacert"?: Word; "proxy-capath"?: Word; "proxy-cert"?: Word; "proxy-cert-type"?: Word; "proxy-ciphers"?: Word; "proxy-crlfile"?: Word; "proxy-digest"?: boolean; "proxy-insecure"?: boolean; "proxy-key"?: Word; "proxy-key-type"?: Word; "proxy-negotiate"?: boolean; "proxy-ntlm"?: boolean; "proxy-pass"?: Word; "proxy-pinnedpubkey"?: Word; "proxy-service-name"?: Word; "proxy-ssl-allow-beast"?: boolean; "proxy-ssl-auto-client-cert"?: boolean; "proxy-tls13-ciphers"?: Word; "proxy-tlsauthtype"?: Word; "proxy-tlspassword"?: Word; "proxy-tlsuser"?: Word; "proxy-tlsv1"?: boolean; "proxy1.0"?: Word; proxytunnel?: boolean; pubkey?: Word; rate?: Word; raw?: boolean; "remote-header-name"?: boolean; "remote-name"?: boolean; "remote-name-all"?: boolean; "remote-time"?: boolean; "remove-on-error"?: boolean; "request-target"?: Word; retry?: Word; "retry-all-errors"?: boolean; "retry-connrefused"?: boolean; "retry-delay"?: Word; "retry-max-time"?: Word; "sasl-authzid"?: Word; "sasl-ir"?: boolean; "service-name"?: Word; sessionid?: boolean; socks4?: Word; socks4a?: Word; socks5?: Word; "socks5-basic"?: boolean; "socks5-gssapi"?: boolean; "socks5-gssapi-nec"?: boolean; "socks5-hostname"?: Word; "speed-limit"?: Word; "speed-time"?: Word; ssl?: boolean; "ssl-allow-beast"?: boolean; "ssl-auto-client-cert"?: boolean; "ssl-no-revoke"?: boolean; "ssl-reqd"?: boolean; "ssl-revoke-best-effort"?: boolean; sslv2?: boolean; sslv3?: boolean; "suppress-connect-headers"?: boolean; "tcp-fastopen"?: boolean; "tcp-nodelay"?: boolean; "tftp-blksize"?: Word; "tftp-no-options"?: boolean; "tls-max"?: Word; "tls13-ciphers"?: Word; tlsauthtype?: Word; tlspassword?: Word; tlsuser?: Word; tlsv1?: boolean; "tlsv1.0"?: boolean; "tlsv1.1"?: boolean; "tlsv1.2"?: boolean; "tlsv1.3"?: boolean; "tr-encoding"?: boolean; "use-ascii"?: boolean; version?: boolean; wdebug?: boolean; "write-out"?: Word; xattr?: boolean; // TODO: --npn not supported warning // TODO: --metalink disabled warning // TODO: remove any. // This is difficult because we have curl's arguments but also a couple // curlconverter-specific arguments that are handled by the same code. [key: string]: any; } // type Satisfies<T, U extends T> = void; // type AssertSubsetKeys = Satisfies< // keyof typeof curlLongOpts | "authtype" | "proxyauthtype", // keyof OperationConfig // >; // These options can be specified more than once, they // are always returned as a list. // For all other options, if you specify it more than once // curl will use the last one. const canBeList = new Set<keyof OperationConfig>([ "connect-to", "cookie", "data", "form", "header", "hsts", "mail-rcpt", "output", "proxy-header", "quote", "resolve", "telnet-option", "upload-file", "url-query", "url", ]); export interface GlobalConfig { version?: boolean; trace?: Word; "trace-ascii"?: Word; "trace-time"?: boolean; stderr?: Word; libcurl?: Word; "test-event"?: boolean; "progress-bar"?: boolean; "progress-meter"?: boolean; "fail-early"?: boolean; "styled-output"?: boolean; help?: boolean; // TODO: might use the next word, then curl exits immediately config?: Word; // TODO: is it really global? silent?: boolean; "show-error"?: boolean; verbose?: boolean; parallel?: boolean; "parallel-immediate"?: boolean; "parallel-max"?: Word; configs: OperationConfig[]; warnings: Warnings; // These are specific to the curlconverter cli language?: string; stdin?: boolean; } function checkSupported( global_: GlobalConfig, lookup: string, longArg: LongShort, supportedOpts?: Set<string>, ) { if (supportedOpts && !supportedOpts.has(longArg.name)) { // TODO: better message. include generator name? warnf(global_, [ longArg.name, lookup + " is not a supported option" + (longArg.removed ? ", it was removed in curl " + longArg.removed : ""), ]); } } export function pushProp<Type>( obj: { [key: string]: Type[] }, prop: string, value: Type, ) { if (!Object.prototype.hasOwnProperty.call(obj, prop)) { obj[prop] = []; } obj[prop].push(value); return obj; } function pushArgValue( global_: GlobalConfig, config: OperationConfig, argName: string, value: Word, ) { // Note: cli.ts assumes that the property names on OperationConfig // are the same as the passed in argument in an error message, so // if you do something like // echo curl example.com | curlconverter - --data-raw foo // The error message will say // "if you pass --stdin or -, you can't also pass --data" // instead of "--data-raw". switch (argName) { case "data": case "data-ascii": return pushProp(config, "data", ["data", value]); case "data-binary": return pushProp(config, "data", [ // Unless it's a file, --data-binary works the same as --data value.startsWith("@") ? "binary" : "data", value, ]); case "data-raw": return pushProp(config, "data", [ // Unless it's a file, --data-raw works the same as --data value.startsWith("@") ? "raw" : "data", value, ]); case "data-urlencode": return pushProp(config, "data", ["urlencode", value]); case "json": config.json = true; return pushProp(config, "data", ["json", value]); case "url-query": if (value.startsWith("+")) { return pushProp(config, "url-query", ["raw", value.slice(1)]); } return pushProp(config, "url-query", ["urlencode", value]); case "form": return pushProp(config, "form", { value, type: "form" }); case "form-string": return pushProp(config, "form", { value, type: "string" }); case "aws-sigv4": config.authtype |= CURLAUTH_AWS_SIGV4; break; case "oauth2-bearer": config.authtype |= CURLAUTH_BEARER; break; case "unix-socket": case "abstract-unix-socket": // Ignore distinction // TODO: this makes the error message wrong // TODO: what's the difference? pushProp(config, "unix-socket", value); break; case "trace": case "trace-ascii": case "stderr": case "libcurl": case "config": case "parallel-max": global_[argName] = value; break; case "language": // --language is a curlconverter specific option global_[argName] = value.toString(); return; } return pushProp(config, argName, value); } // Might create a new config function setArgValue( global_: GlobalConfig, config: OperationConfig, argName: string, toggle: boolean, ): OperationConfig { switch (argName) { case "digest": if (toggle) { config.authtype |= CURLAUTH_DIGEST; } else { config.authtype &= ~CURLAUTH_DIGEST; } break; case "proxy-digest": if (toggle) { config.proxyauthtype |= CURLAUTH_DIGEST; } else { config.proxyauthtype &= ~CURLAUTH_DIGEST; } break; case "negotiate": if (toggle) { config.authtype |= CURLAUTH_NEGOTIATE; } else { config.authtype &= ~CURLAUTH_NEGOTIATE; } break; case "proxy-negotiate": if (toggle) { config.proxyauthtype |= CURLAUTH_NEGOTIATE; } else { config.proxyauthtype &= ~CURLAUTH_NEGOTIATE; } break; case "ntlm": if (toggle) { config.authtype |= CURLAUTH_NTLM; } else { config.authtype &= ~CURLAUTH_NTLM; } break; case "proxy-ntlm": if (toggle) { config.proxyauthtype |= CURLAUTH_NTLM; } else { config.proxyauthtype &= ~CURLAUTH_NTLM; } break; case "ntlm-wb": if (toggle) { config.authtype |= CURLAUTH_NTLM_WB; } else { config.authtype &= ~CURLAUTH_NTLM_WB; } break; case "basic": if (toggle) { config.authtype |= CURLAUTH_BASIC; } else { config.authtype &= ~CURLAUTH_BASIC; } break; case "proxy-basic": if (toggle) { config.proxyauthtype |= CURLAUTH_BASIC; } else { config.proxyauthtype &= ~CURLAUTH_BASIC; } break; case "anyauth": if (toggle) { config.authtype = CURLAUTH_ANY; } break; case "proxy-anyauth": if (toggle) { config.proxyauthtype = CURLAUTH_ANY; } break; case "location": config["location"] = toggle; break; case "location-trusted": config["location"] = toggle; config["location-trusted"] = toggle; break; case "http1.0": config.httpVersion = "1.0"; break; case "http1.1": config.httpVersion = "1.1"; break; case "http2": config.httpVersion = "2"; break; case "http2-prior-knowledge": config.httpVersion = "2-prior-knowledge"; break; case "http3": config.httpVersion = "3"; break; case "http3-only": config.httpVersion = "3-only"; break; case "tlsv1": config.tlsVersion = "1"; break; case "tlsv1.0": config.tlsVersion = "1.0"; break; case "tlsv1.1": config.tlsVersion = "1.1"; break; case "tlsv1.2": config.tlsVersion = "1.2"; break; case "tlsv1.3": config.tlsVersion = "1.3"; break; case "verbose": case "version": case "trace-time": case "test-event": case "progress-bar": case "progress-meter": case "fail-early": case "styled-output": case "help": case "silent": case "show-error": case "parallel": case "parallel-immediate": case "stdin": // --stdin or - is a curlconverter specific option global_[argName] = toggle; break; case "next": // curl ignores --next if the last url node doesn't have a url if ( toggle && config.url && config.url.length > 0 && config.url.length >= (config["upload-file"] || []).length && config.url.length >= (config.output || []).length ) { config = { authtype: CURLAUTH_BASIC, proxyauthtype: CURLAUTH_BASIC }; global_.configs.push(config); } break; default: config[argName] = toggle; } return config; } export function parseArgs( args: Word[], longOpts: LongOpts = curlLongOpts, shortenedLongOpts: LongOpts = curlLongOptsShortened, shortOpts: ShortOpts = curlShortOpts, supportedOpts?: Set<string>, warnings: Warnings = [], ): [GlobalConfig, [string, string][]] { let config: OperationConfig = { authtype: CURLAUTH_BASIC, proxyauthtype: CURLAUTH_BASIC, }; const global_: GlobalConfig = { configs: [config], warnings }; const seen: [string, string][] = []; for (let i = 1, stillflags = true; i < args.length; i++) { const arg: Word = args[i]; if (stillflags && arg.startsWith("-")) { if (eq(arg, "--")) { /* This indicates the end of the flags and thus enables the following (URL) argument to start with -. */ stillflags = false; } else if (arg.startsWith("--")) { const shellToken = firstShellToken(arg); if (shellToken) { // TODO: if there's any text after the "--" or after the variable // we could narrow it down. throw new CCError( "this " + shellToken.type + " could " + (shellToken.type === "command" ? "return" : "be") + " anything\n" + underlineNode(shellToken.syntaxNode), ); } const argStr = arg.toString(); const lookup = argStr.slice(2); let longArg = shortenedLongOpts[lookup]; if (typeof longArg === "undefined") { longArg = longOpts[lookup]; } if (longArg === null) { throw new CCError("option " + argStr + ": is ambiguous"); } if (typeof longArg === "undefined") { // TODO: extract a list of deleted arguments to check here throw new CCError("option " + argStr + ": is unknown"); } if (longArg.type === "string") { i++; if (i >= args.length) { throw new CCError("option " + argStr + ": requires parameter"); } pushArgValue(global_, config, longArg.name, args[i]); } else { config = setArgValue( global_, config, longArg.name, toBoolean(argStr.slice(2)), ); // TODO: all shortened args work correctly? } checkSupported(global_, argStr, longArg, supportedOpts); seen.push([longArg.name, argStr]); } else { // Short option. These can look like // -X POST -> {request: 'POST'} // or // -XPOST -> {request: 'POST'} // or multiple options // -ABCX POST -> {A: true, B: true, C: true, request: 'POST'} // or multiple options and a value for the last one // -ABCXPOST -> {A: true, B: true, C: true, request: 'POST'} // "-" passed to curl as an argument raises an error, // curlconverter's command line uses it to read from stdin if (arg.length === 1) { if (Object.prototype.hasOwnProperty.call(shortOpts, "")) { const shortFor: string = shortOpts[""]; const longArg = longOpts[shortFor]; if (longArg === null) { throw new CCError("option -: is unknown"); } config = setArgValue( global_, config, longArg.name, toBoolean(shortFor), ); seen.push([longArg.name, "-"]); } else { throw new CCError("option -: is unknown"); } } for (let j = 1; j < arg.length; j++) { const jthChar = arg.get(j); if (typeof jthChar !== "string") { // A bash variable in the middle of a short option throw new CCError( "this " + jthChar.type + " could " + (jthChar.type === "command" ? "return" : "be") + " anything\n" + underlineNode(jthChar.syntaxNode), ); } if (!has(shortOpts, jthChar)) { if (has(changedShortOpts, jthChar)) { throw new CCError( "option " + arg + ": " + changedShortOpts[jthChar], ); } // TODO: there are a few deleted short options we could report throw new CCError("option " + arg + ":