/* Various processing of names.
- Copyright (C) 1988, 1992, 1994, 1996, 1997, 1998, 1999, 2000, 2001,
- 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+ Copyright 1988, 1992, 1994, 1996-2001, 2003-2007, 2009, 2013 Free
+ Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Public License for more details.
You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <system.h>
#include <fnmatch.h>
#include <hash.h>
#include <quotearg.h>
+#include <wordsplit.h>
#include "common.h"
\f
#define NELT_NAME 0 /* File name */
#define NELT_CHDIR 1 /* Change directory request */
#define NELT_FMASK 2 /* Change fnmatch options request */
-
+#define NELT_FILE 3 /* Read file names from that file */
+
struct name_elt /* A name_array element. */
{
char type; /* Element type, see NELT_* constants above */
{
const char *name; /* File or directory name */
int matching_flags;/* fnmatch options if type == NELT_FMASK */
+ struct
+ {
+ const char *name;/* File name */
+ int term; /* File name terminator in the list */
+ FILE *fp;
+ } file;
} v;
};
ep->v.name = name;
}
+void
+name_add_file (const char *name, int term)
+{
+ struct name_elt *ep;
+ check_name_alloc ();
+ ep = &name_array[entries++];
+ ep->type = NELT_FILE;
+ ep->v.file.name = name;
+ ep->v.file.term = term;
+ ep->v.file.fp = NULL;
+}
\f
/* Names from external name file. */
free (name_buffer);
free (name_array);
}
+\f
+/* Prevent recursive inclusion of the same file */
+struct file_id_list
+{
+ struct file_id_list *next;
+ ino_t ino;
+ dev_t dev;
+};
+
+static struct file_id_list *file_id_list;
+
+static void
+add_file_id (const char *filename)
+{
+ struct file_id_list *p;
+ struct stat st;
+
+ if (stat (filename, &st))
+ stat_fatal (filename);
+ for (p = file_id_list; p; p = p->next)
+ if (p->ino == st.st_ino && p->dev == st.st_dev)
+ {
+ FATAL_ERROR ((0, 0, _("%s: file list already read"),
+ quotearg_colon (filename)));
+ }
+ p = xmalloc (sizeof *p);
+ p->next = file_id_list;
+ p->ino = st.st_ino;
+ p->dev = st.st_dev;
+ file_id_list = p;
+}
+\f
+enum read_file_list_state /* Result of reading file name from the list file */
+ {
+ file_list_success, /* OK, name read successfully */
+ file_list_end, /* End of list file */
+ file_list_zero, /* Zero separator encountered where it should not */
+ file_list_skip /* Empty (zero-length) entry encountered, skip it */
+ };
+
+/* Read from FP a sequence of characters up to TERM and put them
+ into STK.
+ */
+static enum read_file_list_state
+read_name_from_file (struct name_elt *ent)
+{
+ int c;
+ size_t counter = 0;
+ FILE *fp = ent->v.file.fp;
+ int term = ent->v.file.term;
+
+ for (c = getc (fp); c != EOF && c != term; c = getc (fp))
+ {
+ if (counter == name_buffer_length)
+ name_buffer = x2realloc (name_buffer, &name_buffer_length);
+ name_buffer[counter++] = c;
+ if (c == 0)
+ {
+ /* We have read a zero separator. The file possibly is
+ zero-separated */
+ return file_list_zero;
+ }
+ }
+
+ if (counter == 0 && c != EOF)
+ return file_list_skip;
+
+ if (counter == name_buffer_length)
+ name_buffer = x2realloc (name_buffer, &name_buffer_length);
+ name_buffer[counter] = 0;
+
+ return (counter == 0 && c == EOF) ? file_list_end : file_list_success;
+}
+
+static int
+handle_option (const char *str)
+{
+ struct wordsplit ws;
+ int i;
+
+ while (*str && isspace (*str))
+ ;
+ if (*str != '-')
+ return 1;
+
+ ws.ws_offs = 1;
+ if (wordsplit (str, &ws, WRDSF_DEFFLAGS|WRDSF_DOOFFS))
+ FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"),
+ str, wordsplit_strerror (&ws)));
+ ws.ws_wordv[0] = program_invocation_short_name;
+ more_options (ws.ws_wordc+ws.ws_offs, ws.ws_wordv);
+ for (i = 0; i < ws.ws_wordc+ws.ws_offs; i++)
+ ws.ws_wordv[i] = NULL;
+
+ wordsplit_free (&ws);
+ return 0;
+}
+
+static int
+read_next_name (struct name_elt *ent, struct name_elt *ret)
+{
+ if (!ent->v.file.fp)
+ {
+ if (!strcmp (ent->v.file.name, "-"))
+ {
+ request_stdin ("-T");
+ ent->v.file.fp = stdin;
+ }
+ else
+ {
+ add_file_id (ent->v.file.name);
+ if ((ent->v.file.fp = fopen (ent->v.file.name, "r")) == NULL)
+ open_fatal (ent->v.file.name);
+ }
+ }
+
+ while (1)
+ {
+ switch (read_name_from_file (ent))
+ {
+ case file_list_skip:
+ continue;
+
+ case file_list_zero:
+ WARNOPT (WARN_FILENAME_WITH_NULS,
+ (0, 0, N_("%s: file name read contains nul character"),
+ quotearg_colon (ent->v.file.name)));
+ ent->v.file.term = 0;
+ /* fall through */
+ case file_list_success:
+ if (handle_option (name_buffer) == 0)
+ continue;
+ ret->type = NELT_NAME;
+ ret->v.name = name_buffer;
+ return 0;
+
+ case file_list_end:
+ if (strcmp (ent->v.file.name, "-"))
+ fclose (ent->v.file.fp);
+ ent->v.file.fp = NULL;
+ return 1;
+ }
+ }
+}
+\f
+static void
+copy_name (struct name_elt *ep)
+{
+ const char *source;
+ size_t source_len;
+ char *cursor;
+
+ source = ep->v.name;
+ source_len = strlen (source);
+ if (name_buffer_length < source_len)
+ {
+ do
+ {
+ name_buffer_length *= 2;
+ if (! name_buffer_length)
+ xalloc_die ();
+ }
+ while (name_buffer_length < source_len);
+
+ free (name_buffer);
+ name_buffer = xmalloc(name_buffer_length + 2);
+ }
+ strcpy (name_buffer, source);
+
+ /* Zap trailing slashes. */
+ cursor = name_buffer + strlen (name_buffer) - 1;
+ while (cursor > name_buffer && ISSLASH (*cursor))
+ *cursor-- = '\0';
+}
+\f
static int matching_flags; /* exclude_fnmatch options */
/* Get the next NELT_NAME element from name_array. Result is in
name_next_elt (int change_dirs)
{
static struct name_elt entry;
- const char *source;
- char *cursor;
while (scanned != entries)
{
struct name_elt *ep;
- size_t source_len;
- ep = &name_array[scanned++];
+ ep = &name_array[scanned];
if (ep->type == NELT_FMASK)
{
matching_flags = ep->v.matching_flags;
+ ++scanned;
continue;
}
- source = ep->v.name;
- source_len = strlen (source);
- if (name_buffer_length < source_len)
+ switch (ep->type)
{
- do
+ case NELT_FILE:
+ if (read_next_name (ep, &entry) == 0)
+ return &entry;
+ ++scanned;
+ continue;
+
+ case NELT_CHDIR:
+ if (change_dirs)
{
- name_buffer_length *= 2;
- if (! name_buffer_length)
- xalloc_die ();
+ ++scanned;
+ copy_name (ep);
+ if (chdir (name_buffer) < 0)
+ chdir_fatal (name_buffer);
+ break;
}
- while (name_buffer_length < source_len);
-
- free (name_buffer);
- name_buffer = xmalloc (name_buffer_length + 2);
- }
- strcpy (name_buffer, source);
-
- /* Zap trailing slashes. */
-
- cursor = name_buffer + strlen (name_buffer) - 1;
- while (cursor > name_buffer && ISSLASH (*cursor))
- *cursor-- = '\0';
-
- if (change_dirs && ep->type == NELT_CHDIR)
- {
- if (chdir (name_buffer) < 0)
- chdir_fatal (name_buffer);
- }
- else
- {
+ /* fall trhough */
+ case NELT_NAME:
+ ++scanned;
+ copy_name (ep);
if (unquote_option)
unquote_string (name_buffer);
entry.type = ep->type;
struct name *gnu_list_name;
struct name const *
-name_from_list ()
+name_from_list (void)
{
if (!gnu_list_name)
gnu_list_name = namelist;