LCOV - code coverage report
Current view: top level - apps - gdalalg_vfs_list.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 129 139 92.8 %
Date: 2025-04-16 00:42:22 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.14