Supply an XPath selector instead of CSS
25 | Commands:
26 | generate-completion Generate bash/zsh completion script for hq.
27 |
28 | ```
29 |
30 | # Installation
31 |
32 | ## Homebrew
33 |
34 | ```
35 | > brew tap ludovicianul/tap
36 | > brew install ludovicianul/tap/hq
37 | ```
38 |
39 | ## Manual
40 |
41 | `hq` is compiled to native code using GraalVM. Check
42 | the [release page](https://github.com/ludovicianul/hq/releases/) for binaries (Linux,
43 | MacOS, uberjar).
44 |
45 | After download, you can make `hq` globally available:
46 |
47 | ```bash
48 | sudo cp hq-macos /usr/local/bin/hq
49 | ```
50 |
51 | The uberjar can be run using `java -jar hq`. Requires Java 11+.
52 |
53 | # Autocomplete
54 | Run the following commands to get autocomplete:
55 |
56 | ```bash
57 | hq generate-completion >> hq_autocomplete
58 |
59 | source hq_autocomplete
60 | ```
61 |
62 | # HTML Sanitizing
63 | `hq` can sanitize html output. Supported modes are: `NONE, BASIC, SIMPLE_TEXT, BASIC_WITH_IMAGES, RELAXED`.
64 |
65 | This is how sanitization works:
66 |
67 | | Policy | Details |
68 | | ------- | ------- |
69 | | `NONE` | Allows only text nodes: all HTML will be stripped. |
70 | | `BASIC` | Allows a fuller range of text nodes: `a, b, blockquote, br, cite, code, dd, dl, dt, em, i, li, ol, p, pre, q, small, span, strike, strong, sub, sup, u, ul`, and appropriate attributes. Does not allow images.|
71 | | `SIMPLE_TEXT` | Allows only simple text formatting: `b, em, i, strong, u`. All other HTML (tags and attributes) will be removed.|
72 | | `BASIC_WITH_IMAGES` | Allows the same text tags as `BASIC`, and also allows `img` tags, with appropriate attributes, with `src` pointing to `http` or `https`.
73 | | `RELAXES` | Allows a full range of text and structural body HTML: `a, b, blockquote, br, caption, cite, code, col, colgroup, dd, div, dl, dt, em, h1, h2, h3, h4, h5, h6, i, img, li, ol, p, pre, q, small, span, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, u, ul`.|
74 |
75 | # Examples
76 |
77 | Get the `div` with id `mainLeaderboard`:
78 |
79 | ```
80 | ➜ curl -s https://www.w3schools.com/cssref/css_selectors.php | hq "#main > p:nth-child(6)" -t
81 |
82 | In CSS, selectors are patterns used to select the element(s) you want to style.
83 |
84 | ```
85 |
86 | Get the text inside an article:
87 |
88 | ```
89 | ➜ curl -s https://ludovicianul.github.io/2021/07/16/unicode_language_version/ | hq '.post' -t
90 |
91 | Make sure you know which Unicode version is supported by your programming language version 16 Jul 2021 While enhancing CATS I recently added a feature to send requests that include
92 | single and multi code point emojis. This is a single code point emoji: 🥶, which can be represented in Java as the \uD83E\uDD76 string. The test case is simple: inject emojis within
93 | strings and expect that the REST endpoint will sanitize the input and remove them entirely (I appreciate this might not be a valid case for all APIs, this is why the behaviour is
94 | configurable in CATS, but not the focus of this article). I usually recommend that any REST endpoint should sanitize input before validating it and remove special characters.
95 | A typical regex for this would be [\p{C}\p{Z}\p{So}]+ (although you should enhance it to allow spaces between words), which means: p{C} - match Unicode invisible Control
96 | Chars (\u000D - carriage return for example) ...
97 | ...
98 | ```
99 |
100 | Sanitize the html according to the [specified policy](#html-sanitizing):
101 | ```
102 | ➜ curl -s https://ludovicianul.github.io/2021/07/16/unicode_language_version/ | hq html -s=BASIC -p
103 |
104 |
105 |
106 |
107 | m's blog
108 | practical thoughts about software engineering
109 | Home
110 | About
111 | GitHub
112 | © 2021. All rights reserved.
113 | Make sure you know which Unicode version is supported by your programming language version
114 | 16 Jul 2021
115 |
116 | ...
117 |
118 |
119 | ```
120 |
121 | Get all `href` attributes from a given page:
122 |
123 | ```shell
124 | ➜ curl -s https://ludovicianul.github.io | hq "*" -a "href"
125 | http://gmpg.org/xfn/11
126 | https://ludovicianul.github.io/public/css/poole.css
127 | https://ludovicianul.github.io/public/css/syntax.css
128 | https://ludovicianul.github.io/public/css/hyde.css
129 | https://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700|Abril+Fatface
130 | https://ludovicianul.github.io/public/apple-touch-icon-144-precomposed.png
131 | https://ludovicianul.github.io/public/favicon.ico
132 | /atom.xml
133 | https://ludovicianul.github.io/
134 | https://ludovicianul.github.io/
135 | /about/
136 | ...
137 | ```
138 |
139 | # Resources
140 |
141 | - [Universal selector in CSS](https://www.scaler.com/topics/universal-selector-in-css/)
142 | - [HTML elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element)
143 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ludovicianul/HtmlCommand.java:
--------------------------------------------------------------------------------
1 | package io.github.ludovicianul;
2 |
3 | import io.quarkus.runtime.QuarkusApplication;
4 | import io.quarkus.runtime.annotations.QuarkusMain;
5 | import jakarta.inject.Inject;
6 | import java.io.BufferedReader;
7 | import java.io.FileReader;
8 | import java.io.IOException;
9 | import java.io.InputStreamReader;
10 | import java.io.Reader;
11 | import java.nio.file.Files;
12 | import java.nio.file.Path;
13 | import java.util.List;
14 | import java.util.stream.Collectors;
15 | import org.jsoup.Jsoup;
16 | import org.jsoup.nodes.Document;
17 | import org.jsoup.parser.Parser;
18 | import org.jsoup.safety.Cleaner;
19 | import org.jsoup.select.Elements;
20 | import picocli.AutoComplete;
21 | import picocli.CommandLine;
22 | import picocli.CommandLine.Command;
23 | import picocli.CommandLine.Parameters;
24 | import us.codecraft.xsoup.Xsoup;
25 |
26 | @QuarkusMain
27 | @Command(
28 | name = "hq",
29 | mixinStandardHelpOptions = true,
30 | version = "hq 1.3.1",
31 | usageHelpWidth = 100,
32 | header = "hq - command line HTML and XML elements finder and sanitizer; version 1.3.1\n",
33 | subcommands = AutoComplete.GenerateCompletion.class)
34 | public class HtmlCommand implements Runnable, QuarkusApplication {
35 |
36 | @Inject
37 | CommandLine.IFactory factory;
38 |
39 | @Parameters(
40 | index = "0",
41 | paramLabel = "",
42 | defaultValue = "*",
43 | description = "The CSS selector")
44 | String selector;
45 |
46 | @CommandLine.Option(
47 | names = {"-a", "--attribute"},
48 | paramLabel = "",
49 | description = "Return only this attribute from the selected HTML elements")
50 | String attribute;
51 |
52 | @CommandLine.Option(
53 | names = {"-f", "--file"},
54 | paramLabel = "",
55 | description = "The HTML input file. If not supplied it will default to stdin")
56 | String file;
57 |
58 | @CommandLine.Option(
59 | names = {"-o", "--output"},
60 | paramLabel = "",
61 | description = "The output file. If not supplied it will default to stdout")
62 | String output;
63 |
64 | @CommandLine.Option(
65 | names = {"-x", "--xpath"},
66 | paramLabel = "",
67 | description = "Supply an XPath selector instead of CSS")
68 | String xpath;
69 |
70 | @CommandLine.Option(
71 | names = {"-t", "--text"},
72 | description = "Display only the inner text of the selected HTML top element")
73 | boolean text;
74 |
75 | @CommandLine.Option(
76 | names = {"-p", "--pretty"},
77 | description = "Force pretty printing the output")
78 | boolean prettyPrint;
79 |
80 | @CommandLine.Option(
81 | names = {"-s", "--sanitize"},
82 | paramLabel = "",
83 | description = "Sanitize the html input according to the given policy")
84 | Sanitize sanitize;
85 |
86 | @CommandLine.Option(
87 | names = {"-r", "--remove"},
88 | paramLabel = "",
89 | description = "Remove nodes matching given selector")
90 | String remove;
91 |
92 | @CommandLine.Option(
93 | names = {"-D", "--debug"},
94 | description = "Debug")
95 | boolean debug;
96 |
97 | @Override
98 | public void run() {
99 | try {
100 | String html;
101 | if (file == null) {
102 | html = this.parseSystemIn();
103 | } else {
104 | html = this.parseFile();
105 | }
106 | if (debug) {
107 | System.out.println("Received file: \n" + html);
108 | }
109 | if (html != null && !html.trim().isBlank()) {
110 | this.processHtml(html);
111 | }
112 | } catch (Exception e) {
113 | throw new RuntimeException(e);
114 | }
115 | }
116 |
117 | private void processHtml(String html) throws IOException {
118 | Elements elements;
119 | Parser parser = isXML(html) ? Parser.xmlParser() : Parser.htmlParser();
120 | Document document = Jsoup.parse(html, parser);
121 |
122 | if (sanitize != null) {
123 | document = this.sanitize(document);
124 | }
125 |
126 | this.setPrettyPrint(document);
127 | elements = this.evaluateSelector(document);
128 | this.removeIfNeeded(elements);
129 |
130 | this.printResult(elements);
131 | }
132 |
133 | private boolean isXML(String text) {
134 | return text.startsWith(" elementsWithAttribute = elements.eachAttr(attribute);
169 | this.writeToOutput(
170 | elementsWithAttribute.stream().collect(Collectors.joining(System.lineSeparator())));
171 | } else if (text) {
172 | this.writeToOutput(elements.text());
173 | } else {
174 | this.writeToOutput(elements.outerHtml());
175 | }
176 | }
177 |
178 | private void writeToOutput(String string) throws IOException {
179 | if (output == null) {
180 | System.out.println(string);
181 | } else {
182 | Path path = Path.of(output);
183 | Files.writeString(path, string);
184 | }
185 | }
186 |
187 | private String parseInput(Reader reader) throws IOException {
188 | try (BufferedReader in = new BufferedReader(reader)) {
189 | return in.lines().collect(Collectors.joining(System.lineSeparator()));
190 | }
191 | }
192 |
193 | private String parseFile() throws IOException {
194 | return this.parseInput(new FileReader(file));
195 | }
196 |
197 | private String parseSystemIn() throws IOException {
198 | return this.parseInput(new InputStreamReader(System.in));
199 | }
200 |
201 | @Override
202 | public int run(String... args) {
203 | return new CommandLine(this, factory).execute(args);
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM https://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
124 |
125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
162 | if ERRORLEVEL 1 goto error
163 | goto end
164 |
165 | :error
166 | set ERROR_CODE=1
167 |
168 | :end
169 | @endlocal & set ERROR_CODE=%ERROR_CODE%
170 |
171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
175 | :skipRcPost
176 |
177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
179 |
180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
181 |
182 | exit /B %ERROR_CODE%
183 |
--------------------------------------------------------------------------------
/hq_autocomplete:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # hq Bash Completion
4 | # =======================
5 | #
6 | # Bash completion support for the `hq` command,
7 | # generated by [picocli](https://picocli.info/) version 4.7.1.
8 | #
9 | # Installation
10 | # ------------
11 | #
12 | # 1. Source all completion scripts in your .bash_profile
13 | #
14 | # cd $YOUR_APP_HOME/bin
15 | # for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
16 | #
17 | # 2. Open a new bash console, and type `hq [TAB][TAB]`
18 | #
19 | # 1a. Alternatively, if you have [bash-completion](https://github.com/scop/bash-completion) installed:
20 | # Place this file in a `bash-completion.d` folder:
21 | #
22 | # * /etc/bash-completion.d
23 | # * /usr/local/etc/bash-completion.d
24 | # * ~/bash-completion.d
25 | #
26 | # Documentation
27 | # -------------
28 | # The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
29 | # 'hq (..)'. By reading entered command line parameters,
30 | # it determines possible bash completions and writes them to the COMPREPLY variable.
31 | # Bash then completes the user input if only one entry is listed in the variable or
32 | # shows the options if more than one is listed in COMPREPLY.
33 | #
34 | # References
35 | # ----------
36 | # [1] http://stackoverflow.com/a/12495480/1440785
37 | # [2] http://tiswww.case.edu/php/chet/bash/FAQ
38 | # [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
39 | # [4] http://zsh.sourceforge.net/Doc/Release/Options.html#index-COMPLETE_005fALIASES
40 | # [5] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
41 | # [6] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
42 | # [7] https://stackoverflow.com/questions/3249432/can-a-bash-tab-completion-script-be-used-in-zsh/27853970#27853970
43 | #
44 |
45 | if [ -n "$BASH_VERSION" ]; then
46 | # Enable programmable completion facilities when using bash (see [3])
47 | shopt -s progcomp
48 | elif [ -n "$ZSH_VERSION" ]; then
49 | # Make alias a distinct command for completion purposes when using zsh (see [4])
50 | setopt COMPLETE_ALIASES
51 | alias compopt=complete
52 |
53 | # Enable bash completion in zsh (see [7])
54 | # Only initialize completions module once to avoid unregistering existing completions.
55 | if ! type compdef > /dev/null; then
56 | autoload -U +X compinit && compinit
57 | fi
58 | autoload -U +X bashcompinit && bashcompinit
59 | fi
60 |
61 | # CompWordsContainsArray takes an array and then checks
62 | # if all elements of this array are in the global COMP_WORDS array.
63 | #
64 | # Returns zero (no error) if all elements of the array are in the COMP_WORDS array,
65 | # otherwise returns 1 (error).
66 | function CompWordsContainsArray() {
67 | declare -a localArray
68 | localArray=("$@")
69 | local findme
70 | for findme in "${localArray[@]}"; do
71 | if ElementNotInCompWords "$findme"; then return 1; fi
72 | done
73 | return 0
74 | }
75 | function ElementNotInCompWords() {
76 | local findme="$1"
77 | local element
78 | for element in "${COMP_WORDS[@]}"; do
79 | if [[ "$findme" = "$element" ]]; then return 1; fi
80 | done
81 | return 0
82 | }
83 |
84 | # The `currentPositionalIndex` function calculates the index of the current positional parameter.
85 | #
86 | # currentPositionalIndex takes three parameters:
87 | # the command name,
88 | # a space-separated string with the names of options that take a parameter, and
89 | # a space-separated string with the names of boolean options (that don't take any params).
90 | # When done, this function echos the current positional index to std_out.
91 | #
92 | # Example usage:
93 | # local currIndex=$(currentPositionalIndex "mysubcommand" "$ARG_OPTS" "$FLAG_OPTS")
94 | function currentPositionalIndex() {
95 | local commandName="$1"
96 | local optionsWithArgs="$2"
97 | local booleanOptions="$3"
98 | local previousWord
99 | local result=0
100 |
101 | for i in $(seq $((COMP_CWORD - 1)) -1 0); do
102 | previousWord=${COMP_WORDS[i]}
103 | if [ "${previousWord}" = "$commandName" ]; then
104 | break
105 | fi
106 | if [[ "${optionsWithArgs}" =~ ${previousWord} ]]; then
107 | ((result-=2)) # Arg option and its value not counted as positional param
108 | elif [[ "${booleanOptions}" =~ ${previousWord} ]]; then
109 | ((result-=1)) # Flag option itself not counted as positional param
110 | fi
111 | ((result++))
112 | done
113 | echo "$result"
114 | }
115 |
116 | # compReplyArray generates a list of completion suggestions based on an array, ensuring all values are properly escaped.
117 | #
118 | # compReplyArray takes a single parameter: the array of options to be displayed
119 | #
120 | # The output is echoed to std_out, one option per line.
121 | #
122 | # Example usage:
123 | # local options=("foo", "bar", "baz")
124 | # local IFS=$'\n'
125 | # COMPREPLY=($(compReplyArray "${options[@]}"))
126 | function compReplyArray() {
127 | declare -a options
128 | options=("$@")
129 | local curr_word=${COMP_WORDS[COMP_CWORD]}
130 | local i
131 | local quoted
132 | local optionList=()
133 |
134 | for (( i=0; i<${#options[@]}; i++ )); do
135 | # Double escape, since we want escaped values, but compgen -W expands the argument
136 | printf -v quoted %q "${options[i]}"
137 | quoted=\'${quoted//\'/\'\\\'\'}\'
138 |
139 | optionList[i]=$quoted
140 | done
141 |
142 | # We also have to add another round of escaping to $curr_word.
143 | curr_word=${curr_word//\\/\\\\}
144 | curr_word=${curr_word//\'/\\\'}
145 |
146 | # Actually generate completions.
147 | local IFS=$'\n'
148 | echo -e "$(compgen -W "${optionList[*]}" -- "$curr_word")"
149 | }
150 |
151 | # Bash completion entry point function.
152 | # _complete_hq finds which commands and subcommands have been specified
153 | # on the command line and delegates to the appropriate function
154 | # to generate possible options and subcommands for the last specified subcommand.
155 | function _complete_hq() {
156 | # Edge case: if command line has no space after subcommand, then don't assume this subcommand is selected (remkop/picocli#1468).
157 | if [ "${COMP_LINE}" = "${COMP_WORDS[0]} generate-completion" ]; then _picocli_hq; return $?; fi
158 |
159 | # Find the longest sequence of subcommands and call the bash function for that subcommand.
160 | local cmds0=(generate-completion)
161 |
162 | if CompWordsContainsArray "${cmds0[@]}"; then _picocli_hq_generatecompletion; return $?; fi
163 |
164 | # No subcommands were specified; generate completions for the top-level command.
165 | _picocli_hq; return $?;
166 | }
167 |
168 | # Generates completions for the options and subcommands of the `hq` command.
169 | function _picocli_hq() {
170 | # Get completion data
171 | local curr_word=${COMP_WORDS[COMP_CWORD]}
172 | local prev_word=${COMP_WORDS[COMP_CWORD-1]}
173 |
174 | local commands="generate-completion"
175 | local flag_opts="-t --text -p --pretty -D --debug -h --help -V --version"
176 | local arg_opts="-a --attribute -f --file -o --output -x --xpath -s --sanitize -r --remove"
177 | local POLICY_option_args=("NONE" "BASIC" "SIMPLE_TEXT" "BASIC_WITH_IMAGES" "RELAXED") # --sanitize values
178 |
179 | type compopt &>/dev/null && compopt +o default
180 |
181 | case ${prev_word} in
182 | -a|--attribute)
183 | return
184 | ;;
185 | -f|--file)
186 | return
187 | ;;
188 | -o|--output)
189 | return
190 | ;;
191 | -x|--xpath)
192 | return
193 | ;;
194 | -s|--sanitize)
195 | local IFS=$'\n'
196 | COMPREPLY=( $( compReplyArray "${POLICY_option_args[@]}" ) )
197 | return $?
198 | ;;
199 | -r|--remove)
200 | return
201 | ;;
202 | esac
203 |
204 | if [[ "${curr_word}" == -* ]]; then
205 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
206 | else
207 | local positionals=""
208 | local IFS=$'\n'
209 | COMPREPLY=( $(compgen -W "${commands// /$'\n'}${IFS}${positionals}" -- "${curr_word}") )
210 | fi
211 | }
212 |
213 | # Generates completions for the options and subcommands of the `generate-completion` subcommand.
214 | function _picocli_hq_generatecompletion() {
215 | # Get completion data
216 | local curr_word=${COMP_WORDS[COMP_CWORD]}
217 |
218 | local commands=""
219 | local flag_opts="-h --help -V --version"
220 | local arg_opts=""
221 |
222 | if [[ "${curr_word}" == -* ]]; then
223 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
224 | else
225 | local positionals=""
226 | local IFS=$'\n'
227 | COMPREPLY=( $(compgen -W "${commands// /$'\n'}${IFS}${positionals}" -- "${curr_word}") )
228 | fi
229 | }
230 |
231 | # Define a completion specification (a compspec) for the
232 | # `hq`, `hq.sh`, and `hq.bash` commands.
233 | # Uses the bash `complete` builtin (see [6]) to specify that shell function
234 | # `_complete_hq` is responsible for generating possible completions for the
235 | # current word on the command line.
236 | # The `-o default` option means that if the function generated no matches, the
237 | # default Bash completions and the Readline default filename completions are performed.
238 | complete -F _complete_hq -o default hq hq.sh hq.bash
239 |
240 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # https://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Mingw, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | fi
118 |
119 | if [ -z "$JAVA_HOME" ]; then
120 | javaExecutable="`which javac`"
121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
122 | # readlink(1) is not available as standard on Solaris 10.
123 | readLink=`which readlink`
124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
125 | if $darwin ; then
126 | javaHome="`dirname \"$javaExecutable\"`"
127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
128 | else
129 | javaExecutable="`readlink -f \"$javaExecutable\"`"
130 | fi
131 | javaHome="`dirname \"$javaExecutable\"`"
132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
133 | JAVA_HOME="$javaHome"
134 | export JAVA_HOME
135 | fi
136 | fi
137 | fi
138 |
139 | if [ -z "$JAVACMD" ] ; then
140 | if [ -n "$JAVA_HOME" ] ; then
141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
142 | # IBM's JDK on AIX uses strange locations for the executables
143 | JAVACMD="$JAVA_HOME/jre/sh/java"
144 | else
145 | JAVACMD="$JAVA_HOME/bin/java"
146 | fi
147 | else
148 | JAVACMD="`which java`"
149 | fi
150 | fi
151 |
152 | if [ ! -x "$JAVACMD" ] ; then
153 | echo "Error: JAVA_HOME is not defined correctly." >&2
154 | echo " We cannot execute $JAVACMD" >&2
155 | exit 1
156 | fi
157 |
158 | if [ -z "$JAVA_HOME" ] ; then
159 | echo "Warning: JAVA_HOME environment variable is not set."
160 | fi
161 |
162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
163 |
164 | # traverses directory structure from process work directory to filesystem root
165 | # first directory with .mvn subdirectory is considered project base directory
166 | find_maven_basedir() {
167 |
168 | if [ -z "$1" ]
169 | then
170 | echo "Path not specified to find_maven_basedir"
171 | return 1
172 | fi
173 |
174 | basedir="$1"
175 | wdir="$1"
176 | while [ "$wdir" != '/' ] ; do
177 | if [ -d "$wdir"/.mvn ] ; then
178 | basedir=$wdir
179 | break
180 | fi
181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
182 | if [ -d "${wdir}" ]; then
183 | wdir=`cd "$wdir/.."; pwd`
184 | fi
185 | # end of workaround
186 | done
187 | echo "${basedir}"
188 | }
189 |
190 | # concatenates all lines of a file
191 | concat_lines() {
192 | if [ -f "$1" ]; then
193 | echo "$(tr -s '\n' ' ' < "$1")"
194 | fi
195 | }
196 |
197 | BASE_DIR=`find_maven_basedir "$(pwd)"`
198 | if [ -z "$BASE_DIR" ]; then
199 | exit 1;
200 | fi
201 |
202 | ##########################################################################################
203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
204 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
205 | ##########################################################################################
206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
207 | if [ "$MVNW_VERBOSE" = true ]; then
208 | echo "Found .mvn/wrapper/maven-wrapper.jar"
209 | fi
210 | else
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
213 | fi
214 | if [ -n "$MVNW_REPOURL" ]; then
215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
216 | else
217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
218 | fi
219 | while IFS="=" read key value; do
220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
221 | esac
222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
223 | if [ "$MVNW_VERBOSE" = true ]; then
224 | echo "Downloading from: $jarUrl"
225 | fi
226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
227 | if $cygwin; then
228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
229 | fi
230 |
231 | if command -v wget > /dev/null; then
232 | if [ "$MVNW_VERBOSE" = true ]; then
233 | echo "Found wget ... using wget"
234 | fi
235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
236 | wget "$jarUrl" -O "$wrapperJarPath"
237 | else
238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
239 | fi
240 | elif command -v curl > /dev/null; then
241 | if [ "$MVNW_VERBOSE" = true ]; then
242 | echo "Found curl ... using curl"
243 | fi
244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
245 | curl -o "$wrapperJarPath" "$jarUrl" -f
246 | else
247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
248 | fi
249 |
250 | else
251 | if [ "$MVNW_VERBOSE" = true ]; then
252 | echo "Falling back to using Java to download"
253 | fi
254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
255 | # For Cygwin, switch paths to Windows format before running javac
256 | if $cygwin; then
257 | javaClass=`cygpath --path --windows "$javaClass"`
258 | fi
259 | if [ -e "$javaClass" ]; then
260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
261 | if [ "$MVNW_VERBOSE" = true ]; then
262 | echo " - Compiling MavenWrapperDownloader.java ..."
263 | fi
264 | # Compiling the Java class
265 | ("$JAVA_HOME/bin/javac" "$javaClass")
266 | fi
267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
268 | # Running the downloader
269 | if [ "$MVNW_VERBOSE" = true ]; then
270 | echo " - Running MavenWrapperDownloader.java ..."
271 | fi
272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
273 | fi
274 | fi
275 | fi
276 | fi
277 | ##########################################################################################
278 | # End of extension
279 | ##########################################################################################
280 |
281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
282 | if [ "$MVNW_VERBOSE" = true ]; then
283 | echo $MAVEN_PROJECTBASEDIR
284 | fi
285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
286 |
287 | # For Cygwin, switch paths to Windows format before running java
288 | if $cygwin; then
289 | [ -n "$M2_HOME" ] &&
290 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
291 | [ -n "$JAVA_HOME" ] &&
292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
293 | [ -n "$CLASSPATH" ] &&
294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
295 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
297 | fi
298 |
299 | # Provide a "standardized" way to retrieve the CLI args that will
300 | # work with both Windows and non-Windows executions.
301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
302 | export MAVEN_CMD_LINE_ARGS
303 |
304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
305 |
306 | exec "$JAVACMD" \
307 | $MAVEN_OPTS \
308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
311 |
--------------------------------------------------------------------------------