Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ViewString for string to correctly escape characters needing it #3693

Merged
merged 2 commits into from
Nov 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/list.gi
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ local str,ls, i;
fi;

if IsString( list ) then
return Concatenation("\"", list, "\"");
return VIEW_STRING_FOR_STRING(list);
fi;

# make strings for objects in l
Expand Down
62 changes: 55 additions & 7 deletions src/stringobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "error.h"
#include "gaputils.h"
#include "io.h"
#include "listfunc.h"
#include "lists.h"
#include "modules.h"
#include "opers.h"
Expand Down Expand Up @@ -529,8 +530,11 @@ static Obj CopyString(Obj list, Int mut)
/****************************************************************************
**
*F PrintString(<list>) . . . . . . . . . . . . . . . . . . . print a string
*F FuncVIEW_STRING_FOR_STRING(<list>) . . . . . . view a string as a string
**
** 'PrintString' prints the string with the handle <list>.
** 'VIEW_STRING_FOR_STRING' returns a string containing what PrintString
** outputs.
**
** No linebreaks are allowed, if one must be inserted anyhow, it must
** be escaped by a backslash '\', which is done in 'Pr'.
Expand All @@ -542,14 +546,38 @@ static Obj CopyString(Obj list, Int mut)
** characters. The function can be used to print *any* string in a way
** which can be read in by GAP afterwards.
*/
void PrintString(Obj list)

// Type of function given to OutputStringGeneric
typedef void StringOutputterType(void * data, char * strbuf, UInt len);

// Output using Pr
void ToPrOutputter(void * data, char * strbuf, UInt len)
{
strbuf[len++] = '\0';
Pr("%s", (Int)strbuf, 0L);
}

// Output to a string
void ToStringOutputter(void * data, char * buf, UInt lenbuf)
{
Obj str = (Obj)data;
UInt lenstr = GET_LEN_STRING(str);
UInt newlen = lenstr + lenbuf;
GROW_STRING(str, newlen);
memcpy(CHARS_STRING(str) + lenstr, buf, lenbuf);
CHARS_STRING(str)[newlen] = '\0';
SET_LEN_STRING(str, newlen);
}

void OutputStringGeneric(Obj list, StringOutputterType func, void * data)
{
char PrStrBuf[10007]; /* 7 for a \c\123 at the end */
UInt scanout, n;
UInt scanout = 0, n;
UInt1 c;
UInt len = GET_LEN_STRING(list);
UInt off = 0;
Pr("\"", 0L, 0L);
PrStrBuf[scanout++] = '\"';
func(data, PrStrBuf, scanout);
while (off < len) {
scanout = 0;
do {
Expand Down Expand Up @@ -606,12 +634,32 @@ void PrintString(Obj list)
PrStrBuf[scanout++] = c;
}
} while (off < len && scanout < 10000);
PrStrBuf[scanout++] = '\0';
Pr("%s", (Int)PrStrBuf, 0L);
func(data, PrStrBuf, scanout);
}
Pr("\"", 0L, 0L);
scanout = 0;
PrStrBuf[scanout++] = '\"';
func(data, PrStrBuf, scanout);
}

void PrintString(Obj list)
{
OutputStringGeneric(list, ToPrOutputter, (void *)0);
}

Obj FuncVIEW_STRING_FOR_STRING(Obj self, Obj string)
{
if (!IS_STRING(string)) {
RequireArgument("VIEW_STRING_FOR_STRING", string, "must be a string");
}

if (!IS_STRING_REP(string)) {
string = CopyToStringRep(string);
}

Obj output = NEW_STRING(0);
OutputStringGeneric(string, ToStringOutputter, output);
return output;
}

/****************************************************************************
**
Expand Down Expand Up @@ -1905,7 +1953,7 @@ static StructGVarFilt GVarFilts [] = {
*V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
*/
static StructGVarFunc GVarFuncs [] = {

GVAR_FUNC_1ARGS(VIEW_STRING_FOR_STRING, string),
GVAR_FUNC_1ARGS(IS_STRING_CONV, string),
GVAR_FUNC_1ARGS(CONV_STRING, string),
GVAR_FUNC_1ARGS(COPY_TO_STRING_REP, string),
Expand Down
27 changes: 26 additions & 1 deletion tst/testinstall/strings.tst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
##
## This file tests output methods (mainly for strings)
##
#@local x
#@local x, str
gap> START_TEST("strings.tst");

# FFE
Expand Down Expand Up @@ -42,10 +42,28 @@ gap> String(x);
"abc"
gap> x:="\0xFF";
"\377"
gap> PrintString(x);
"\377"
gap> ViewString(x);
"\"\\377\""
gap> x:="\0x42\0x23\0x10\0x10\0x10";
"B#\020\020\020"
gap> PrintString(x);
"B#\020\020\020"
gap> ViewString(x);
"\"B#\\020\\020\\020\""
gap> x:="A string with \0xFF Hex stuff \0x42 in it";
"A string with \377 Hex stuff B in it"
gap> PrintString(x);
"A string with \377 Hex stuff B in it"
gap> ViewString(x);
"\"A string with \\377 Hex stuff B in it\""
gap> x := "\n\t\c\\\"'";
"\n\t\c\\\"'"
gap> PrintString(x);
"\n\t\c\\\"'"
gap> ViewString(x);
"\"\\n\\t\\c\\\\\\\"'\""
gap> "\0yab";
Syntax error: Expecting hexadecimal escape, or two more octal digits in stream\
:1
Expand Down Expand Up @@ -174,5 +192,12 @@ gap> x:='\0xFF';
gap> x:='\0xab';
'\253'

# Huge strings
gap> for len in [10,100,1000,10000,100000] do
> str := List([1..len], x -> 'a');
> Assert(0, Concatenation("\"",str,"\"") = ViewString(str));
> Assert(0, Concatenation(str,"\n") = DisplayString(str));
> od;;

#
gap> STOP_TEST( "strings.tst", 1);