Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

kdb-ls-depth: Implement depth parameter for kdb ls #1345

Closed
wants to merge 8 commits into from
61 changes: 58 additions & 3 deletions doc/help/kdb-ls.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,76 @@ This command will list the name of all keys below a given path.
Print version info.
- `-p`, `--profile`=<profile>:
Use a different kdb profile.
- `-m`, `--min-depth`=<min-depth>:
Specify the minimum path depth of the output (0 by default), exclusive
and relative to the name to list.
- `-M`, `--max-depth`=<max-depth>:
Specify the maximum path depth of the output (unlimited by default, 1
to show only the next level), inclusive and relative to the name to list.
- `-v`, `--verbose`:
Explain what is happening.
- `-0`, `--null`:
Use binary 0 termination.
- `-C`, `--color`=[when]:
Print never/auto(default)/always colored output.
- `-d`, `--debug`:
Give debug information.

## EXAMPLES

To list all keys below `user/example`:
`kdb ls user/example`
```sh

# Create the keys we use for the examples
kdb set /sw/elektra/examples/kdb-ls/test val1
kdb set /sw/elektra/examples/kdb-ls/test/foo/bar val2
kdb set /sw/elektra/examples/kdb-ls/test/fizz/buzz fizzbuzz
kdb set /sw/elektra/examples/kdb-ls/tost val3
kdb set /sw/elektra/examples/kdb-ls/tost/level lvl

# list all keys below /sw/elektra/examples/kdb-ls
kdb ls /sw/elektra/examples/kdb-ls
#>user/sw/elektra/examples/kdb-ls/test
#>user/sw/elektra/examples/kdb-ls/test/fizz/buzz
#>user/sw/elektra/examples/kdb-ls/test/foo/bar
#>user/sw/elektra/examples/kdb-ls/tost
#>user/sw/elektra/examples/kdb-ls/tost/level

# list the next level of keys below /sw/elektra/examples/kdb-ls
# note that if the search key ends with a /, it lists the next level
kdb ls /sw/elektra/examples/kdb-ls/ --max-depth=1
#>user/sw/elektra/examples/kdb-ls/test
#>user/sw/elektra/examples/kdb-ls/tost

# list the current level of keys below /sw/elektra/examples/kdb-ls
# note the difference to the previous example
kdb ls /sw/elektra/examples/kdb-ls --max-depth=1
# this yields no output as /sw/elektra/examples/kdb-ls is not a key

# list all keys below /sw/elektra/examples/kdb-ls with are minimum 1 level away from that key
# and maximum 2 levels away
kdb ls /sw/elektra/examples/kdb-ls --min-depth=1 --max-depth=2
#>user/sw/elektra/examples/kdb-ls/tost/level

# list all keys below /sw/elektra/examples/kdb-ls/test
kdb ls /sw/elektra/examples/kdb-ls/test
#>user/sw/elektra/examples/kdb-ls/test
#>user/sw/elektra/examples/kdb-ls/test/fizz/buzz
#>user/sw/elektra/examples/kdb-ls/test/foo/bar

# list all keys under /sw/elektra/examples/kdb-ls in verbose mode
kdb ls /sw/elektra/examples/kdb-ls/ -v
#>size of all keys in mountpoint: 31
#>size of requested keys: 5
#>user/sw/elektra/examples/kdb-ls/test
#>user/sw/elektra/examples/kdb-ls/test/fizz/buzz
#>user/sw/elektra/examples/kdb-ls/test/foo/bar
#>user/sw/elektra/examples/kdb-ls/tost
#>user/sw/elektra/examples/kdb-ls/tost/level

```

## SEE ALSO

- If the user would also like to see the values of the keys below `path` then you should
consider the [kdb-export(1)](kdb-export.md) command.
- [elektra-key-names(7)](elektra-key-names.md) for an explanation of key names.
- [elektra-key-names(7)](elektra-key-names.md) for an explanation of key names.
65 changes: 60 additions & 5 deletions src/tools/kdb/ls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ LsCommand::LsCommand () : kdb (root)

int LsCommand::execute (Cmdline const & cl)
{
if (cl.arguments.size () != 1)
{
throw invalid_argument ("1 argument required");
}
checkArguments (cl);

printWarnings (cerr, root);

Expand All @@ -45,13 +42,71 @@ int LsCommand::execute (Cmdline const & cl)
cout.unsetf (std::ios_base::skipws);
}

cout << part;
printResults (part, getDepth (root), cl);

printWarnings (cerr, root);

return 0;
}

void LsCommand::checkArguments (Cmdline const & cl)
{
if (cl.arguments.size () != 1)
{
throw invalid_argument ("1 argument required");
}
if (cl.maxDepth <= cl.minDepth)
{
throw invalid_argument ("the maximum depth has to be larger than the minimum depth");
}
if (cl.maxDepth < 0)
{
throw invalid_argument ("the maximum depth has to be a positive number");
}
if (cl.minDepth < 0)
{
throw invalid_argument ("the minimum depth has to be a positive number");
}
}

void LsCommand::printResults (KeySet const & part, const int rootDepth, Cmdline const & cl)
{
const int offset = root.getBaseName ().empty () || shallShowNextLevel (cl.arguments[0]) ? 1 : 0;
const int relativeMinDepth = rootDepth + cl.minDepth + offset;
const int relativeMaxDepth = std::max (cl.maxDepth, rootDepth + cl.maxDepth + offset);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this line may cause an integer overflow:

../src/tools/kdb/ls.cpp:76:64: runtime error: signed integer overflow: 1 + 2147483647 cannot be represented in type 'int'
SUMMARY: AddressSanitizer: undefined-behavior ../src/tools/kdb/ls.cpp:76:64

.

if (cl.debug)
{
cout << "The root depth is " << rootDepth << ", the relative minimum depth is " << relativeMinDepth
<< " and the relative maximum depth is " << relativeMaxDepth << endl;
}

for (const auto & it : part)
{
const int depth = getDepth (it);
if ((depth >= relativeMinDepth && depth < relativeMaxDepth) || cl.debug)
{
cout << it;
if (cl.debug)
{
cout << " " << depth;
}
cout << endl;
}
}
}

int LsCommand::getDepth (Key const & key)
{
return std::distance (key.begin (), key.end ());
}

bool LsCommand::shallShowNextLevel (const string argument)
{
auto it = argument.rbegin ();
// If the argument ends in / its an indicator to complete the next level (like done by shells), but not if its escaped
return it != argument.rend () && (*it) == '/' && ((++it) == argument.rend () || (*it) != '\\');
}

LsCommand::~LsCommand ()
{
}
8 changes: 7 additions & 1 deletion src/tools/kdb/ls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class LsCommand : public Command

virtual std::string getShortOptions () override
{
return "v0C";
return "dM:m:v0C";
}

virtual std::string getSynopsis () override
Expand All @@ -46,6 +46,12 @@ class LsCommand : public Command
}

virtual int execute (Cmdline const & cmdline) override;

private:
void checkArguments (Cmdline const & cl);
void printResults (kdb::KeySet const & part, const int rootDepth, Cmdline const & cl);
int getDepth (kdb::Key const & key);
bool shallShowNextLevel (const std::string argument);
};

#endif
1 change: 1 addition & 0 deletions tests/shell/shell_recorder/tutorial_wrapper/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ endfunction ()

add_s_test (tutorial_cascading "${CMAKE_SOURCE_DIR}/doc/tutorials/cascading.md")
add_s_test (kdb-complete "${CMAKE_SOURCE_DIR}/doc/help/kdb-complete.md")
add_s_test (kdb-ls "${CMAKE_SOURCE_DIR}/doc/help/kdb-ls.md")
add_s_test (kdb-global-umount "${CMAKE_SOURCE_DIR}/doc/help/kdb-global-umount.md")

add_plugin_shell_test (blockresolver)
Expand Down