[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Valgrind and Bison memory leaks
From: |
Joel E. Denny |
Subject: |
Valgrind and Bison memory leaks |
Date: |
Fri, 3 Nov 2006 05:14:09 -0500 (EST) |
I try to use Valgrind to make sure I don't create new memory leaks in
Bison. However, Valgrind's complaints about all the existing memory leaks
really get in the way.
I'd like to commit the following, which I believe is a significant
improvement. There's a lot going on in this patch, so it definitely
warrants another pair of eyes.
Most importantly, the end of the patch includes a small change to
lib/quotearg.*, which I see are part of gnulib. I'll need some guidance
on whether that change makes sense for gnulib in general and on what to do
with it.
Index: ChangeLog
===================================================================
RCS file: /sources/bison/bison/ChangeLog,v
retrieving revision 1.1598
diff -p -u -r1.1598 ChangeLog
--- ChangeLog 1 Nov 2006 06:09:40 -0000 1.1598
+++ ChangeLog 3 Nov 2006 10:05:48 -0000
@@ -1,3 +1,60 @@
+2006-11-03 Joel E. Denny <address@hidden>
+
+ Don't let Bison leak memory except when it complains.
+ * src/files.c (spec_verbose_file, spec_graph_file, spec_defines_file,
+ parser_file_name, all_but_ext, all_but_tab_ext, dir_prefix,
+ src_extension, header_extension): Remove const since they must be
+ freed.
+ (compute_exts_from_gf, compute_exts_from_src): Don't leak temporary
+ values.
+ (compute_file_name_parts, compute_output_file_names): Don't store
+ read-only data in variables that will be freed.
+ (compute_output_file_names): Free all_but_ext, all_but_tab_ext,
+ src_extension, and header_extension.
+ (output_file_names_free): New public function to free
+ spec_verbose_file, spec_graph_file, spec_defines_file,
+ parser_file_name, and dir_prefix.
+ * src/files.h: Update.
+ * src/getargs.c (getargs): Don't store read-only data in variables that
+ will be freed.
+ * src/main.c (main): Invoke output_file_names_free, code_scanner_free
+ (which previously existed but was unused), and quotearg_free.
+ * src/muscle_tab.c (muscle_entry): Rewrite the value field as a union
+ so it can store a `const char *' or a `char *'.
+ (muscle_entry_free): New private function for freeing a muscle_entry
+ and its value if it's a `char *'.
+ (muscle_init): Initialize muscle_table with muscle_entry_free instead
+ of free.
+ (muscle_insert): Create new entries with `const char *' values, and
+ aver that old retrieved entries have `const char *' values.
+ (muscle_grow): Likewise but `char *'.
+ (muscle_code_grow): Go ahead and free the string passed to muscle_grow
+ since it's not needed anymore.
+ (muscle_find): aver that the retrieved entry's value is a `char *'
+ since the only existing caller in Bison modifies the value and always
+ muscle_*grow's the value.
+ (muscle_m4_output): Use the value as a `const char *' regardless of
+ what kind it actually is.
+ * src/muscle_tab.h (muscle_insert): value arg is now a `const char *'.
+ * src/parse-gram.y (%union): Make `chars' field a `const char *', and
+ add a new `char *nonconst_chars' field.
+ ("{...}"): Declare semantic type as nonconst_chars.
+ * src/scan-code.h, scan-code.l (translate_rule_action,
+ translate_symbol_action, translate_code, translate_action): Return
+ `const char *' rather than `char *' since external code should not free
+ these strings.
+ * src/scan-gram.l: Used val->nonconst_chars for BRACED_CODE, which is
+ "{...}" in the parser.
+ * tests/Makefile.am (maintainer-check-valgrind): Set
+ VALGRIND_OPTS='--leak-check=full --show-reacheable=yes' before invoking
+ Valgrind.
+ * tests/calc.at (_AT_DATA_CALC_Y): fclose the FILE* so Valgrind doesn't
+ complain.
+ * tests/testsuite.at (AT_CHECK): Redefine so that running Bison and
+ expecting a non-zero exit status sets --leak-check=summary and
+ --show-reachable=no for Valgrind. Bison unabashedly leaks memory in
+ this case, and we don't want to hear about it.
+
2006-10-31 Joel E. Denny <address@hidden>
Disable unset/unused mid-rule value warnings by default, and recognize
Index: src/files.c
===================================================================
RCS file: /sources/bison/bison/src/files.c,v
retrieving revision 1.97
diff -p -u -r1.97 files.c
--- src/files.c 12 Oct 2006 23:29:52 -0000 1.97
+++ src/files.c 3 Nov 2006 10:05:48 -0000
@@ -48,10 +48,10 @@ struct obstack post_prologue_obstack;
char const *spec_outfile = NULL; /* for -o. */
char const *spec_file_prefix = NULL; /* for -b. */
char const *spec_name_prefix = NULL; /* for -p. */
-char const *spec_verbose_file = NULL; /* for --verbose. */
-char const *spec_graph_file = NULL; /* for -g. */
-char const *spec_defines_file = NULL; /* for --defines. */
-char const *parser_file_name;
+char *spec_verbose_file = NULL; /* for --verbose. */
+char *spec_graph_file = NULL; /* for -g. */
+char *spec_defines_file = NULL; /* for --defines. */
+char *parser_file_name;
uniqstr grammar_file = NULL;
uniqstr current_file = NULL;
@@ -72,14 +72,14 @@ uniqstr current_file = NULL;
empty string (meaning the current directory); otherwise it is
`dir/'. */
-static char const *all_but_ext;
-static char const *all_but_tab_ext;
-char const *dir_prefix;
+static char *all_but_ext;
+static char *all_but_tab_ext;
+char *dir_prefix;
/* C source file extension (the parser source). */
-static char const *src_extension = NULL;
+static char *src_extension = NULL;
/* Header file extension (if option ``-d'' is specified). */
-static char const *header_extension = NULL;
+static char *header_extension = NULL;
/*-----------------------------------------------------------------.
| Return a newly allocated string composed of the concatenation of |
@@ -157,10 +157,13 @@ tr (const char *in, char from, char to)
static void
compute_exts_from_gf (const char *ext)
{
- src_extension = tr (ext, 'y', 'c');
- src_extension = tr (src_extension, 'Y', 'C');
- header_extension = tr (ext, 'y', 'h');
- header_extension = tr (header_extension, 'Y', 'H');
+ char *tmp;
+ tmp = tr (ext, 'y', 'c');
+ src_extension = tr (tmp, 'Y', 'C');
+ free (tmp);
+ tmp = tr (ext, 'y', 'h');
+ header_extension = tr (tmp, 'Y', 'H');
+ free (tmp);
}
/* Compute extensions from the given c source file extension. */
@@ -170,9 +173,11 @@ compute_exts_from_src (const char *ext)
/* We use this function when the user specifies `-o' or `--output',
so the extenions must be computed unconditionally from the file name
given by this option. */
+ char *tmp;
src_extension = xstrdup (ext);
- header_extension = tr (ext, 'c', 'h');
- header_extension = tr (header_extension, 'C', 'H');
+ tmp = tr (ext, 'c', 'h');
+ header_extension = tr (tmp, 'C', 'H');
+ free (tmp);
}
@@ -270,14 +275,14 @@ compute_file_name_parts (void)
else if (yacc_flag)
{
/* If --yacc, then the output is `y.tab.c'. */
- dir_prefix = "";
- all_but_tab_ext = "y";
+ dir_prefix = xstrdup ("");
+ all_but_tab_ext = xstrdup ("y");
}
else
{
/* Otherwise, ALL_BUT_TAB_EXT is computed from the input
grammar: `foo/bar.yy' => `bar'. */
- dir_prefix = "";
+ dir_prefix = xstrdup ("");
all_but_tab_ext =
xstrndup (base, (strlen (base) - (ext ? strlen (ext) : 0)));
}
@@ -306,12 +311,13 @@ compute_output_file_names (void)
/* If not yet done. */
if (!src_extension)
- src_extension = ".c";
+ src_extension = xstrdup (".c");
if (!header_extension)
- header_extension = ".h";
+ header_extension = xstrdup (".h");
name[names++] = parser_file_name =
- spec_outfile ? spec_outfile : concat2 (all_but_ext, src_extension);
+ spec_outfile
+ ? xstrdup (spec_outfile) : concat2 (all_but_ext, src_extension);
if (defines_flag)
{
@@ -337,4 +343,19 @@ compute_output_file_names (void)
for (i = 0; i < j; i++)
if (strcmp (name[i], name[j]) == 0)
warn (_("conflicting outputs to file %s"), quote (name[i]));
+
+ free (all_but_ext);
+ free (all_but_tab_ext);
+ free (src_extension);
+ free (header_extension);
+}
+
+void
+output_file_names_free (void)
+{
+ free (spec_verbose_file);
+ free (spec_graph_file);
+ free (spec_defines_file);
+ free (parser_file_name);
+ free (dir_prefix);
}
Index: src/files.h
===================================================================
RCS file: /sources/bison/bison/src/files.h,v
retrieving revision 1.41
diff -p -u -r1.41 files.h
--- src/files.h 12 Oct 2006 23:29:52 -0000 1.41
+++ src/files.h 3 Nov 2006 10:05:48 -0000
@@ -29,7 +29,7 @@
extern char const *spec_outfile;
/* File name for the parser (i.e., the one above, or its default.) */
-extern char const *parser_file_name;
+extern char *parser_file_name;
/* Symbol prefix specified with -p, or 0 if no -p. */
extern const char *spec_name_prefix;
@@ -38,16 +38,16 @@ extern const char *spec_name_prefix;
extern char const *spec_file_prefix;
/* --verbose. */
-extern char const *spec_verbose_file;
+extern char *spec_verbose_file;
/* File name specified for the output graph. */
-extern char const *spec_graph_file;
+extern char *spec_graph_file;
/* File name specified with --defines. */
-extern char const *spec_defines_file;
+extern char *spec_defines_file;
/* Directory prefix of output file names. */
-extern char const *dir_prefix;
+extern char *dir_prefix;
/* If semantic parser, output a .h file that defines YYSTYPE... */
@@ -63,6 +63,7 @@ extern uniqstr grammar_file;
extern uniqstr current_file;
void compute_output_file_names (void);
+void output_file_names_free (void);
FILE *xfopen (const char *name, const char *mode);
void xfclose (FILE *ptr);
Index: src/getargs.c
===================================================================
RCS file: /sources/bison/bison/src/getargs.c,v
retrieving revision 1.84
diff -p -u -r1.84 getargs.c
--- src/getargs.c 1 Nov 2006 06:09:40 -0000 1.84
+++ src/getargs.c 3 Nov 2006 10:05:48 -0000
@@ -408,7 +408,7 @@ getargs (int argc, char *argv[])
/* Here, the -g and --graph=FILE options are differentiated. */
graph_flag = true;
if (optarg)
- spec_graph_file = AS_FILE_NAME (optarg);
+ spec_graph_file = xstrdup (AS_FILE_NAME (optarg));
break;
case 'h':
@@ -426,7 +426,7 @@ getargs (int argc, char *argv[])
/* Here, the -d and --defines options are differentiated. */
defines_flag = true;
if (optarg)
- spec_defines_file = AS_FILE_NAME (optarg);
+ spec_defines_file = xstrdup (AS_FILE_NAME (optarg));
break;
case 'k':
Index: src/main.c
===================================================================
RCS file: /sources/bison/bison/src/main.c,v
retrieving revision 1.90
diff -p -u -r1.90 main.c
--- src/main.c 12 Oct 2006 23:29:52 -0000 1.90
+++ src/main.c 3 Nov 2006 10:05:48 -0000
@@ -26,6 +26,7 @@
#include <bitset_stats.h>
#include <bitset.h>
#include <configmake.h>
+#include <quotearg.h>
#include <timevar.h>
#include "LR0.h"
@@ -43,6 +44,7 @@
#include "print_graph.h"
#include "reader.h"
#include "reduce.h"
+#include "scan-code.h"
#include "scan-gram.h"
#include "symtab.h"
#include "tables.h"
@@ -168,12 +170,15 @@ main (int argc, char *argv[])
reduce_free ();
conflicts_free ();
grammar_free ();
+ output_file_names_free ();
/* The scanner memory cannot be released right after parsing, as it
contains things such as user actions, prologue, epilogue etc. */
gram_scanner_free ();
muscle_free ();
uniqstrs_free ();
+ code_scanner_free ();
+ quotearg_free ();
timevar_pop (TV_FREE);
if (trace_flag & trace_bitsets)
Index: src/muscle_tab.c
===================================================================
RCS file: /sources/bison/bison/src/muscle_tab.c,v
retrieving revision 1.39
diff -p -u -r1.39 muscle_tab.c
--- src/muscle_tab.c 3 Jan 2006 20:19:41 -0000 1.39
+++ src/muscle_tab.c 3 Nov 2006 10:05:48 -0000
@@ -33,7 +33,11 @@
typedef struct
{
const char *key;
- char *value;
+ enum { CONST_VALUE, FREEABLE_VALUE } kind;
+ union {
+ const char *const_value;
+ char *freeable_value;
+ } value;
} muscle_entry;
/* An obstack used to create some entries. */
@@ -64,6 +68,15 @@ hash_muscle (const void *x, size_t table
| Also set up the MUSCLE_OBSTACK. |
`-----------------------------------------------------------------*/
+static void
+muscle_entry_free (void *entry)
+{
+ muscle_entry *mentry = entry;
+ if (mentry->kind == FREEABLE_VALUE)
+ free (mentry->value.freeable_value);
+ free (mentry);
+}
+
void
muscle_init (void)
{
@@ -71,7 +84,7 @@ muscle_init (void)
obstack_init (&muscle_obstack);
muscle_table = hash_initialize (HT_INITIAL_CAPACITY, NULL, hash_muscle,
- hash_compare_muscles, free);
+ hash_compare_muscles, muscle_entry_free);
/* Version and input file. */
MUSCLE_INSERT_STRING ("version", VERSION);
@@ -98,7 +111,7 @@ muscle_free (void)
`------------------------------------------------------------*/
void
-muscle_insert (const char *key, char *value)
+muscle_insert (const char *key, const char *value)
{
muscle_entry probe;
muscle_entry *entry;
@@ -112,8 +125,11 @@ muscle_insert (const char *key, char *va
entry = xmalloc (sizeof *entry);
entry->key = key;
hash_insert (muscle_table, entry);
+ entry->kind = CONST_VALUE;
}
- entry->value = value;
+ else
+ aver (entry->kind == CONST_VALUE);
+ entry->value.const_value = value;
}
@@ -138,19 +154,21 @@ muscle_grow (const char *key, const char
entry = xmalloc (sizeof *entry);
entry->key = key;
hash_insert (muscle_table, entry);
- entry->value = xstrdup (val);
+ entry->kind = FREEABLE_VALUE;
+ entry->value.freeable_value = xstrdup (val);
}
else
{
/* Grow the current value. */
char *new_val;
- obstack_sgrow (&muscle_obstack, entry->value);
- free (entry->value);
+ aver (entry->kind == FREEABLE_VALUE);
+ obstack_sgrow (&muscle_obstack, entry->value.freeable_value);
+ free (entry->value.freeable_value);
obstack_sgrow (&muscle_obstack, separator);
obstack_sgrow (&muscle_obstack, val);
obstack_1grow (&muscle_obstack, 0);
new_val = obstack_finish (&muscle_obstack);
- entry->value = xstrdup (new_val);
+ entry->value.freeable_value = xstrdup (new_val);
obstack_free (&muscle_obstack, new_val);
}
}
@@ -173,6 +191,7 @@ muscle_code_grow (const char *key, const
obstack_1grow (&muscle_obstack, 0);
extension = obstack_finish (&muscle_obstack);
muscle_grow (key, extension, "");
+ obstack_free (&muscle_obstack, extension);
}
@@ -206,7 +225,12 @@ muscle_find (const char *key)
probe.key = key;
result = hash_lookup (muscle_table, &probe);
- return result ? result->value : NULL;
+ if (result)
+ {
+ aver (result->kind == FREEABLE_VALUE);
+ return result->value.freeable_value;
+ }
+ return NULL;
}
@@ -218,7 +242,8 @@ static inline bool
muscle_m4_output (muscle_entry *entry, FILE *out)
{
fprintf (out, "m4_define([b4_%s],\n", entry->key);
- fprintf (out, "[[%s]])\n\n\n", entry->value);
+ /* Use the value as a CONST_VALUE regardless of what kind it actually is. */
+ fprintf (out, "[[%s]])\n\n\n", entry->value.const_value);
return true;
}
Index: src/muscle_tab.h
===================================================================
RCS file: /sources/bison/bison/src/muscle_tab.h,v
retrieving revision 1.17
diff -p -u -r1.17 muscle_tab.h
--- src/muscle_tab.h 8 Oct 2006 11:07:02 -0000 1.17
+++ src/muscle_tab.h 3 Nov 2006 10:05:48 -0000
@@ -24,7 +24,7 @@
# include "location.h"
void muscle_init (void);
-void muscle_insert (const char *key, char *value);
+void muscle_insert (const char *key, const char *value);
char *muscle_find (const char *key);
void muscle_free (void);
Index: src/parse-gram.y
===================================================================
RCS file: /sources/bison/bison/src/parse-gram.y,v
retrieving revision 1.95
diff -p -u -r1.95 parse-gram.y
--- src/parse-gram.y 21 Oct 2006 10:03:35 -0000 1.95
+++ src/parse-gram.y 3 Nov 2006 10:05:49 -0000
@@ -99,7 +99,8 @@ static int current_prec = 0;
symbol *symbol;
symbol_list *list;
int integer;
- char *chars;
+ char *nonconst_chars;
+ char const *chars;
assoc assoc;
uniqstr uniqstr;
unsigned char character;
@@ -183,7 +184,8 @@ static int current_prec = 0;
/* braceless is not to be used for rule or symbol actions, as it
calls translate_code. */
-%type <chars> STRING "{...}" "%{...%}" EPILOGUE braceless content content.opt
+%type <chars> STRING "%{...%}" EPILOGUE braceless content content.opt
+%type <nonconst_chars> "{...}"
%printer { fputs (quotearg_style (c_quoting_style, $$), stderr); }
STRING
%printer { fprintf (stderr, "{\n%s\n}", $$); }
Index: src/scan-code.h
===================================================================
RCS file: /sources/bison/bison/src/scan-code.h,v
retrieving revision 1.3
diff -p -u -r1.3 scan-code.h
--- src/scan-code.h 13 Jul 2006 08:12:00 -0000 1.3
+++ src/scan-code.h 3 Nov 2006 10:05:49 -0000
@@ -35,13 +35,13 @@ void code_scanner_free (void);
/* The action of the rule R contains $$, $1 etc. referring to the values
of the rule R. */
-char *translate_rule_action (symbol_list *r);
+const char *translate_rule_action (symbol_list *r);
/* The action A refers to $$ and @$ only, referring to a symbol. */
-char *translate_symbol_action (const char *a, location l);
+const char *translate_symbol_action (const char *a, location l);
/* The action contains no special escapes, just protect M4 special
symbols. */
-char *translate_code (const char *a, location l);
+const char *translate_code (const char *a, location l);
#endif /* !SCAN_CODE_H_ */
Index: src/scan-code.l
===================================================================
RCS file: /sources/bison/bison/src/scan-code.l,v
retrieving revision 1.13
diff -p -u -r1.13 scan-code.l
--- src/scan-code.l 15 Sep 2006 16:34:48 -0000 1.13
+++ src/scan-code.l 3 Nov 2006 10:05:49 -0000
@@ -372,7 +372,7 @@ handle_action_at (symbol_list *rule, cha
translation is for \a rule, in the context \a sc_context
(SC_RULE_ACTION, SC_SYMBOL_ACTION, INITIAL). */
-static char *
+static const char *
translate_action (int sc_context, symbol_list *rule, const char *a, location l)
{
char *res;
@@ -394,20 +394,20 @@ translate_action (int sc_context, symbol
return res;
}
-char *
+const char *
translate_rule_action (symbol_list *rule)
{
return translate_action (SC_RULE_ACTION, rule, rule->action,
rule->action_location);
}
-char *
+const char *
translate_symbol_action (const char *a, location l)
{
return translate_action (SC_SYMBOL_ACTION, NULL, a, l);
}
-char *
+const char *
translate_code (const char *a, location l)
{
return translate_action (INITIAL, NULL, a, l);
Index: src/scan-gram.l
===================================================================
RCS file: /sources/bison/bison/src/scan-gram.l,v
retrieving revision 1.106
diff -p -u -r1.106 scan-gram.l
--- src/scan-gram.l 21 Oct 2006 10:03:35 -0000 1.106
+++ src/scan-gram.l 3 Nov 2006 10:05:49 -0000
@@ -523,7 +523,7 @@ splice (\\[ \f\t\v]*\n)*
{
STRING_FINISH;
loc->start = code_start;
- val->chars = last_string;
+ val->nonconst_chars = last_string;
BEGIN INITIAL;
return BRACED_CODE;
}
@@ -537,7 +537,7 @@ splice (\\[ \f\t\v]*\n)*
unexpected_eof (code_start, "}");
STRING_FINISH;
loc->start = code_start;
- val->chars = last_string;
+ val->nonconst_chars = last_string;
BEGIN INITIAL;
return BRACED_CODE;
}
Index: tests/Makefile.am
===================================================================
RCS file: /sources/bison/bison/tests/Makefile.am,v
retrieving revision 1.42
diff -p -u -r1.42 Makefile.am
--- tests/Makefile.am 6 Mar 2006 08:29:22 -0000 1.42
+++ tests/Makefile.am 3 Nov 2006 10:05:49 -0000
@@ -88,6 +88,7 @@ maintainer-check-posix: $(TESTSUITE)
.PHONY: maintainer-check-valgrind
maintainer-check-valgrind: $(TESTSUITE)
test -z '$(VALGRIND)' || \
+ VALGRIND_OPTS='--leak-check=full --show-reachable=yes' \
$(TESTSUITE) PREBISON='$(VALGRIND) -q' PREPARSER='$(VALGRIND) -q'
.PHONY: maintainer-check
Index: tests/calc.at
===================================================================
RCS file: /sources/bison/bison/tests/calc.at,v
retrieving revision 1.92
diff -p -u -r1.92 calc.at
--- tests/calc.at 15 Sep 2006 15:56:26 -0000 1.92
+++ tests/calc.at 3 Nov 2006 10:05:49 -0000
@@ -337,6 +337,7 @@ main (int argc, const char **argv)
]AT_SKEL_CC_IF([], [m4_bmatch([$4], [%debug],
[ yydebug = 1;])])[
status = yyparse (]AT_PARAM_IF([&result, &count])[);
+ fclose (input);
if (global_result != result)
abort ();
if (global_count != count)
Index: tests/testsuite.at
===================================================================
RCS file: /sources/bison/bison/tests/testsuite.at,v
retrieving revision 1.28
diff -p -u -r1.28 testsuite.at
--- tests/testsuite.at 14 May 2005 06:49:48 -0000 1.28
+++ tests/testsuite.at 3 Nov 2006 10:05:50 -0000
@@ -19,6 +19,14 @@
# 02110-1301, USA.
+# Bison often leaks memory when its exit status is non-zero, so set
+# --leak-check=summary for Valgrind in that case.
+m4_pushdef([ORIGINAL_AT_CHECK], m4_defn([AT_CHECK]))
+m4_pushdef([AT_CHECK],
+[ORIGINAL_AT_CHECK(m4_if(m4_quote(m4_substr(m4_quote($1), 0, 5)), [bison],
+ m4_if([$2], [0], [], [], [],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS
--leak-check=summary --show-reachable=no" ]]))$@)])
+
# Testing resistance to user bugs.
m4_include([input.at])
@@ -64,3 +72,6 @@ m4_include([c++.at])
m4_include([cxx-type.at])
# Regression tests
m4_include([glr-regression.at])
+
+m4_popdef([AT_CHECK])
+m4_popdef([ORIGINAL_AT_CHECK])
Index: gnulib/lib/quotearg.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/quotearg.c,v
retrieving revision 1.51
diff -p -u -r1.51 quotearg.c
--- gnulib/lib/quotearg.c 31 Oct 2006 21:51:45 -0000 1.51
+++ gnulib/lib/quotearg.c 3 Nov 2006 10:05:51 -0000
@@ -590,6 +590,17 @@ quotearg_n_options (int n, char const *a
static struct slotvec *slotvec = &slotvec0;
struct slotvec *sv = slotvec;
+ if (arg == NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < nslots; i += 1)
+ if (slotvec[i].val != slot0)
+ free (slotvec[i].val);
+ if (slotvec != &slotvec0)
+ free (slotvec);
+ return NULL;
+ }
+
if (n < 0)
abort ();
@@ -689,3 +700,9 @@ quotearg_colon (char const *arg)
{
return quotearg_char (arg, ':');
}
+
+void
+quotearg_free (void)
+{
+ quotearg_n_options (0, NULL, 0, NULL);
+}
Index: gnulib/lib/quotearg.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/quotearg.h,v
retrieving revision 1.12
diff -p -u -r1.12 quotearg.h
--- gnulib/lib/quotearg.h 14 May 2005 06:03:58 -0000 1.12
+++ gnulib/lib/quotearg.h 3 Nov 2006 10:05:51 -0000
@@ -134,4 +134,7 @@ char *quotearg_char (char const *arg, ch
/* Equivalent to quotearg_char (ARG, ':'). */
char *quotearg_colon (char const *arg);
+/* Free any allocated memory. */
+void quotearg_free (void);
+
#endif /* !QUOTEARG_H_ */
- Valgrind and Bison memory leaks,
Joel E. Denny <=