mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-30 05:39:12 +02:00
Improve behavior of the repo-ls command.
* Exclude linefeed when there is no output to avoid a blank line. * Honor filter when adding . path or listing a single file.
This commit is contained in:
parent
237ba54d20
commit
6fe60a2428
@ -41,6 +41,15 @@
|
|||||||
|
|
||||||
<p>Fix expression when recursion enabled in <code>storageInfoListP()</code>.</p>
|
<p>Fix expression when recursion enabled in <code>storageInfoListP()</code>.</p>
|
||||||
</release-item>
|
</release-item>
|
||||||
|
|
||||||
|
<release-item>
|
||||||
|
<release-item-contributor-list>
|
||||||
|
<release-item-reviewer id="stefan.fercot"/>
|
||||||
|
<release-item-reviewer id="cynthia.shang"/>
|
||||||
|
</release-item-contributor-list>
|
||||||
|
|
||||||
|
<p>Improve behavior of the <cmd>repo-ls</cmd> command.</p>
|
||||||
|
</release-item>
|
||||||
</release-development-list>
|
</release-development-list>
|
||||||
</release-core-list>
|
</release-core-list>
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ Repository List Command
|
|||||||
#include "common/io/handleWrite.h"
|
#include "common/io/handleWrite.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/memContext.h"
|
#include "common/memContext.h"
|
||||||
|
#include "common/regExp.h"
|
||||||
#include "common/type/json.h"
|
#include "common/type/json.h"
|
||||||
#include "common/type/string.h"
|
#include "common/type/string.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
@ -45,13 +46,8 @@ storageListRenderCallback(void *data, const StorageInfo *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add seperator character
|
// Add seperator character
|
||||||
if (!listData->first)
|
if (!listData->first && listData->json)
|
||||||
{
|
ioWrite(listData->write, COMMA_BUF);
|
||||||
if (listData->json)
|
|
||||||
ioWrite(listData->write, COMMA_BUF);
|
|
||||||
else
|
|
||||||
ioWrite(listData->write, LF_BUF);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
listData->first = false;
|
listData->first = false;
|
||||||
|
|
||||||
@ -103,6 +99,7 @@ storageListRenderCallback(void *data, const StorageInfo *info)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ioWrite(listData->write, BUFSTR(info->name));
|
ioWrite(listData->write, BUFSTR(info->name));
|
||||||
|
ioWrite(listData->write, LF_BUF);
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN_VOID();
|
FUNCTION_TEST_RETURN_VOID();
|
||||||
@ -134,8 +131,10 @@ storageListRender(IoWrite *write)
|
|||||||
else if (strLstSize(cfgCommandParam()) > 1)
|
else if (strLstSize(cfgCommandParam()) > 1)
|
||||||
THROW(ParamInvalidError, "only one path may be specified");
|
THROW(ParamInvalidError, "only one path may be specified");
|
||||||
|
|
||||||
// Get output
|
// Get options
|
||||||
bool json = strEqZ(cfgOptionStr(cfgOptOutput), "json") ? true : false;
|
bool json = strEqZ(cfgOptionStr(cfgOptOutput), "json") ? true : false;
|
||||||
|
const String *expression = cfgOptionStrNull(cfgOptFilter);
|
||||||
|
RegExp *regExp = expression == NULL ? NULL : regExpNew(expression);
|
||||||
|
|
||||||
// Render the info list
|
// Render the info list
|
||||||
StorageListRenderCallbackData data =
|
StorageListRenderCallbackData data =
|
||||||
@ -155,24 +154,30 @@ storageListRender(IoWrite *write)
|
|||||||
|
|
||||||
if (info.exists && info.type == storageTypeFile)
|
if (info.exists && info.type == storageTypeFile)
|
||||||
{
|
{
|
||||||
info.name = DOT_STR;
|
if (regExp == NULL || regExpMatch(regExp, storagePathP(storageRepo(), path)))
|
||||||
storageListRenderCallback(&data, &info);
|
{
|
||||||
|
info.name = DOT_STR;
|
||||||
|
storageListRenderCallback(&data, &info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Else try to list the path
|
// Else try to list the path
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// The path will always be reported as existing so we don't get different results from storage that does not support paths
|
// The path will always be reported as existing so we don't get different results from storage that does not support paths
|
||||||
if (data.json)
|
if (data.json && (regExp == NULL || regExpMatch(regExp, DOT_STR)))
|
||||||
storageListRenderCallback(&data, &(StorageInfo){.type = storageTypePath, .name = DOT_STR});
|
storageListRenderCallback(&data, &(StorageInfo){.type = storageTypePath, .name = DOT_STR});
|
||||||
|
|
||||||
// List content of the path
|
// List content of the path
|
||||||
storageInfoListP(
|
storageInfoListP(
|
||||||
storageRepo(), path, storageListRenderCallback, &data, .sortOrder = sortOrder,
|
storageRepo(), path, storageListRenderCallback, &data, .sortOrder = sortOrder, .expression = expression,
|
||||||
.expression = cfgOptionStrNull(cfgOptFilter), .recurse = cfgOptionBool(cfgOptRecurse));
|
.recurse = cfgOptionBool(cfgOptRecurse));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.json)
|
if (data.json)
|
||||||
|
{
|
||||||
ioWrite(data.write, BRACER_BUF);
|
ioWrite(data.write, BRACER_BUF);
|
||||||
|
ioWrite(data.write, LF_BUF);
|
||||||
|
}
|
||||||
|
|
||||||
ioWriteClose(data.write);
|
ioWriteClose(data.write);
|
||||||
|
|
||||||
@ -190,7 +195,6 @@ cmdStorageList(void)
|
|||||||
TRY_BEGIN()
|
TRY_BEGIN()
|
||||||
{
|
{
|
||||||
storageListRender(ioHandleWriteNew(STRDEF("stdout"), STDOUT_FILENO));
|
storageListRender(ioHandleWriteNew(STRDEF("stdout"), STDOUT_FILENO));
|
||||||
ioHandleWriteOneStr(STDOUT_FILENO, LF_STR);
|
|
||||||
}
|
}
|
||||||
// Ignore write errors because it's possible (even likely) that this output is being piped to something like head which
|
// Ignore write errors because it's possible (even likely) that this output is being piped to something like head which
|
||||||
// will exit when it gets what it needs and leave us writing to a broken pipe. It would be better to just ignore the broken
|
// will exit when it gets what it needs and leave us writing to a broken pipe. It would be better to just ignore the broken
|
||||||
|
@ -54,7 +54,7 @@ testRun(void)
|
|||||||
strNewBuf(output),
|
strNewBuf(output),
|
||||||
"{"
|
"{"
|
||||||
"\".\":{\"type\":\"path\"}"
|
"\".\":{\"type\":\"path\"}"
|
||||||
"}",
|
"}\n",
|
||||||
" check output");
|
" check output");
|
||||||
|
|
||||||
// Empty directory
|
// Empty directory
|
||||||
@ -73,9 +73,26 @@ testRun(void)
|
|||||||
strNewBuf(output),
|
strNewBuf(output),
|
||||||
"{"
|
"{"
|
||||||
"\".\":{\"type\":\"path\"}"
|
"\".\":{\"type\":\"path\"}"
|
||||||
"}",
|
"}\n",
|
||||||
" check output");
|
" check output");
|
||||||
|
|
||||||
|
output = bufNew(0);
|
||||||
|
cfgOptionSet(cfgOptFilter, cfgSourceParam, VARSTRDEF("\\."));
|
||||||
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "empty directory with filter match (json)");
|
||||||
|
TEST_RESULT_STR_Z(
|
||||||
|
strNewBuf(output),
|
||||||
|
"{"
|
||||||
|
"\".\":{\"type\":\"path\"}"
|
||||||
|
"}\n",
|
||||||
|
" check output");
|
||||||
|
|
||||||
|
output = bufNew(0);
|
||||||
|
cfgOptionSet(cfgOptFilter, cfgSourceParam, VARSTRDEF("2$"));
|
||||||
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "empty directory with no filter match (json)");
|
||||||
|
TEST_RESULT_STR_Z(strNewBuf(output), "{}\n", " check output");
|
||||||
|
|
||||||
|
cfgOptionSet(cfgOptFilter, cfgSourceParam, NULL);
|
||||||
|
|
||||||
// Add path and file
|
// Add path and file
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
cfgOptionSet(cfgOptSort, cfgSourceParam, VARSTRDEF("asc"));
|
cfgOptionSet(cfgOptSort, cfgSourceParam, VARSTRDEF("asc"));
|
||||||
@ -90,7 +107,7 @@ testRun(void)
|
|||||||
output = bufNew(0);
|
output = bufNew(0);
|
||||||
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
||||||
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "path and file (text)");
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "path and file (text)");
|
||||||
TEST_RESULT_STR_Z(strNewBuf(output), "aaa\nbbb\nlink\npipe", " check output");
|
TEST_RESULT_STR_Z(strNewBuf(output), "aaa\nbbb\nlink\npipe\n", " check output");
|
||||||
|
|
||||||
output = bufNew(0);
|
output = bufNew(0);
|
||||||
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("json"));
|
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("json"));
|
||||||
@ -103,7 +120,7 @@ testRun(void)
|
|||||||
"\"bbb\":{\"type\":\"path\"},"
|
"\"bbb\":{\"type\":\"path\"},"
|
||||||
"\"link\":{\"type\":\"link\",\"destination\":\"../bbb\"},"
|
"\"link\":{\"type\":\"link\",\"destination\":\"../bbb\"},"
|
||||||
"\"pipe\":{\"type\":\"special\"}"
|
"\"pipe\":{\"type\":\"special\"}"
|
||||||
"}",
|
"}\n",
|
||||||
" check output");
|
" check output");
|
||||||
|
|
||||||
// Reverse sort
|
// Reverse sort
|
||||||
@ -113,7 +130,7 @@ testRun(void)
|
|||||||
output = bufNew(0);
|
output = bufNew(0);
|
||||||
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
||||||
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "path and file (text)");
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "path and file (text)");
|
||||||
TEST_RESULT_STR_Z(strNewBuf(output), "pipe\nlink\nbbb\naaa", " check output");
|
TEST_RESULT_STR_Z(strNewBuf(output), "pipe\nlink\nbbb\naaa\n", " check output");
|
||||||
|
|
||||||
// Recurse
|
// Recurse
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -123,7 +140,7 @@ testRun(void)
|
|||||||
output = bufNew(0);
|
output = bufNew(0);
|
||||||
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
||||||
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "filter");
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "filter");
|
||||||
TEST_RESULT_STR_Z(strNewBuf(output), "pipe\nlink\nbbb/ccc\nbbb\naaa", " check output");
|
TEST_RESULT_STR_Z(strNewBuf(output), "pipe\nlink\nbbb/ccc\nbbb\naaa\n", " check output");
|
||||||
|
|
||||||
// Filter
|
// Filter
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -133,7 +150,7 @@ testRun(void)
|
|||||||
output = bufNew(0);
|
output = bufNew(0);
|
||||||
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
||||||
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "filter");
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "filter");
|
||||||
TEST_RESULT_STR_Z(strNewBuf(output), "aaa", " check output");
|
TEST_RESULT_STR_Z(strNewBuf(output), "aaa\n", " check output");
|
||||||
|
|
||||||
// Subdirectory
|
// Subdirectory
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -144,7 +161,7 @@ testRun(void)
|
|||||||
output = bufNew(0);
|
output = bufNew(0);
|
||||||
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
cfgOptionSet(cfgOptOutput, cfgSourceParam, VARSTRDEF("text"));
|
||||||
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "subdirectory");
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "subdirectory");
|
||||||
TEST_RESULT_STR_Z(strNewBuf(output), "ccc", " check output");
|
TEST_RESULT_STR_Z(strNewBuf(output), "ccc\n", " check output");
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
// Redirect stdout to a file
|
// Redirect stdout to a file
|
||||||
@ -166,7 +183,6 @@ testRun(void)
|
|||||||
strLstAddZ(argListTmp, "ccc");
|
strLstAddZ(argListTmp, "ccc");
|
||||||
harnessCfgLoad(cfgCmdRepoLs, argListTmp);
|
harnessCfgLoad(cfgCmdRepoLs, argListTmp);
|
||||||
|
|
||||||
output = bufNew(0);
|
|
||||||
TEST_ERROR(storageListRender(ioBufferWriteNew(output)), ParamInvalidError, "only one path may be specified");
|
TEST_ERROR(storageListRender(ioBufferWriteNew(output)), ParamInvalidError, "only one path may be specified");
|
||||||
|
|
||||||
// File
|
// File
|
||||||
@ -176,13 +192,29 @@ testRun(void)
|
|||||||
strLstAddZ(argList, "--output=json");
|
strLstAddZ(argList, "--output=json");
|
||||||
harnessCfgLoad(cfgCmdRepoLs, argList);
|
harnessCfgLoad(cfgCmdRepoLs, argList);
|
||||||
|
|
||||||
|
output = bufNew(0);
|
||||||
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "file (json)");
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "file (json)");
|
||||||
TEST_RESULT_STR_Z(
|
TEST_RESULT_STR_Z(
|
||||||
strNewBuf(output),
|
strNewBuf(output),
|
||||||
"{"
|
"{"
|
||||||
"\".\":{\"type\":\"file\",\"size\":8,\"time\":1578671569}"
|
"\".\":{\"type\":\"file\",\"size\":8,\"time\":1578671569}"
|
||||||
"}",
|
"}\n",
|
||||||
" check output");
|
" check output");
|
||||||
|
|
||||||
|
output = bufNew(0);
|
||||||
|
cfgOptionSet(cfgOptFilter, cfgSourceParam, VARSTRDEF("\\/aaa$"));
|
||||||
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "file (json)");
|
||||||
|
TEST_RESULT_STR_Z(
|
||||||
|
strNewBuf(output),
|
||||||
|
"{"
|
||||||
|
"\".\":{\"type\":\"file\",\"size\":8,\"time\":1578671569}"
|
||||||
|
"}\n",
|
||||||
|
" check output");
|
||||||
|
|
||||||
|
output = bufNew(0);
|
||||||
|
cfgOptionSet(cfgOptFilter, cfgSourceParam, VARSTRDEF("bbb$"));
|
||||||
|
TEST_RESULT_VOID(storageListRender(ioBufferWriteNew(output)), "file (json)");
|
||||||
|
TEST_RESULT_STR_Z(strNewBuf(output), "{}\n", " check output");
|
||||||
}
|
}
|
||||||
|
|
||||||
// *****************************************************************************************************************************
|
// *****************************************************************************************************************************
|
||||||
|
Loading…
x
Reference in New Issue
Block a user