12/28/2006
Patch for GNU touch to add -p option, a la mkdir
The other day I realized that often when I’m using touch, I’d like it to have the ability to create ancestor directories that don’t exist, a la mkdir -p. I quickly hacked together a shell script to do what I want.
Then I thought that this might be a generally useful extension to touch, so I thought I’d take a stab at adding it directly to the touch source code in GNU coreutils. Here’s my patch, which I sent to the mailing list:
--- coreutils-6.7.orig/src/touch.c 2006-10-22 09:54:15.000000000 -0700
+++ coreutils-6.7/src/touch.c 2006-12-28 15:35:28.000000000 -0800
@@ -34,6 +34,8 @@
#include "safe-read.h"
#include "stat-time.h"
#include "utimens.h"
+#include "savewd.h"
+#include "mkdir-p.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "touch"
@@ -112,6 +114,62 @@
error (EXIT_FAILURE, 0, _("invalid date format %s"), quote (flex_date));
}
+/*
+ */
+
+/* Options passed to subsidiary functions. */
+struct mkdir_options
+{
+ /* Function to make an ancestor, or NULL if ancestors should not be
+ made. */
+ int (*make_ancestor_function) (char const *, char const *, void *);
+
+ /* Mode for ancestor directory. */
+ mode_t ancestor_mode;
+
+ /* Mode for directory itself. */
+ mode_t mode;
+
+ /* File mode bits affected by MODE. */
+ mode_t mode_bits;
+
+ /* If not null, format to use when reporting newly made directories. */
+ char const *created_directory_format;
+};
+
+static struct mkdir_options global_mkdir_options;
+
+static void
+announce_mkdir (char const *dir, void *options)
+{
+ struct mkdir_options const *o = options;
+ if (o->created_directory_format)
+ error (0, 0, o->created_directory_format, quote (dir));
+}
+
+/* Make ancestor directory DIR, whose last component is COMPONENT,
+ with options OPTIONS. Assume the working directory is COMPONENT's
+ parent. Return 0 if successful and the resulting directory is
+ readable, 1 if successful but the resulting directory is not
+ readable, -1 (setting errno) otherwise. */
+static int
+make_ancestor (char const *dir, char const *component, void *options)
+{
+ struct mkdir_options const *o = options;
+ int r = mkdir (component, o->ancestor_mode);
+ if (r == 0)
+ {
+ r = ! (o->ancestor_mode & S_IRUSR);
+ announce_mkdir (dir, options);
+ }
+ return r;
+}
+
+/* */
+
/* Update the time of file FILE according to the options given.
Return true if successful. */
@@ -129,6 +187,18 @@
fd = STDOUT_FILENO;
else if (! no_create)
{
+ {
+ char *dir = dir_name (file);
+ struct savewd wd;
+ savewd_init (&wd);
+ void *make_ancestor_function = global_mkdir_options.make_ancestor_function;
+ mode_t mode = global_mkdir_options.mode;
+ mode_t mode_bits = global_mkdir_options.mode_bits;
+ make_dir_parents (dir, &wd, make_ancestor_function, &global_mkdir_options,
+ mode, announce_mkdir,
+ mode_bits, (uid_t) -1, (gid_t) -1, true);
+ }
+
/* Try to open FILE, creating it if necessary. */
fd = fd_reopen (STDIN_FILENO, file,
O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
@@ -246,6 +316,7 @@
-m change only the modification time\n\
"), stdout);
fputs (_("\
+ -p, --parents no error if existing, make parent directories as needed\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\
@@ -272,6 +343,10 @@
bool date_set = false;
bool ok = true;
char const *flex_date = NULL;
+ global_mkdir_options.make_ancestor_function = NULL;
+ global_mkdir_options.mode = S_IRWXUGO;
+ global_mkdir_options.mode_bits = 0;
+ global_mkdir_options.created_directory_format = NULL;
initialize_main (&argc, &argv);
program_name = argv[0];
@@ -284,7 +359,7 @@
change_times = 0;
no_create = use_ref = false;
- while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
+ while ((c = getopt_long (argc, argv, "acd:fmpr:t:", longopts, NULL)) != -1)
{
switch (c)
{
@@ -307,6 +382,12 @@
change_times |= CH_MTIME;
break;
+ case 'p':
+ global_mkdir_options.make_ancestor_function = make_ancestor;
+ mode_t umask_value = umask (0);
+ global_mkdir_options.ancestor_mode = (S_IRWXUGO & ~umask_value) | (S_IWUSR | S_IXUSR);
+ break;
+
case 'r':
use_ref = true;
ref_file = optarg;
and I also have this patch in Subversion here.
This feels fairly hacky to me, as there’s a fair amount of code duplication from mkdir.c - my hope is that the coreutils maintainers can take this and clean it up, perhaps by moving some of the common functionality into the lib directory.
Del.icio.us
Digg
Reddit
Technorati
Possibly related posts
No comments yet. Be the first.
Leave a reply


