Skip to content

Commit

Permalink
Add shellArgs for MI, rename serverExec to targetExec, pub import ITr…
Browse files Browse the repository at this point in the history
…ansport from transport.base
  • Loading branch information
dd86k committed Aug 28, 2024
1 parent 5a02aef commit 00a82ba
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 13 deletions.
2 changes: 1 addition & 1 deletion source/adapters/base.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/// License: BSD-3-Clause-Clear
module adapters.base;

import transports.base;
public import transports.base : ITransport;
import core.thread : Thread;
import std.datetime : Duration, dur;
import ddlogger;
Expand Down
1 change: 0 additions & 1 deletion source/adapters/dap.d
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import std.string : chompPrefix;
import std.conv : text;
import std.utf : validate;
import adapters.base;
import transports.base : ITransport;
import utils.json;
import ddlogger;

Expand Down
18 changes: 9 additions & 9 deletions source/adapters/mi.d
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,15 @@
/// License: BSD-3-Clause-Clear
module adapters.mi;

import adapters.base;
import transports.base : ITransport;
import logging;
import config;
import server : serverExec;
import std.conv : to;
import std.format : format;
import std.file : chdir;
import std.array : replace, split;
import std.ascii : isWhite;
import std.array : replace;
import logging;
import config;
import utils.shell : shellArgs;
import adapters.base;
import server : targetExec;

// TODO: Function to separate shell-like arguments (with quotes)

Expand Down Expand Up @@ -133,7 +132,7 @@ class MIAdapter : Adapter
send(format(`&"%s"`~"\n", formatCString( fullrequest )));

// Get arguments
string[] args = fullrequest.split!isWhite;
string[] args = shellArgs( fullrequest );
if (args.length == 0)
{
send(doneMsg);
Expand All @@ -155,6 +154,7 @@ class MIAdapter : Adapter
// - file-exec-and-symbols: set exec and symbols
// - goto: break-insert -t TARGET or exec-jump TARGET

// Command list: gdb/mi/mi-cmds.c
// Filter by recognized requests
AdapterRequest request;
switch (requestCommand) {
Expand All @@ -169,7 +169,7 @@ class MIAdapter : Adapter
}

// If server got exec specified earlier
string exec2 = serverExec();
string exec2 = targetExec();
if (exec2)
{
request.launchOptions.path = exec2;
Expand Down
7 changes: 5 additions & 2 deletions source/server.d
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ private
__gshared string[] execArgs;
}

// Executable path if given to server
string serverExec()
/// Get currently set target executable path.
/// Returns: Path string.
string targetExec()
{
return exec;
}
Expand All @@ -71,6 +72,8 @@ void startServer(Adapter adapter, string[] args) // Handles adapter
exec = args[1];
}

// TODO: startup arguments

// Get requests
logTrace("Listening...");
RequestType debuggerType;
Expand Down
142 changes: 142 additions & 0 deletions source/utils/shell.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/// Utilities for managing a shell environment.
///
/// Authors: dd86k <[email protected]>
/// Copyright: dd86k <[email protected]>
/// License: BSD-3-Clause-Clear
module utils.shell;

import std.ascii : isWhite;

/// Split arguments while accounting for quotes.
///
/// Uses the GC to append to the new array.
/// Params: text = Shell-like input.
/// Returns: Arguments.
/// Throws: Does not explicitly throw any exceptions.
string[] shellArgs(string text)
{
// NOTE: This is mostly for the MI adapter
// Roughly follow gdb/mi/mi-parse.c

// If the input is empty, there is nothing to do
if (text == null || text.length == 0)
return null;

// TODO: Redo function more elegantly
// Could return both array and iterable struct

string[] args; // new argument list
size_t i; // Current character index
size_t start; // start of argument
char stop; // stop character

// Get the first significant character
for (; i < text.length; ++i)
{
switch (text[i]) {
case '\n', '\r', 0:
return args;
case '"', '\'': // Match next '"' or '\''
stop = text[i];
start = i + 1;
do
{
if (++i >= text.length)
{
args ~= text[start..i-1];
return args;
}
}
while (text[i] != stop);
args ~= text[start..i++]; // exclude quote but need to skip it too
continue;
default:
if (isWhite(text[i])) // skip whitespace
{
do
{
if (++i >= text.length)
return args;
}
while (isWhite(text[i]));
}

// consume until whitespace
start = i;
do
{
if (++i >= text.length)
{
args ~= text[start..i];
return args;
}

switch (text[i]) {
case '\n', '\r':
args ~= text[start..i];
return args;
// Quote within text
// This is painful because `--option="test 2"` needs to be turned
// into `--option=test 2` - which is not done yet
case '"', '\'':
stop = text[i];
start = i + 1;
do
{
if (++i >= text.length)
{
args ~= text[start..i];
return args;
}
}
while (text[i] != stop);
args ~= text[start..i];
if (i + 1 >= text.length)
return args;
break;
default:
}
}
while (text[i] > ' ');
args ~= text[start..i];
continue;
}
}

return args;
}
unittest
{
// empty inputs
assert(shellArgs(null) == null);
assert(shellArgs("") == null);
// spacing
assert(shellArgs("hello") == [ "hello" ]);
assert(shellArgs(" hello") == [ "hello" ]);
assert(shellArgs("hello ") == [ "hello" ]);
assert(shellArgs("hello dave") == [ "hello", "dave" ]);
assert(shellArgs("hello dave\n") == [ "hello", "dave" ]);
assert(shellArgs("hello dave\nhello dave") == [ "hello", "dave" ]);
assert(shellArgs("hello\tdave") == [ "hello", "dave" ]);
assert(shellArgs("hello dave") == [ "hello", "dave" ]);
assert(shellArgs("hello dave ") == [ "hello", "dave" ]);
assert(shellArgs(" hello dave ") == [ "hello", "dave" ]);
// quotes
assert(shellArgs(`hello "dave davidson"`) == [ "hello", "dave davidson" ]);
assert(shellArgs(`hello 'dave davidson'`) == [ "hello", "dave davidson" ]);
assert(shellArgs(`hello "test1" "test2"`) == [ "hello", "test1", "test2" ]);
assert(shellArgs(`hello "test1" 'test2'`) == [ "hello", "test1", "test2" ]);
assert(shellArgs(`hello 'test1' "test2"`) == [ "hello", "test1", "test2" ]);
assert(shellArgs(`hello ""`) == [ "hello", "" ]);
assert(shellArgs(`hello ''`) == [ "hello", "" ]);
assert(shellArgs(`hello "" ""`) == [ "hello", "", "" ]);
assert(shellArgs(`hello '' ''`) == [ "hello", "", "" ]);
assert(shellArgs(`hello "a" ''`) == [ "hello", "a", "" ]);
// combination of all the above
assert(shellArgs(`"long/path" -o "dave davidson" 'super nice'`) ==
[ "long/path", "-o", "dave davidson", "super nice" ]);
/* TODO: Include when pattern appears
assert(shellArgs(`"long/path test" --option="dave davidson"`) ==
[ "long/path test", `--option="dave davidson"` ]);
*/
}

0 comments on commit 00a82ba

Please sign in to comment.