I'm attaching a patch to add a new parameter -keep-top-dirs to
genisoimage.

Motivation: I have a large archive folder with many top level
directories, each named like yyyy-mm-dd. I want to archive it on discs,
but it doesn't fit on a single disc, so I have to specify a subset of
directories as input to genisoimage. I want to keep the directory
structure, the disc shall have the yyyy-mm-dd directories as entries of
the root directory.

For example, I want to burn 50 folders like 2015-* to disc 1, and
60 folders named 2016-* to disc b.

IIUC there is no existing way to keep top level directories with a
simple parameter list like: genisoimage -o output.iso 2015-*

I understand that currently there are the following options:
- copy/move all desired directories to a temporary directory and use
  genisoimage -o output.iso /path/to/temporary/directory
- instead of coping/moving, add symbolic links to a temporary directory
  for each desired directory.
- use the -graft-points option and use a pathspec list like
  dir1=dir1 dir2=dir2 dir3=dir3

I consider all of these option inconvenient, especially if the list
of directories isn't small.

With the attached patch, it is possible to achieve my requirement with
this simple command: genisoimage -o output.iso -keep-top-dirs 2015-*

The patch is on top of version cdrkit_1.1.11-3.1ubuntu1.dsc
Feedback welcome.  Please kindly let me know if I should rather provide
a patch on top of a different version.
Is there interest to merge it?

Regards,
Kai
# User Kai Engert <k...@kuix.de>
Add genisoimage parameter -keep-top-dirs

diff -r 643d90fc2314 genisoimage/genisoimage.1
--- a/genisoimage/genisoimage.1	Wed Dec 29 14:34:35 2021 +0100
+++ b/genisoimage/genisoimage.1	Thu Dec 30 00:08:05 2021 +0100
@@ -559,16 +559,26 @@
 .B \-graft\-points
 Allow use of graft points for filenames. If this option is used, all
 filenames are checked for graft points. The filename is divided at the
 first unescaped equal sign. All occurrences of `\(rs' and `=' characters
 must be escaped with `\(rs' if
 .B \-graft\-points
 has been specified.
 .TP
+.B \-keep\-top\-dirs
+If a pathspec parameter refers to a directory, add it as a
+subdirectory of the root directory (do not merge the directory contents
+to the root directory). Option -keep-top-dirs cannot be combined with
+option -graft-points, it is a shortcut for a parameter list like
+-graft-points dir1=dir1 dir2=dir2 dir3=dir3 ...
+.IP
+Because '.' has a special meaning, the option is ignored when using '.'
+as a pathspec parameter.
+.TP
 .BI \-hide " glob"
 Hide any files matching
 .IR glob ,
 a shell wildcard pattern, from being seen in the ISO9660 or Rock Ridge
 directory.
 .I glob
 may match any part of the filename or path.  If
 .I glob
diff -r 643d90fc2314 genisoimage/genisoimage.c
--- a/genisoimage/genisoimage.c	Wed Dec 29 14:34:35 2021 +0100
+++ b/genisoimage/genisoimage.c	Thu Dec 30 00:08:05 2021 +0100
@@ -177,16 +177,17 @@
 int	volume_set_size = 1;
 int	volume_sequence_number = 1;

 struct eltorito_boot_entry_info *first_boot_entry = NULL;
 struct eltorito_boot_entry_info *last_boot_entry = NULL;
 struct eltorito_boot_entry_info *current_boot_entry = NULL;

 int	use_graft_ptrs;		/* Use graft points */
+int	keep_top_dirs;		/* Use graft points */
 int	jhide_trans_tbl;	/* Hide TRANS.TBL from Joliet tree */
 int	hide_rr_moved;		/* Name RR_MOVED .rr_moved in Rock Ridge tree */
 int	omit_period = 0;	/* Violates iso9660, but these are a pain */
 int	transparent_compression = 0; /* So far only works with linux */
 int	omit_version_number = 0; /* May violate iso9660, but noone uses vers */
 int	no_rr = 0;		/* Do not use RR attributes from old session */
 int	force_rr = 0;		/* Force to use RR attributes from old session */
 Uint	RR_relocation_depth = 6; /* Violates iso9660, but most systems work */
@@ -401,16 +402,18 @@
 #define	OPTION_XA_RATIONALIZED		1067

 #define	OPTION_SUNX86BOOT		1068
 #define	OPTION_SUNX86LABEL		1069

 #define	OPTION_ALLOW_LEADING_DOTS	1070
 #define	OPTION_PUBLISHER		1071

+#define OPTION_KEEP_TOP_DIRS		1072
+
 #ifdef		JIGDO_TEMPLATE
 #define	OPTION_JTT_OUTPUT		1101
 #define	OPTION_JTJ_OUTPUT		1102
 #define	OPTION_JT_MIN_SIZE		1103
 #define	OPTION_JT_PATH_MAP		1104
 #define	OPTION_JT_MD5_LIST		1105
 #define	OPTION_JT_INCLUDE		1106
 #define	OPTION_JT_EXCLUDE		1107
@@ -644,16 +647,18 @@
 	{{"path-list", required_argument, NULL, OPTION_P_LIST},
 	'\0', "FILE", "File with list of pathnames to process", ONE_DASH},
 	{{"preparer", required_argument, NULL, 'p'},
 	'p', "PREP", "Set Volume preparer", ONE_DASH},
 	{{"print-size", no_argument, NULL, OPTION_PRINT_SIZE},
 	'\0', NULL, "Print estimated filesystem size and exit", ONE_DASH},
 	{{"publisher", required_argument, NULL, OPTION_PUBLISHER},
 	'\0', "PUB", "Set Volume publisher", ONE_DASH},
+	{{"keep-top-dirs", no_argument, NULL, OPTION_KEEP_TOP_DIRS},
+	'\0', NULL, "If pathspec is a directory then keep it as a top level entry", ONE_DASH},
 	{{"publisher", required_argument, NULL, 'P'},
 	'P', "PUB", "Set Volume publisher", ONE_DASH},
 	{{"quiet", no_argument, NULL, OPTION_QUIET},
 	'\0', NULL, "Run quietly", ONE_DASH},
 	{{"rational-rock", no_argument, NULL, 'r'},
 	'r', NULL, "Generate rationalized Rock Ridge directory information", ONE_DASH},
 	{{"rock", no_argument, NULL, 'R'},
 	'R', NULL, "Generate Rock Ridge directory information", ONE_DASH},
@@ -877,16 +882,17 @@
 static	void	susage(int excode);
 static	void	usage(int excode);
 int	iso9660_date(char *result, time_t crtime);
 static	void	hide_reloc_dir(void);
 static	char *get_pnames(int argc, char **argv, int opt, char *pname,
 								  int pnsize, FILE *fp);
 char *findgequal(char *s);
 static	char *escstrcpy(char *to, char *from);
+static int all_chars_are_dot_or_pathsep(const char *str);
 void *e_malloc(size_t size);

 static int
 read_one_rcfile(char *filename)
 {
 	int linum = 0;
 	char linebuffer[256];
 	FILE *fp;
@@ -1354,16 +1360,19 @@
 			/* A filename that we take as input. */
 			optind--;
 			have_cmd_line_pathspec = 1;
 			goto parse_input_files;

 		case OPTION_USE_GRAFT:
 			use_graft_ptrs = 1;
 			break;
+		case OPTION_KEEP_TOP_DIRS:
+			keep_top_dirs = 1;
+			break;
 		case 'C':
 			/*
 			 * This is a temporary hack until cdrecord gets the
 			 * proper hooks in it.
 			 */
 			cdrecord_data = optarg;
 			break;
 		case OPTION_GUI:
@@ -2773,16 +2782,25 @@
 		comerrno(EX_BAD, "Can't have -prep-boot with -apple\n");
 #else
 		fprintf(stderr, "Can't have -prep-boot with -apple\n");
 		exit(1);
 #endif
 	}
 #endif	/* PREP_BOOT */

+	if (use_graft_ptrs && keep_top_dirs) {
+#ifdef	USE_LIBSCHILY
+		comerrno(EX_BAD, "Can't have -keep-top-dirs with -graft-points\n");
+#else
+		fprintf(stderr, "Can't have -keep-top-dirs with -graft-points\n");
+		exit(1);
+#endif
+	}
+
 	if (apple_hyb || apple_ext)
 		apple_both = 1;

 	if (probe)
 		/* we need to search for all types of Apple/Unix files */
 		hfs_select = ~0;

 	if (apple_both && verbose && !(hfs_select || *afpfile || magic_filename)) {
@@ -3287,40 +3305,55 @@
 			fprintf(stderr, "Invalid node - '%s'.\n", node);
 			exit(1);
 #endif
 		} else {
 			/*
 			 * Now see whether the user wants to add a regular
 			 * file or a directory at this point.
 			 */
-			if (S_ISDIR(st.st_mode)) {
+			int add_directory_contents = 0;
+
+			if (!keep_top_dirs && S_ISDIR(st.st_mode)) {
+				add_directory_contents = 1;
+			} else {
+				if (short_name == NULL) {
+					short_name = strrchr(node,
+							PATH_SEPARATOR);
+					if (short_name == NULL ||
+							short_name < node) {
+						short_name = node;
+					} else {
+						short_name++;
+					}
+				}
+				if (short_name != NULL && all_chars_are_dot_or_pathsep(short_name)) {
+				    /*
+				     * This happens if -keep-top-dirs is used
+				     * and . is used as a pathspec parameter.
+				     * In this case we don't have a printable short name,
+				     * so let's ignore -keep-top-dirs for the . parameter.
+				     */
+				    add_directory_contents = 1;
+				}
+			}
+			if (add_directory_contents) {
 				if (debug) {
 					fprintf(stderr, "graft_dir: '%s : %s', node: '%s', (scan)\n",
 						graft_dir->whole_name,
 						graft_dir->de_name, node);
 				}
 				if (!scan_directory_tree(graft_dir,
 								node, &de)) {
 					exit(1);
 				}
 				if (debug) {
 					fprintf(stderr, "scan done\n");
 				}
 			} else {
-				if (short_name == NULL) {
-					short_name = strrchr(node,
-							PATH_SEPARATOR);
-					if (short_name == NULL ||
-							short_name < node) {
-						short_name = node;
-					} else {
-						short_name++;
-					}
-				}
 				if (debug) {
 					fprintf(stderr, "graft_dir: '%s : %s', node: '%s', short_name: '%s'\n",
 						graft_dir->whole_name,
 						graft_dir->de_name, node,
 						short_name);
 				}
 #ifdef APPLE_HYB
 				if (!insert_file_entry(graft_dir, node,
@@ -3810,16 +3843,28 @@
 		}
 		p++;
 	}
 	if (debug)
 		fprintf(stderr, "ESC:  '%s'\n", to);
 	return (to);
 }

+static int
+all_chars_are_dot_or_pathsep(const char *str)
+{
+	while (*str) {
+	    if (*str != PATH_SEPARATOR && *str != '.') {
+		return 0;
+	    }
+	    ++str;
+	}
+	return 1;
+}
+
 void *
 e_malloc(size_t size)
 {
 	void		*pt = 0;

 	if ((size > 0) && ((pt = malloc(size)) == NULL)) {
 #ifdef	USE_LIBSCHILY
 		comerr("Not enough memory\n");

Reply via email to