[m-rev.] incomplete diff: allow quotes in mdb commands

Mark Brown dougl at cs.mu.OZ.AU
Fri Feb 22 17:52:27 AEDT 2002


On 21-Feb-2002, Mark Brown <dougl at cs.mu.OZ.AU> wrote:
> This is in response to David Overton's bug report.  I've only written
> the documentation so far, but I thought I'd invite comment on that
> now before starting on the code.
> 

Here is the full diff for review.

Cheers,
Mark.

Estimated hours taken: 2.5
Branches: main

Allow mdb commands to quote words so that they can contain whitespace,
and allow characters with special meaning to mdb to be escaped.

trace/mercury_trace_internal.c:
	Implement the new feature.

doc/user_guide.texi:
	Document the new feature.

tests/debugger/Mmakefile:
tests/debugger/cmd_quote.exp:
tests/debugger/cmd_quote.inp:
tests/debugger/cmd_quote.m:
	Test case.

Index: doc/user_guide.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/user_guide.texi,v
retrieving revision 1.292
diff -u -r1.292 user_guide.texi
--- doc/user_guide.texi	12 Feb 2002 16:36:10 -0000	1.292
+++ doc/user_guide.texi	21 Feb 2002 13:32:59 -0000
@@ -1820,10 +1820,21 @@
 
 When the debugger (as opposed to the program being debugged) is interacting
 with the user, the debugger prints a prompt and reads in a line of text,
-which it will interpret as its next command. Each command line consists
-of several words separated by white space. The first word is the name of
-the command, while any other words give options and/or parameters to the
-command.
+which it will interpret as its next command line.
+A command line consists of a single command,
+or several commands separated by semicolons.
+Each command consists of several words separated by white space.
+The first word is the name of the command,
+while any other words give options and/or parameters to the command.
+
+A word may itself contain semicolons or whitespace if it is
+enclosed in single quotes (').
+This is useful for commands that have other commands as parameters,
+for example @samp{view -w 'xterm -e'}.
+Characters that have special meaning to @samp{mdb} will be treated like
+ordinary charaters if they are escaped with a backslash (\).
+It is possible to escape single quotes, whitespace, semicolons, newlines
+and the escape character itself.
 
 Some commands take a number as their first parameter.
 For such commands, users can type `@var{number} @var{command}'
Index: tests/debugger/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/debugger/Mmakefile,v
retrieving revision 1.62
diff -u -r1.62 Mmakefile
--- tests/debugger/Mmakefile	20 Feb 2002 03:14:44 -0000	1.62
+++ tests/debugger/Mmakefile	21 Feb 2002 20:35:35 -0000
@@ -27,6 +27,7 @@
 NONRETRY_PROGS = \
 	breakpoints			\
 	browse_pretty			\
+	cmd_quote			\
 	debugger_regs			\
 	exception_cmd			\
 	exception_value			\
@@ -120,6 +121,12 @@
 browser_test.out: browser_test browser_test.inp
 	$(MDB) ./browser_test < browser_test.inp 2>&1 | \
 		sed 's/io.m:[0-9]*/io.m:NNNN/g' > browser_test.out 2>&1
+
+# We need to pipe the output through sed to avoid hard-coding dependencies on
+# particular line numbers in the standard library source code.
+cmd_quote.out: cmd_quote cmd_quote.inp
+	$(MDB) ./cmd_quote < cmd_quote.inp 2>&1 | \
+		sed 's/io.m:[0-9]*/io.m:NNNN/g' > cmd_quote.out 2>&1
 
 debugger_regs.out: debugger_regs debugger_regs.inp
 	$(MDB) ./debugger_regs < debugger_regs.inp > debugger_regs.out 2>&1
Index: tests/debugger/cmd_quote.exp
===================================================================
RCS file: tests/debugger/cmd_quote.exp
diff -N tests/debugger/cmd_quote.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/debugger/cmd_quote.exp	21 Feb 2002 20:54:02 -0000
@@ -0,0 +1,41 @@
+       1:      1  1 CALL pred cmd_quote:main/2-0 (det) cmd_quote.m:7
+mdb> echo on
+Command echo enabled.
+mdb> print \ foo
+mdb: there is no such variable.
+mdb> print \\
+mdb: there is no such variable.
+mdb> print 'foo'
+mdb: there is no such variable.
+mdb> print '\''
+mdb: there is no such variable.
+mdb> print ''''
+mdb: there is no such variable.
+mdb> print \
+> foo \
+> bar \
+> baz
+mdb: print: usage error -- type `help print' for help.
+mdb> print 'foo
+> bar
+> baz '
+mdb: there is no such variable.
+mdb> print '
+> foo \
+> bar \'
+> '
+mdb: there is no such variable.
+mdb> \\\
+> \\\\
+Unknown command `\'. Give the command `help' for help.
+mdb> \\\\\\\\
+Unknown command `\\\\'. Give the command `help' for help.
+mdb> \'\'\'\'
+Unknown command `'''''. Give the command `help' for help.
+mdb> 'foo\''
+Unknown command `foo''. Give the command `help' for help.
+mdb> \ \ \ .
+Unknown command `   .'. Give the command `help' for help.
+mdb> '\ \\\''
+Unknown command ` \''. Give the command `help' for help.
+mdb> quit -y
Index: tests/debugger/cmd_quote.inp
===================================================================
RCS file: tests/debugger/cmd_quote.inp
diff -N tests/debugger/cmd_quote.inp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/debugger/cmd_quote.inp	21 Feb 2002 20:54:02 -0000
@@ -0,0 +1,25 @@
+echo on
+print \ foo
+print \\
+print 'foo'
+print '\''
+print ''''
+print \
+foo \
+bar \
+baz
+print 'foo
+bar
+baz '
+print '
+foo \
+bar \'
+'
+\\\
+\\\\
+\\\\\\\\
+\'\'\'\'
+'foo\''
+\ \ \ .
+'\ \\\''
+quit -y
Index: tests/debugger/cmd_quote.m
===================================================================
RCS file: tests/debugger/cmd_quote.m
diff -N tests/debugger/cmd_quote.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/debugger/cmd_quote.m	21 Feb 2002 20:37:49 -0000
@@ -0,0 +1,9 @@
+:- module cmd_quote.
+:- interface.
+:- import_module io.
+:- pred main(io__state::di, io__state::uo) is det.
+:- implementation.
+
+main -->
+	io__write_string("Hello world!\n").
+
Index: trace/mercury_trace_internal.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_internal.c,v
retrieving revision 1.116
diff -u -r1.116 mercury_trace_internal.c
--- trace/mercury_trace_internal.c	21 Feb 2002 01:53:29 -0000	1.116
+++ trace/mercury_trace_internal.c	22 Feb 2002 06:45:06 -0000
@@ -68,6 +68,10 @@
   #include <sys/stropts.h>
 #endif
 
+/* Special characters used in mdb commands. */
+#define MR_MDB_QUOTE_CHAR	'\''
+#define MR_MDB_ESCAPE_CHAR	'\\'
+
 /* The initial size of arrays of words. */
 #define	MR_INIT_WORD_COUNT	20
 
@@ -270,11 +274,13 @@
 			char ***words, int *word_max, int *word_count);
 static	int	MR_trace_break_into_words(char *line,
 			char ***words_ptr, int *word_max_ptr);
+static	int	MR_trace_break_off_one_word(char *line, int char_pos);
 static	void	MR_trace_expand_aliases(char ***words,
 			int *word_max, int *word_count);
 static	MR_bool	MR_trace_source(const char *filename, MR_bool ignore_errors);
 static	void	MR_trace_source_from_open_file(FILE *fp);
 static	char	*MR_trace_getline_queue(void);
+static	MR_bool	MR_trace_continue_line(char *ptr, MR_bool *quoted);
 static	void	MR_insert_line_at_head(const char *line);
 static	void	MR_insert_line_at_tail(const char *line);
 
@@ -3600,9 +3606,11 @@
 }
 
 /*
-** Given a text line, break it up into words composed of non-space characters
-** separated by space characters. Make each word a NULL-terminated string,
-** overwriting some spaces in the line array in the process.
+** Given a text line, break it up into words.  Words are composed of
+** non-space characters separated by space characters, except where
+** quotes (') or escapes (\) change the treatment of characters. Make
+** each word a NULL-terminated string, and remove the quotes and escapes,
+** overwriting some parts of the line array in the process.
 **
 ** On return *words will point to an array of strings, with space for
 ** *words_max strings. The number of strings filled in will be given by
@@ -3639,18 +3647,57 @@
 		MR_ensure_big_enough(token_number, word, char *,
 			MR_INIT_WORD_COUNT);
 		words[token_number] = line + char_pos;
+		char_pos = MR_trace_break_off_one_word(line, char_pos);
 
-		while (line[char_pos] != '\0' && !MR_isspace(line[char_pos])) {
-			char_pos++;
+		token_number++;
+	}
+}
+
+static int
+MR_trace_break_off_one_word(char *line, int char_pos)
+{
+	int		lag = 0;
+	MR_bool		quoted = MR_FALSE;
+	MR_bool		another = MR_FALSE;
+
+	while (line[char_pos] != '\0')
+	{
+		if (!quoted && MR_isspace(line[char_pos])) {
+			another = MR_TRUE;
+			break;
 		}
+		if (line[char_pos] == MR_MDB_QUOTE_CHAR) {
+			lag++;
+			char_pos++;
+			quoted = !quoted;
+		} else {
+			if (line[char_pos] == MR_MDB_ESCAPE_CHAR) {
+				lag++;
+				char_pos++;
+				if (line[char_pos] == '\0') {
+					MR_fatal_error(
+						"MR_trace_break_off_one_word: "
+						"unhandled backslash");
+				}
+			}
 
-		if (line[char_pos] != '\0') {
-			line[char_pos] = '\0';
+			if (lag) {
+				line[char_pos - lag] = line[char_pos];
+			}
 			char_pos++;
 		}
+	}
 
-		token_number++;
+	if (quoted) {
+		MR_fatal_error("MR_trace_break_off_one_word: unmatched quote");
+	}
+
+	line[char_pos - lag] = '\0';
+	if (another) {
+		char_pos++;
 	}
+
+	return char_pos;
 }
 
 static void
@@ -3726,15 +3773,20 @@
 ** Call MR_trace_getline to get the next line of input, then do some
 ** further processing.  If the input has reached EOF, return the command
 ** "quit".  If the line contains multiple commands then split it and
-** only return the first one.  The command is returned in a MR_malloc'd
-** buffer.
+** only return the first one.  If the newline at the end is either quoted
+** or escaped, read another line (using the prompt '>') and append it to
+** the first.  The command is returned in a MR_malloc'd buffer.
 */
 
 char *
 MR_trace_get_command(const char *prompt, FILE *mdb_in, FILE *mdb_out)
 {
 	char		*line;
-	char		*semicolon;
+	char		*ptr;
+	char		*cmd_chars;
+	int		cmd_char_max;
+	MR_bool		quoted;
+	int		len, extra_len;
 
 	line = MR_trace_getline(prompt, mdb_in, mdb_out);
 
@@ -3745,19 +3797,31 @@
 		** specially in the command interpreter.
 		*/
 		line = MR_copy_string("quit");
+		return line;
 	}
 
-	if ((semicolon = strchr(line, ';')) != NULL) {
+	len = strlen(line);
+	ptr = line;
+	cmd_chars = line;
+	cmd_char_max = len + 1;
+	quoted = MR_FALSE;
+	while (MR_trace_continue_line(ptr, &quoted)) {
 		/*
-		** The line contains at least two commands.
-		** Return only the first command now; put the others
-		** back in the input to be processed later.
+		** We were inside quotes when the end of the line was
+		** reached, or the newline was escaped, so input continues
+		** on the next line.  We append it to the first line,
+		** allocating more space if necessary.
 		*/
-		*semicolon = '\0';
-		MR_insert_line_at_head(MR_copy_string(semicolon + 1));
+		line = MR_trace_getline("> ", mdb_in, mdb_out);
+		extra_len = strlen(line);
+		/* cmd_char_max is always > 0 */
+		MR_ensure_big_enough(len + extra_len + 1, cmd_char, char, 0);
+		ptr = cmd_chars + len;
+		strcpy(ptr, line);
+		len = len + extra_len;
 	}
 
-	return line;
+	return cmd_chars;
 }
 
 /*
@@ -3855,6 +3919,51 @@
 		MR_line_tail->MR_line_next = line;
 		MR_line_tail = line;
 	}
+}
+
+/*
+** This returns MR_TRUE iff the given line continues on to the next line,
+** because the newline is in quotes or escaped.  The second parameter
+** indicates whether we are inside quotes or not, and is updated by
+** this function.  If an unquoted and unescaped semicolon is encountered,
+** the line is split at that point.
+*/
+
+static MR_bool
+MR_trace_continue_line(char *ptr, MR_bool *quoted)
+{
+	MR_bool		escaped = MR_FALSE;
+
+	while (*ptr != '\0') {
+		if (escaped) {
+			/* do nothing special */
+			escaped = MR_FALSE;
+		} else if (*ptr == MR_MDB_ESCAPE_CHAR) {
+			escaped = MR_TRUE;
+		} else if (*ptr == MR_MDB_QUOTE_CHAR) {
+			*quoted = !(*quoted);
+		} else if (!(*quoted) && *ptr == ';') {
+			/*
+			** The line contains at least two commands.
+			** Return only the first command now; put the
+			** others back in the input to be processed later.
+			*/
+			*ptr = '\0';
+			MR_insert_line_at_head(MR_copy_string(ptr + 1));
+			return MR_FALSE;
+		}
+
+		++ptr;
+	}
+
+	if (escaped) {
+		/*
+		** Replace the escaped newline with a space.
+		*/
+		*(ptr - 1) = ' ';
+	}
+
+	return (*quoted || escaped);
 }
 
 MR_Code *
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list