bug#55937: [PATCH] touch: create parent directories if needed

2022-06-15 Thread Alan Rosenthal
Thanks for taking the time to review my patch.

I agree with you that this change is not strictly required. It's more of a
nice to have / ergonomic improvement to the existing touch interface.

There are several StackOverflow posts that ask for this very feature:
https://unix.stackexchange.com/q/63098 &
https://unix.stackexchange.com/q/305844

Below is the help text for touch:
> Update the access and modification times of each FILE to the current time.
> A FILE argument that does not exist is created empty, unless -c or -h is
supplied.

I was surprised to learn there was no existing flag that would allow
directory creation when a file and its directories do not exist. Currently
if directories do not exist, the command fails. I would go as far to argue
that creating a file also implies creating any required directories, since
directories must exist before we can create said file.

I also agree with David Hilton's recommendation that we should not change
the default behavior of touch, like in my first patch, but rather add an
opt-in flag for this behavior.

Thank you for reading my reply and I look forward to your future feedback.

On Tue, Jun 14, 2022 at 10:37 PM Paul Eggert  wrote:

> On 6/14/22 19:20, Alan Rosenthal wrote:
> > `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`.
>
> I don't see how this useful enough to merit a change, since one can
> achieve the effect of the proposed "touch -p" with the already-existing
> "mkdir -p" followed by plain "touch". mkdir -p already exists and should
> work everywhere that's POSIX-compatible. We don't need -p for other
> commands that create files (e.g., cp, mv, ln); what's special about
> 'touch'?
>
>


bug#55937: [PATCH] touch: create parent directories if needed

2022-06-14 Thread Paul Eggert

On 6/14/22 19:20, Alan Rosenthal wrote:

`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`.


I don't see how this useful enough to merit a change, since one can 
achieve the effect of the proposed "touch -p" with the already-existing 
"mkdir -p" followed by plain "touch". mkdir -p already exists and should 
work everywhere that's POSIX-compatible. We don't need -p for other 
commands that create files (e.g., cp, mv, ln); what's special about 'touch'?







bug#55937: [PATCH] touch: create parent directories if needed

2022-06-14 Thread Alan Rosenthal
`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();
+  ptrdiff_t ret = mkancesdirs((char*) file, , make_dir, NULL);
+  if (ret == -1)
+{
+  error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
+  return false;
+}
+  int r = savewd_restore(, 0);
+  if (r < 0)
+{
+  error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
+  return false;
+}
+  savewd_finish();
+}
+
   /* 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=WORDchange 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 
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'},
>

bug#55937: [PATCH] touch: create parent directories if needed

2022-06-13 Thread David Hilton
I don't like this as the default behavior. If this feature is added, it
should be optional, and enabled with -p, like mkdir.

David


bug#55937: [PATCH] touch: create parent directories if needed

2022-06-13 Thread Alan Rosenthal
`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();
+  ptrdiff_t ret = mkancesdirs((char*) file, , make_dir, NULL);
+  if (ret == -1)
+{
+  error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
+  return false;
+}
+  int r = savewd_restore(, 0);
+  if (r < 0)
+{
+  error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
+  return false;
+}
+  savewd_finish();
+}
+
   /* 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 
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},
>

bug#55937: [PATCH] touch: create parent directories if needed

2022-06-12 Thread Alan Rosenthal
`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();
+  ptrdiff_t ret = mkancesdirs((char*) file, , make_dir, NULL);
+  if (ret == -1)
+{
+  error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
+  return false;
+}
+  int r = savewd_restore(, 0);
+  if (r < 0)
+{
+  error (0, open_errno, _("cannot mkdir %s"), quoteaf (file));
+  return false;
+}
+  savewd_finish();
+}
+
   /* 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