Subject: Re: [PATCH] touch: create parent directories if needed

From: Alan Rosenthal <[email-masked]>
Date: Tue, 14 Jun 2022 20:20:40 -0400

`touch -p a/b/c/d/e` will now be the same as running:
`mkdir -p a/b/c/d && touch a/b/c/d/e`.

Added an option -p/--create-dirs to create any required directories.
Default behavior remains the same.
---
 src/touch.c | 40 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/src/touch.c b/src/touch.c
index 21c247d0b..543f92b41 100644
--- a/src/touch.c
+++ b/src/touch.c
@@ -28,10 +28,12 @@
 #include "die.h"
 #include "error.h"
 #include "fd-reopen.h"
+#include "mkancesdirs.h"
 #include "parse-datetime.h"
 #include "posixtm.h"
 #include "posixver.h"
 #include "quote.h"
+#include "savewd.h"
 #include "stat-time.h"
 #include "utimens.h"

@@ -55,6 +57,9 @@ static int change_times;
 /* (-c) If true, don't create if not already there.  */
 static bool no_create;

+/* (-p) If true, create directories if not already there.  */
+static bool create_dirs;
+
 /* (-r) If true, use times from a reference file.  */
 static bool use_ref;

@@ -88,6 +93,7 @@ static struct option const longopts[] =
   {"date", required_argument, NULL, 'd'},
   {"reference", required_argument, NULL, 'r'},
   {"no-dereference", no_argument, NULL, 'h'},
+  {"create-dirs", no_argument, NULL, 'p'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
@@ -116,6 +122,14 @@ get_reldate (struct timespec *result,
     die (EXIT_FAILURE, 0, _("invalid date format %s"), quote (flex_date));
 }

+/* Create directory, called by mkancesdirs(). */
+
+static int
+make_dir(char const * file, char const * component, void * arg)
+{
+  return mkdir(component, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+}
+
 /* Update the time of file FILE according to the options given.
    Return true if successful.  */

@@ -130,6 +144,25 @@ touch (char const *file)
     fd = STDOUT_FILENO;
   else if (! (no_create || no_dereference))
     {
+      if (create_dirs)
+        {
+          struct savewd wd;
+          savewd_init(&wd);
+          ptrdiff_t ret = mkancesdirs((char*) file, &wd, make_dir, NULL);
+          if (ret == -1)
+            {
+              error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
+              return false;
+            }
+          int r = savewd_restore(&wd, 0);
+          if (r < 0)
+            {
+              error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
+              return false;
+            }
+          savewd_finish(&wd);
+        }
+
       /* Try to open FILE, creating it if necessary.  */
       fd = fd_reopen (STDIN_FILENO, file,
                       O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
MODE_RW_UGO);
@@ -240,6 +273,7 @@ change the times of the file associated with standard
output.\n\
   -m                     change only the modification time\n\
 "), stdout);
       fputs (_("\
+  -p, --create-dirs      create any required parent directories\n\
   -r, --reference=FILE   use this file's times instead of current time\n\
   -t STAMP               use [[CC]YY]MMDDhhmm[.ss] instead of current
time\n\
       --time=WORD        change the specified time:\n\
@@ -276,7 +310,7 @@ main (int argc, char **argv)
   change_times = 0;
   no_create = use_ref = false;

-  while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL)) !=
-1)
+  while ((c = getopt_long (argc, argv, "acd:fhmpr:t:", longopts, NULL)) !=
-1)
     {
       switch (c)
         {
@@ -303,6 +337,10 @@ main (int argc, char **argv)
           change_times |= CH_MTIME;
           break;

+        case 'p':
+          create_dirs = true;
+          break;
+
         case 'r':
           use_ref = true;
           ref_file = optarg;
-- 
2.20.1

On Mon, Jun 13, 2022 at 7:52 AM Alan Rosenthal <[email-masked]>
wrote:

> `touch a/b/c/d/e` will now be the same as running `mkdir -p a/b/c/d &&
> touch a/b/c/d/e`.
> Added an option --no-create-dirs to not create any directories.
> ---
>  src/touch.c | 40 +++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 39 insertions(+), 1 deletion(-)
>
> diff --git a/src/touch.c b/src/touch.c
> index 21c247d0b..557530f79 100644
> --- a/src/touch.c
> +++ b/src/touch.c
> @@ -28,10 +28,12 @@
>  #include "die.h"
>  #include "error.h"
>  #include "fd-reopen.h"
> +#include "mkancesdirs.h"
>  #include "parse-datetime.h"
>  #include "posixtm.h"
>  #include "posixver.h"
>  #include "quote.h"
> +#include "savewd.h"
>  #include "stat-time.h"
>  #include "utimens.h"
>
> @@ -55,6 +57,9 @@ static int change_times;
>  /* (-c) If true, don't create if not already there.  */
>  static bool no_create;
>
> +/* (-c) If true, don't create directories if not already there.  */
> +static bool no_create_dirs;
> +
>  /* (-r) If true, use times from a reference file.  */
>  static bool use_ref;
>
> @@ -88,6 +93,7 @@ static struct option const longopts[] =
>    {"date", required_argument, NULL, 'd'},
>    {"reference", required_argument, NULL, 'r'},
>    {"no-dereference", no_argument, NULL, 'h'},
> +  {"no-create-dirs", no_argument, NULL, 'i'},
>    {GETOPT_HELP_OPTION_DECL},
>    {GETOPT_VERSION_OPTION_DECL},
>    {NULL, 0, NULL, 0}
> @@ -116,6 +122,14 @@ get_reldate (struct timespec *result,
>      die (EXIT_FAILURE, 0, _("invalid date format %s"), quote (flex_date));
>  }
>
> +/* Create directory, called by mkancesdirs(). */
> +
> +static int
> +make_dir(char const * file, char const * component, void * arg)
> +{
> +  return mkdir(component, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH |
> S_IXOTH);
> +}
> +
>  /* Update the time of file FILE according to the options given.
>     Return true if successful.  */
>
> @@ -130,6 +144,25 @@ touch (char const *file)
>      fd = STDOUT_FILENO;
>    else if (! (no_create || no_dereference))
>      {
> +      if (! no_create_dirs)
> +        {
> +          struct savewd wd;
> +          savewd_init(&wd);
> +          ptrdiff_t ret = mkancesdirs((char*) file, &wd, make_dir, NULL);
> +          if (ret == -1)
> +            {
> +              error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
> +              return false;
> +            }
> +          int r = savewd_restore(&wd, 0);
> +          if (r < 0)
> +            {
> +              error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
> +              return false;
> +            }
> +          savewd_finish(&wd);
> +        }
> +
>        /* Try to open FILE, creating it if necessary.  */
>        fd = fd_reopen (STDIN_FILENO, file,
>                        O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
> MODE_RW_UGO);
> @@ -234,6 +267,7 @@ change the times of the file associated with standard
> output.\n\
>    -f                     (ignored)\n\
>  "), stdout);
>        fputs (_("\
> +  -i, --no-create-dirs   do not create any required parent directories\n\
>    -h, --no-dereference   affect each symbolic link instead of any
> referenced\n\
>                           file (useful only on systems that can change
> the\n\
>                           timestamps of a symlink)\n\
> @@ -276,7 +310,7 @@ main (int argc, char **argv)
>    change_times = 0;
>    no_create = use_ref = false;
>
> -  while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL)) !=
> -1)
> +  while ((c = getopt_long (argc, argv, "acd:fhimr:t:", longopts, NULL))
> != -1)
>      {
>        switch (c)
>          {
> @@ -299,6 +333,10 @@ main (int argc, char **argv)
>            no_dereference = true;
>            break;
>
> +        case 'i':
> +          no_create_dirs = true;
> +          break;
> +
>          case 'm':
>            change_times |= CH_MTIME;
>            break;
> --
> 2.20.1
>
> On Sun, Jun 12, 2022 at 10:05 PM Alan Rosenthal <[email-masked]>
> wrote:
>
>> `touch a/b/c/d/e` will now be the same as running `mkdir -p a/b/c/d &&
>> touch a/b/c/d/e`.
>> Added an option --no-create-dirs to not create any directories.
>> ---
>>  src/touch.c | 39 ++++++++++++++++++++++++++++++++++++++-
>>  1 file changed, 38 insertions(+), 1 deletion(-)
>>
>> diff --git a/src/touch.c b/src/touch.c
>> index 21c247d0b..9034e8797 100644
>> --- a/src/touch.c
>> +++ b/src/touch.c
>> @@ -28,10 +28,12 @@
>>  #include "die.h"
>>  #include "error.h"
>>  #include "fd-reopen.h"
>> +#include "mkancesdirs.h"
>>  #include "parse-datetime.h"
>>  #include "posixtm.h"
>>  #include "posixver.h"
>>  #include "quote.h"
>> +#include "savewd.h"
>>  #include "stat-time.h"
>>  #include "utimens.h"
>>
>> @@ -55,6 +57,9 @@ static int change_times;
>>  /* (-c) If true, don't create if not already there.  */
>>  static bool no_create;
>>
>> +/* (-c) If true, don't create directories if not already there.  */
>> +static bool no_create_dirs;
>> +
>>  /* (-r) If true, use times from a reference file.  */
>>  static bool use_ref;
>>
>> @@ -88,6 +93,7 @@ static struct option const longopts[] =
>>    {"date", required_argument, NULL, 'd'},
>>    {"reference", required_argument, NULL, 'r'},
>>    {"no-dereference", no_argument, NULL, 'h'},
>> +  {"no_create_dirs", no_argument, NULL, 'i'},
>>    {GETOPT_HELP_OPTION_DECL},
>>    {GETOPT_VERSION_OPTION_DECL},
>>    {NULL, 0, NULL, 0}
>> @@ -116,6 +122,14 @@ get_reldate (struct timespec *result,
>>      die (EXIT_FAILURE, 0, _("invalid date format %s"), quote
>> (flex_date));
>>  }
>>
>> +/* Create directory, called by mkancesdirs(). */
>> +
>> +static int
>> +make_dir(char const * file, char const * component, void * arg)
>> +{
>> +  return mkdir(component, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH |
>> S_IXOTH);
>> +}
>> +
>>  /* Update the time of file FILE according to the options given.
>>     Return true if successful.  */
>>
>> @@ -130,6 +144,25 @@ touch (char const *file)
>>      fd = STDOUT_FILENO;
>>    else if (! (no_create || no_dereference))
>>      {
>> +      if (! no_create_dirs)
>> +        {
>> +          struct savewd wd;
>> +          savewd_init(&wd);
>> +          ptrdiff_t ret = mkancesdirs((char*) file, &wd, make_dir, NULL);
>> +          if (ret == -1)
>> +            {
>> +              error (0, open_errno, _("cannot mkdir %s"), quoteaf
>> (file));
>> +              return false;
>> +            }
>> +          int r = savewd_restore(&wd, 0);
>> +          if (r < 0)
>> +            {
>> +              error (0, open_errno, _("cannot mkdir %s"), quoteaf
>> (file));
>> +              return false;
>> +            }
>> +          savewd_finish(&wd);
>> +        }
>> +
>>        /* Try to open FILE, creating it if necessary.  */
>>        fd = fd_reopen (STDIN_FILENO, file,
>>                        O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
>> MODE_RW_UGO);
>> @@ -276,7 +309,7 @@ main (int argc, char **argv)
>>    change_times = 0;
>>    no_create = use_ref = false;
>>
>> -  while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL))
>> != -1)
>> +  while ((c = getopt_long (argc, argv, "acd:fhimr:t:", longopts, NULL))
>> != -1)
>>      {
>>        switch (c)
>>          {
>> @@ -299,6 +332,10 @@ main (int argc, char **argv)
>>            no_dereference = true;
>>            break;
>>
>> +        case 'i':
>> +          no_create_dirs = true;
>> +          break;
>> +
>>          case 'm':
>>            change_times |= CH_MTIME;
>>            break;
>> --
>> 2.20.1
>>
>>