Skip to content

Commit 5c72ec3

Browse files
idodeclareVladimir Kotal
authored andcommitted
Format argv logging to aid copy/paste execution
Also, move determination of operating system name to PlatformUtils from web/Util.
1 parent 4cb9b46 commit 5c72ec3

File tree

9 files changed

+254
-105
lines changed

9 files changed

+254
-105
lines changed

opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Ctags.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
import org.opengrok.indexer.configuration.RuntimeEnvironment;
4848
import org.opengrok.indexer.index.IndexerParallelizer;
4949
import org.opengrok.indexer.logger.LoggerFactory;
50+
import org.opengrok.indexer.util.Executor;
5051
import org.opengrok.indexer.util.IOUtils;
52+
import org.opengrok.indexer.util.PlatformUtils;
5153
import org.opengrok.indexer.util.SourceSplitter;
5254

5355
/**
@@ -214,11 +216,7 @@ private void initialize() {
214216
}
215217

216218
private void run() throws IOException {
217-
StringBuilder sb = new StringBuilder();
218-
for (String s : command) {
219-
sb.append(s).append(" ");
220-
}
221-
String commandStr = sb.toString();
219+
String commandStr = Executor.escapeForShell(command, false, PlatformUtils.isWindows());
222220
LOGGER.log(Level.FINE, "Executing ctags command [{0}]", commandStr);
223221

224222
ProcessBuilder processBuilder = new ProcessBuilder(command);

opengrok-indexer/src/main/java/org/opengrok/indexer/index/Indexer.java

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import org.opengrok.indexer.util.CtagsUtil;
7070
import org.opengrok.indexer.util.Executor;
7171
import org.opengrok.indexer.util.OptionParser;
72+
import org.opengrok.indexer.util.PlatformUtils;
7273
import org.opengrok.indexer.util.Statistics;
7374

7475
/**
@@ -1121,29 +1122,8 @@ private static void pauseToAwaitProfiler() {
11211122
}
11221123

11231124
private static String getCtagsCommand() {
1124-
StringBuilder result = new StringBuilder();
11251125
Ctags ctags = CtagsUtil.newInstance(env);
1126-
List<String> argv = ctags.getArgv();
1127-
for (int i = 0; i < argv.size(); ++i) {
1128-
if (i > 0) {
1129-
result.append("\t");
1130-
}
1131-
String arg = argv.get(i);
1132-
result.append(maybeEscapeForSh(arg));
1133-
if (i + 1 < argv.size()) {
1134-
result.append(" \\");
1135-
}
1136-
result.append("\n");
1137-
}
1138-
return result.toString();
1139-
}
1140-
1141-
private static String maybeEscapeForSh(String value) {
1142-
if (!value.matches(".*[^-:.+=a-zA-Z0-9_].*")) {
1143-
return value;
1144-
}
1145-
return "$'" + value.replace("\\", "\\\\").replace("'", "\\'").replace("\n", "\\n").
1146-
replace("\r", "\\r").replace("\t", "\\t") + "'";
1126+
return Executor.escapeForShell(ctags.getArgv(), true, PlatformUtils.isWindows());
11471127
}
11481128

11491129
private Indexer() {

opengrok-indexer/src/main/java/org/opengrok/indexer/util/Executor.java

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
/*
2121
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
22+
* Portions Copyright (c) 2019, Chris Fraire <[email protected]>.
2223
*/
2324

2425
package org.opengrok.indexer.util;
@@ -39,6 +40,9 @@
3940
import java.util.TimerTask;
4041
import java.util.logging.Level;
4142
import java.util.logging.Logger;
43+
import java.util.regex.Matcher;
44+
import java.util.regex.Pattern;
45+
4246
import org.opengrok.indexer.configuration.RuntimeEnvironment;
4347
import org.opengrok.indexer.logger.LoggerFactory;
4448

@@ -51,6 +55,10 @@ public class Executor {
5155

5256
private static final Logger LOGGER = LoggerFactory.getLogger(Executor.class);
5357

58+
private static final Pattern ARG_WIN_QUOTING = Pattern.compile("[^-:.+=%a-zA-Z0-9_/\\\\]");
59+
private static final Pattern ARG_UNIX_QUOTING = Pattern.compile("[^-:.+=%a-zA-Z0-9_/]");
60+
private static final Pattern ARG_GNU_STYLE_EQ = Pattern.compile("^--[-.a-zA-Z0-9_]+=");
61+
5462
private List<String> cmdList;
5563
private File workingDirectory;
5664
private byte[] stdout;
@@ -147,7 +155,8 @@ public int exec(boolean reportExceptions) {
147155
public int exec(final boolean reportExceptions, StreamHandler handler) {
148156
int ret = -1;
149157
ProcessBuilder processBuilder = new ProcessBuilder(cmdList);
150-
final String cmd_str = processBuilder.command().toString();
158+
final String cmd_str = escapeForShell(processBuilder.command(), false,
159+
PlatformUtils.isWindows());
151160
final String dir_str;
152161
Timer timer = null; // timer for timing out the process
153162

@@ -172,7 +181,7 @@ public int exec(final boolean reportExceptions, StreamHandler handler) {
172181
env_str = " with environment: " + env_map.toString();
173182
}
174183
LOGGER.log(Level.FINE,
175-
"Executing command {0} in directory {1}{2}",
184+
"Executing command [{0}] in directory {1}{2}",
176185
new Object[] {cmd_str, dir_str, env_str});
177186

178187
Process process = null;
@@ -191,7 +200,7 @@ public void run() {
191200
} catch (IOException ex) {
192201
if (reportExceptions) {
193202
LOGGER.log(Level.SEVERE,
194-
"Error while executing command {0} in directory {1}",
203+
"Error while executing command [{0}] in directory {1}",
195204
new Object[] {cmd_str, dir_str});
196205
LOGGER.log(Level.SEVERE, "Error during process pipe listening", ex);
197206
}
@@ -211,7 +220,7 @@ public void run() {
211220
timer.schedule(new TimerTask() {
212221
@Override public void run() {
213222
LOGGER.log(Level.WARNING,
214-
String.format("Terminating process of command %s in directory %s " +
223+
String.format("Terminating process of command [%s] in directory %s " +
215224
"due to timeout %d seconds", cmd_str, dir_str, timeout / 1000));
216225
proc.destroy();
217226
}
@@ -223,7 +232,7 @@ public void run() {
223232
ret = process.waitFor();
224233

225234
LOGGER.log(Level.FINE,
226-
"Finished command {0} in directory {1} with exit code {2}",
235+
"Finished command [{0}] in directory {1} with exit code {2}",
227236
new Object[] {cmd_str, dir_str, ret});
228237

229238
// Wait for the stderr read-out thread to finish the processing and
@@ -260,9 +269,9 @@ public void run() {
260269
if (ret != 0 && reportExceptions) {
261270
int MAX_MSG_SZ = 512; /* limit to avoid flooding the logs */
262271
StringBuilder msg = new StringBuilder("Non-zero exit status ")
263-
.append(ret).append(" from command ")
272+
.append(ret).append(" from command [")
264273
.append(cmd_str)
265-
.append(" in directory ")
274+
.append("] in directory ")
266275
.append(dir_str);
267276
if (stderr != null && stderr.length > 0) {
268277
msg.append(": ");
@@ -397,4 +406,79 @@ public void uncaughtException(Thread t, Throwable e) {
397406
});
398407
}
399408
}
409+
410+
/**
411+
* Build a string from the specified argv list with optional tab-indenting
412+
* and line-continuations if {@code multiline} is {@code true}.
413+
* @param isWindows a value indicating if the platform is Windows so that
414+
* PowerShell escaping is done; else Bourne shell escaping
415+
* is done.
416+
* @return a defined instance
417+
*/
418+
public static String escapeForShell(List<String> argv, boolean multiline, boolean isWindows) {
419+
StringBuilder result = new StringBuilder();
420+
for (int i = 0; i < argv.size(); ++i) {
421+
if (multiline && i > 0) {
422+
result.append("\t");
423+
}
424+
String arg = argv.get(i);
425+
result.append(isWindows ? maybeEscapeForPowerShell(arg) : maybeEscapeForSh(arg));
426+
if (i + 1 < argv.size()) {
427+
if (!multiline) {
428+
result.append(" ");
429+
} else {
430+
result.append(isWindows ? " `" : " \\");
431+
result.append(System.lineSeparator());
432+
}
433+
}
434+
}
435+
return result.toString();
436+
}
437+
438+
private static String maybeEscapeForSh(String value) {
439+
Matcher m = ARG_UNIX_QUOTING.matcher(value);
440+
if (!m.find()) {
441+
return value;
442+
}
443+
m = ARG_GNU_STYLE_EQ.matcher(value);
444+
if (!m.find()) {
445+
return "$'" + escapeForSh(value) + "'";
446+
}
447+
String following = value.substring(m.end());
448+
return m.group() + "$'" + escapeForSh(following) + "'";
449+
}
450+
451+
private static String escapeForSh(String value) {
452+
return value.replace("\\", "\\\\").
453+
replace("'", "\\'").
454+
replace("\n", "\\n").
455+
replace("\r", "\\r").
456+
replace("\f", "\\f").
457+
replace("\u0011", "\\v").
458+
replace("\t", "\\t");
459+
}
460+
461+
private static String maybeEscapeForPowerShell(String value) {
462+
Matcher m = ARG_WIN_QUOTING.matcher(value);
463+
if (!m.find()) {
464+
return value;
465+
}
466+
m = ARG_GNU_STYLE_EQ.matcher(value);
467+
if (!m.find()) {
468+
return "\"" + escapeForPowerShell(value) + "\"";
469+
}
470+
String following = value.substring(m.end());
471+
return m.group() + "\"" + escapeForPowerShell(following) + "\"";
472+
}
473+
474+
private static String escapeForPowerShell(String value) {
475+
return value.replace("`", "``").
476+
replace("\"", "`\"").
477+
replace("$", "`$").
478+
replace("\n", "`n").
479+
replace("\r", "`r").
480+
replace("\f", "`f").
481+
replace("\u0011", "`v").
482+
replace("\t", "`t");
483+
}
400484
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* CDDL HEADER START
3+
*
4+
* The contents of this file are subject to the terms of the
5+
* Common Development and Distribution License (the "License").
6+
* You may not use this file except in compliance with the License.
7+
*
8+
* See LICENSE.txt included in this distribution for the specific
9+
* language governing permissions and limitations under the License.
10+
*
11+
* When distributing Covered Code, include this CDDL HEADER in each
12+
* file and include the License file at LICENSE.txt.
13+
* If applicable, add the following below this CDDL HEADER, with the
14+
* fields enclosed by brackets "[]" replaced with your own identifying
15+
* information: Portions Copyright [yyyy] [name of copyright owner]
16+
*
17+
* CDDL HEADER END
18+
*/
19+
20+
/*
21+
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
22+
* Portions Copyright 2011 Jens Elkner.
23+
* Portions Copyright (c) 2017-2019, Chris Fraire <[email protected]>.
24+
* Portions Copyright (c) 2019, Krystof Tulinger <[email protected]>.
25+
*/
26+
27+
package org.opengrok.indexer.util;
28+
29+
import java.util.Locale;
30+
31+
public class PlatformUtils {
32+
33+
private static String OS;
34+
private static Boolean IS_UNIX;
35+
private static Boolean IS_WINDOWS;
36+
37+
/** Private to enforce static. */
38+
private PlatformUtils() {
39+
}
40+
41+
/**
42+
* Gets a value indicating the operating system name.
43+
* @return the name in lowercase
44+
*/
45+
public static String getOsName() {
46+
if (OS == null) {
47+
OS = System.getProperty("os.name").toLowerCase(Locale.ROOT);
48+
}
49+
return OS;
50+
}
51+
52+
/**
53+
* Gets a value indicating if the operating system name indicates Windows.
54+
* @return {@code true} if Windows
55+
*/
56+
public static boolean isWindows() {
57+
if (IS_WINDOWS == null) {
58+
String osName = getOsName();
59+
IS_WINDOWS = osName.startsWith("windows");
60+
}
61+
return IS_WINDOWS;
62+
}
63+
64+
/**
65+
* Gets a value indicating if the operating system name is Unix-like
66+
* (Solaris, SunOS, Linux, Mac, BSD).
67+
* @return {@code true} if Unix-like
68+
*/
69+
public static boolean isUnix() {
70+
if (IS_UNIX == null) {
71+
String osName = getOsName();
72+
IS_UNIX = osName.startsWith("linux") || osName.startsWith("solaris") ||
73+
osName.contains("bsd") || osName.startsWith("mac") ||
74+
osName.startsWith("sunos");
75+
}
76+
return IS_UNIX;
77+
}
78+
}

opengrok-indexer/src/main/java/org/opengrok/indexer/web/Util.java

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
/*
2121
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
2222
* Portions Copyright 2011 Jens Elkner.
23-
* Portions Copyright (c) 2017-2018, Chris Fraire <[email protected]>.
23+
* Portions Copyright (c) 2017-2019, Chris Fraire <[email protected]>.
2424
* Portions Copyright (c) 2019, Krystof Tulinger <[email protected]>.
2525
*/
2626

@@ -66,6 +66,7 @@
6666
import org.opengrok.indexer.history.HistoryException;
6767
import org.opengrok.indexer.history.HistoryGuru;
6868
import org.opengrok.indexer.logger.LoggerFactory;
69+
import org.opengrok.indexer.util.PlatformUtils;
6970

7071
/**
7172
* Class for useful functions.
@@ -81,8 +82,6 @@ public final class Util {
8182
*/
8283
private static final Pattern NON_ASCII_ALPHA_NUM = Pattern.compile("[^A-Za-z0-9_]");
8384

84-
private static String OS = null;
85-
8685
private static final String anchorLinkStart = "<a href=\"";
8786
private static final String anchorClassStart = "<a class=\"";
8887
private static final String anchorEnd = "</a>";
@@ -832,47 +831,14 @@ public static String uid2url(String uid) {
832831
}
833832

834833
public static String fixPathIfWindows(String path) {
835-
if (Util.isWindows()) {
834+
if (PlatformUtils.isWindows()) {
836835
// Sanitize Windows path delimiters in order not to conflict with Lucene escape character
837836
// and also so the path appears as correctly formed URI in the search results.
838837
return path.replace(File.separatorChar, PATH_SEPARATOR);
839838
}
840839
return path;
841840
}
842841

843-
/**
844-
* Determine the operation system name.
845-
*
846-
* @return the name in lowercase, {@code null} if unknown
847-
*/
848-
public static String getOsName() {
849-
if (OS == null) {
850-
OS = System.getProperty("os.name").toLowerCase(Locale.ROOT);
851-
}
852-
return OS;
853-
}
854-
855-
/**
856-
* Determine if the current platform is Windows.
857-
*
858-
* @return true if windows, false when not windows or we can not determine
859-
*/
860-
public static boolean isWindows() {
861-
String osname = getOsName();
862-
return osname != null ? osname.startsWith("windows") : false;
863-
}
864-
865-
/**
866-
* Determine if the current platform is Unix.
867-
*
868-
* @return true if unix, false when not unix or we can not determine
869-
*/
870-
public static boolean isUnix() {
871-
String osname = getOsName();
872-
return osname != null ? (osname.startsWith("linux") || osname.startsWith("solaris") ||
873-
osname.contains("bsd") || osname.startsWith("mac")) : false;
874-
}
875-
876842
/**
877843
* Write the 'H A D' links. This is used for search results and directory
878844
* listings.

0 commit comments

Comments
 (0)