LCOV - code coverage report
Current view: top level - apps - gdalalg_vsi_list.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 133 143 93.0 %
Date: 2025-06-19 12:30:01 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "vsi list" subcommand
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vsi_list.h"
      14             : 
      15             : #include "cpl_string.h"
      16             : #include "cpl_time.h"
      17             : #include "cpl_vsi.h"
      18             : #include "cpl_vsi_error.h"
      19             : 
      20             : #include <cinttypes>
      21             : 
      22             : //! @cond Doxygen_Suppress
      23             : 
      24             : #ifndef _
      25             : #define _(x) (x)
      26             : #endif
      27             : 
      28             : /************************************************************************/
      29             : /*              GDALVSIListAlgorithm::GDALVSIListAlgorithm()            */
      30             : /************************************************************************/
      31             : 
      32          18 : GDALVSIListAlgorithm::GDALVSIListAlgorithm()
      33          18 :     : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL), m_oWriter(JSONPrint, this)
      34             : {
      35          36 :     auto &arg = AddArg("filename", 0, _("File or directory name"), &m_filename)
      36          18 :                     .SetPositional()
      37          18 :                     .SetRequired();
      38          18 :     SetAutoCompleteFunctionForFilename(arg, 0);
      39             : 
      40          18 :     AddOutputFormatArg(&m_format).SetDefault("json").SetChoices("json", "text");
      41             : 
      42          36 :     AddArg("long-listing", 'l', _("Use a long listing format"), &m_longListing)
      43          18 :         .AddAlias("long");
      44             :     AddArg("recursive", 'R', _("List subdirectories recursively"),
      45          18 :            &m_recursive);
      46          36 :     AddArg("depth", 0, _("Maximum depth in recursive mode"), &m_depth)
      47          18 :         .SetMinValueIncluded(1);
      48          36 :     AddArg("absolute-path", 0, _("Display absolute path"), &m_absolutePath)
      49          18 :         .AddAlias("abs");
      50             :     AddArg("tree", 0, _("Use a hierarchical presentation for JSON output"),
      51          18 :            &m_JSONAsTree);
      52             : 
      53          18 :     AddOutputStringArg(&m_output);
      54             :     AddArg(
      55             :         "stdout", 0,
      56             :         _("Directly output on stdout. If enabled, output-string will be empty"),
      57          36 :         &m_stdout)
      58          18 :         .SetHiddenForCLI();
      59          18 : }
      60             : 
      61             : /************************************************************************/
      62             : /*                   GDALVSIListAlgorithm::Print()                      */
      63             : /************************************************************************/
      64             : 
      65        1807 : void GDALVSIListAlgorithm::Print(const char *str)
      66             : {
      67        1807 :     if (m_stdout)
      68           0 :         fwrite(str, 1, strlen(str), stdout);
      69             :     else
      70        1807 :         m_output += str;
      71        1807 : }
      72             : 
      73             : /************************************************************************/
      74             : /*                  GDALVSIListAlgorithm::JSONPrint()                   */
      75             : /************************************************************************/
      76             : 
      77        1734 : /* static */ void GDALVSIListAlgorithm::JSONPrint(const char *pszTxt,
      78             :                                                   void *pUserData)
      79             : {
      80        1734 :     static_cast<GDALVSIListAlgorithm *>(pUserData)->Print(pszTxt);
      81        1734 : }
      82             : 
      83             : /************************************************************************/
      84             : /*                            GetDepth()                                */
      85             : /************************************************************************/
      86             : 
      87          12 : static int GetDepth(const std::string &filename)
      88             : {
      89          12 :     int depth = 0;
      90          12 :     const char sep = VSIGetDirectorySeparator(filename.c_str())[0];
      91         112 :     for (size_t i = 0; i < filename.size(); ++i)
      92             :     {
      93         106 :         if ((filename[i] == sep || filename[i] == '/') &&
      94           6 :             i != filename.size() - 1)
      95           6 :             ++depth;
      96             :     }
      97          12 :     return depth;
      98             : }
      99             : 
     100             : /************************************************************************/
     101             : /*                 GDALVSIListAlgorithm::PrintEntry()                   */
     102             : /************************************************************************/
     103             : 
     104         361 : void GDALVSIListAlgorithm::PrintEntry(const VSIDIREntry *entry)
     105             : {
     106         722 :     std::string filename;
     107         361 :     if (m_format == "json" && m_JSONAsTree)
     108             :     {
     109          12 :         filename = CPLGetFilename(entry->pszName);
     110             :     }
     111         349 :     else if (m_absolutePath)
     112             :     {
     113          67 :         if (CPLIsFilenameRelative(m_filename.c_str()))
     114             :         {
     115          61 :             char *pszCurDir = CPLGetCurrentDir();
     116          61 :             if (!pszCurDir)
     117           0 :                 pszCurDir = CPLStrdup(".");
     118          61 :             if (m_filename == ".")
     119           0 :                 filename = pszCurDir;
     120             :             else
     121             :                 filename =
     122          61 :                     CPLFormFilenameSafe(pszCurDir, m_filename.c_str(), nullptr);
     123          61 :             CPLFree(pszCurDir);
     124             :         }
     125             :         else
     126             :         {
     127           6 :             filename = m_filename;
     128             :         }
     129             :         filename =
     130          67 :             CPLFormFilenameSafe(filename.c_str(), entry->pszName, nullptr);
     131             :     }
     132             :     else
     133             :     {
     134         282 :         filename = entry->pszName;
     135             :     }
     136             : 
     137         361 :     char permissions[1 + 3 + 3 + 3 + 1] = "----------";
     138             :     struct tm bdt;
     139         361 :     memset(&bdt, 0, sizeof(bdt));
     140             : 
     141         361 :     if (m_longListing)
     142             :     {
     143          76 :         if (entry->bModeKnown)
     144             :         {
     145          76 :             if (VSI_ISDIR(entry->nMode))
     146           5 :                 permissions[0] = 'd';
     147         760 :             for (int i = 0; i < 9; ++i)
     148             :             {
     149         684 :                 if (entry->nMode & (1 << i))
     150         488 :                     permissions[9 - i] = (i % 3) == 0   ? 'x'
     151         244 :                                          : (i % 3) == 1 ? 'w'
     152             :                                                         : 'r';
     153             :             }
     154             :         }
     155           0 :         else if (VSI_ISDIR(entry->nMode))
     156             :         {
     157           0 :             strcpy(permissions, "dr-xr-xr-x");
     158             :         }
     159             :         else
     160             :         {
     161           0 :             strcpy(permissions, "-r--r--r--");
     162             :         }
     163             : 
     164          76 :         CPLUnixTimeToYMDHMS(entry->nMTime, &bdt);
     165             :     }
     166             : 
     167         361 :     if (m_format == "json")
     168             :     {
     169         294 :         if (m_JSONAsTree)
     170             :         {
     171          18 :             while (!m_stackNames.empty() &&
     172          18 :                    GetDepth(m_stackNames.back()) >= GetDepth(entry->pszName))
     173             :             {
     174           0 :                 m_oWriter.EndArray();
     175           0 :                 m_oWriter.EndObj();
     176           0 :                 m_stackNames.pop_back();
     177             :             }
     178             :         }
     179             : 
     180         294 :         if (m_longListing)
     181             :         {
     182          15 :             m_oWriter.StartObj();
     183          15 :             m_oWriter.AddObjKey("name");
     184          15 :             m_oWriter.Add(filename);
     185          15 :             m_oWriter.AddObjKey("type");
     186          15 :             m_oWriter.Add(VSI_ISDIR(entry->nMode) ? "directory" : "file");
     187          15 :             m_oWriter.AddObjKey("size");
     188          15 :             m_oWriter.Add(static_cast<uint64_t>(entry->nSize));
     189          15 :             if (entry->bMTimeKnown)
     190             :             {
     191          15 :                 m_oWriter.AddObjKey("last_modification_date");
     192          15 :                 m_oWriter.Add(CPLSPrintf("%04d-%02d-%02d %02d:%02d:%02dZ",
     193          15 :                                          bdt.tm_year + 1900, bdt.tm_mon + 1,
     194             :                                          bdt.tm_mday, bdt.tm_hour, bdt.tm_min,
     195             :                                          bdt.tm_sec));
     196             :             }
     197          15 :             if (entry->bModeKnown)
     198             :             {
     199          15 :                 m_oWriter.AddObjKey("permissions");
     200          15 :                 m_oWriter.Add(permissions);
     201             :             }
     202          15 :             if (m_JSONAsTree && VSI_ISDIR(entry->nMode))
     203             :             {
     204           2 :                 m_stackNames.push_back(entry->pszName);
     205           2 :                 m_oWriter.AddObjKey("entries");
     206           2 :                 m_oWriter.StartArray();
     207             :             }
     208             :             else
     209             :             {
     210          13 :                 m_oWriter.EndObj();
     211             :             }
     212             :         }
     213             :         else
     214             :         {
     215         279 :             if (m_JSONAsTree && VSI_ISDIR(entry->nMode))
     216             :             {
     217           2 :                 m_oWriter.StartObj();
     218           2 :                 m_oWriter.AddObjKey("name");
     219           2 :                 m_oWriter.Add(filename);
     220             : 
     221           2 :                 m_stackNames.push_back(entry->pszName);
     222           2 :                 m_oWriter.AddObjKey("entries");
     223           2 :                 m_oWriter.StartArray();
     224             :             }
     225             :             else
     226             :             {
     227         277 :                 m_oWriter.Add(filename);
     228             :             }
     229             :         }
     230             :     }
     231          67 :     else if (m_longListing)
     232             :     {
     233         122 :         Print(CPLSPrintf("%s 1 unknown unknown %12" PRIu64
     234             :                          " %04d-%02d-%02d %02d:%02d %s\n",
     235          61 :                          permissions, static_cast<uint64_t>(entry->nSize),
     236          61 :                          bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday,
     237             :                          bdt.tm_hour, bdt.tm_min, filename.c_str()));
     238             :     }
     239             :     else
     240             :     {
     241           6 :         Print(filename.c_str());
     242           6 :         Print("\n");
     243             :     }
     244         361 : }
     245             : 
     246             : /************************************************************************/
     247             : /*                    GDALVSIListAlgorithm::RunImpl()                   */
     248             : /************************************************************************/
     249             : 
     250          17 : bool GDALVSIListAlgorithm::RunImpl(GDALProgressFunc, void *)
     251             : {
     252             :     VSIStatBufL sStat;
     253          17 :     VSIErrorReset();
     254          17 :     const auto nOldErrorNum = VSIGetLastErrorNo();
     255          17 :     if (VSIStatL(m_filename.c_str(), &sStat) != 0)
     256             :     {
     257           2 :         if (nOldErrorNum != VSIGetLastErrorNo())
     258             :         {
     259           1 :             ReportError(CE_Failure, CPLE_FileIO,
     260             :                         "'%s' cannot be accessed. %s: %s", m_filename.c_str(),
     261             :                         VSIErrorNumToString(VSIGetLastErrorNo()),
     262             :                         VSIGetLastErrorMsg());
     263             :         }
     264             :         else
     265             :         {
     266           1 :             ReportError(CE_Failure, CPLE_FileIO,
     267             :                         "'%s' does not exist or cannot be accessed",
     268             :                         m_filename.c_str());
     269             :         }
     270           2 :         return false;
     271             :     }
     272             : 
     273          15 :     bool ret = false;
     274          15 :     if (VSI_ISDIR(sStat.st_mode))
     275             :     {
     276             :         std::unique_ptr<VSIDIR, decltype(&VSICloseDir)> dir(
     277             :             VSIOpenDir(m_filename.c_str(),
     278           7 :                        m_recursive ? (m_depth == 0  ? 0
     279           7 :                                       : m_depth > 0 ? m_depth - 1
     280             :                                                     : -1)
     281             :                                    : 0,
     282             :                        nullptr),
     283          35 :             VSICloseDir);
     284          14 :         if (dir)
     285             :         {
     286          14 :             ret = true;
     287          14 :             if (m_format == "json")
     288          12 :                 m_oWriter.StartArray();
     289         374 :             while (const auto entry = VSIGetNextDirEntry(dir.get()))
     290             :             {
     291         360 :                 if (!(entry->pszName[0] == '.' &&
     292           0 :                       (entry->pszName[1] == '.' || entry->pszName[1] == 0)))
     293             :                 {
     294         360 :                     PrintEntry(entry);
     295             :                 }
     296         360 :             }
     297          18 :             while (!m_stackNames.empty())
     298             :             {
     299           4 :                 m_stackNames.pop_back();
     300           4 :                 m_oWriter.EndArray();
     301           4 :                 m_oWriter.EndObj();
     302             :             }
     303          14 :             if (m_format == "json")
     304          12 :                 m_oWriter.EndArray();
     305             :         }
     306             :     }
     307             :     else
     308             :     {
     309           1 :         ret = true;
     310           2 :         VSIDIREntry sEntry;
     311           1 :         sEntry.pszName = CPLStrdup(m_filename.c_str());
     312           1 :         sEntry.bModeKnown = true;
     313           1 :         sEntry.nMode = sStat.st_mode;
     314           1 :         sEntry.nSize = sStat.st_size;
     315           1 :         PrintEntry(&sEntry);
     316             :     }
     317             : 
     318          15 :     return ret;
     319             : }
     320             : 
     321             : //! @endcond

Generated by: LCOV version 1.14