Skip to content
Draft
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
53 changes: 29 additions & 24 deletions lib/helpview.gi
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ if ARCH_IS_WINDOWS() then
winfilename:=MakeExternalFilename( SplitString( filename, "#" )[1] );
fi;
Print( "Opening help page ", winfilename, " in default windows browser ... \c" );
Exec( Concatenation("start ", winfilename ) );
Exec2( "start", winfilename );
Print( "done! \n" );
end
);
Expand Down Expand Up @@ -138,7 +138,7 @@ elif ARCH_IS_MAC_OS_X() then
fi;
file := file.file;
fi;
Exec(Concatenation("open -a Preview ", file));
Exec2("open", "-a", "Preview", file);
Print("# see page ", page, " in the Preview window.\n");
end
);
Expand All @@ -155,7 +155,7 @@ elif ARCH_IS_MAC_OS_X() then
fi;
file := file.file;
fi;
Exec(Concatenation("open -a \"Adobe Reader\" ", file));
Exec2("open", "-a", "Adobe Reader", file);
Print("# see page ", page, " in the Adobe Reader window.\n");
end
);
Expand All @@ -172,7 +172,7 @@ elif ARCH_IS_MAC_OS_X() then
fi;
file := file.file;
fi;
Exec(Concatenation("open ", file));
Exec2("open ", file);
Print("# see page ", page, " in the pdf viewer window.\n");
end
);
Expand All @@ -187,16 +187,15 @@ elif ARCH_IS_MAC_OS_X() then
fi;
file := file.file;
fi;
Exec( Concatenation(
"osascript <<ENDSCRIPT\n",
"tell application \"Skim\"\n",
"activate\n",
"open \"", file, "\"\n",
"set theDoc to document of front window\n",
"go theDoc to page ",String(page)," of theDoc\n",
"end tell\n",
"ENDSCRIPT\n" ) );
return;
Exec2("osascript",
InputTextString(Concatenation("""
tell application "Skim"
activate
open """, ViewString(file), """
set theDoc to document of front window
go theDoc to page """,String(page),""" of theDoc
end tell
""")));
end
);

Expand All @@ -207,17 +206,20 @@ else # UNIX but not macOS
HELP_VIEWER_INFO.browser := rec(
type := "url",
show := function( url )
local str;
str := "";
Exec2("wslpath", "-a", "-w", url, OutputTextString(str, false));
# Ignoring part of the URL after '#' since we are unable
# to navigate to the precise location on Windows
url := SplitString( url, "#" )[1];
Exec(Concatenation("explorer.exe \"$(wslpath -a -w \"",url, "\")\""));
Exec2("explorer.exe", str);
end
);

HELP_VIEWER_INFO.("pdf viewer") := rec(
type := "pdf",
show := function(file)
local page;
local page, str;
# unfortunately one cannot (yet?) give a start page to windows
page := 1;
if IsRecord(file) then
Expand All @@ -226,7 +228,10 @@ else # UNIX but not macOS
fi;
file := file.file;
fi;
Exec(Concatenation("explorer.exe \"$(wslpath -a -w \"",file, "\")\""));

str := "";
Exec2("wslpath", "-a", "-w", url, OutputTextString(str, false));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you're using the variable url as the -w argument, but the replaced code was using the variable file. Is this a mistake or a deliberate change?

Exec2("explorer.exe", str);
Print("# see page ", page, " in PDF.\n");
end
);
Expand All @@ -235,15 +240,15 @@ else # UNIX but not macOS
HELP_VIEWER_INFO.netscape := rec(
type := "url",
show := function(url)
Exec(Concatenation("netscape -remote \"openURL(file:", url, ")\""));
Exec2("netscape", "-remote", Concatenation("openURL(file:", url, ")"));
end
);

# html version with mozilla
HELP_VIEWER_INFO.mozilla := rec(
type := "url",
show := function(url)
Exec(Concatenation("mozilla -remote \"openURL(file:", url, ")\""));
Exec2("mozilla", "-remote", Concatenation("openURL(file:", url, ")"));
end
);

Expand Down Expand Up @@ -276,36 +281,36 @@ else # UNIX but not macOS
HELP_VIEWER_INFO.lynx := rec(
type := "url",
show := function(url)
Exec(Concatenation("lynx \"", url, "\""));
Exec2("lynx", url);
end
);

# html version with w3m
HELP_VIEWER_INFO.w3m := rec(
type := "url",
show := function(url)
Exec(Concatenation("w3m \"", url, "\""));
Exec2("w3m", url);
end
);

HELP_VIEWER_INFO.elinks := rec(
type := "url",
show := function(url)
Exec(Concatenation("elinks \"", url, "\""));
Exec2("elinks", url);
end
);

HELP_VIEWER_INFO.links2ng := rec(
type := "url",
show := function(url)
Exec(Concatenation("links2 \"", url, "\""));
Exec2("links2", url);
end
);

HELP_VIEWER_INFO.links2 := rec(
type := "url",
show := function(url)
Exec(Concatenation("links2 -g \"", url, "\""));
Exec2("links2", "-g", url);
end
);
fi;
Expand Down
2 changes: 2 additions & 0 deletions lib/process.gd
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,5 @@ DeclareOperation( "Process",
## <#/GAPDoc>
##
DeclareGlobalFunction( "Exec" );

DeclareGlobalName( "Exec2" );
74 changes: 74 additions & 0 deletions lib/process.gi
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,77 @@ InstallGlobalFunction( Exec, function( arg )
Process( dir, shell, InputTextUser(), OutputTextUser(), [ cs, cmd ] );

end );

# TODO: document this, come up with a better name, write some tests...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still a big TODO 🙂 I don't currently have a better name suggestion but I'll have a think.

BindGlobal( "Exec2", function( arg )
local args, result, a, input, output, dir, cmd;

args := [];
result := rec();

# parse the inputs
for a in arg do
if IsDirectory(a) then
if IsBound(dir) then
Error("must specify at most one working directory");
fi;
dir := a;
elif IsInputStream(a) then
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now input and output stream can come at any position and in any order. Perhaps I should make this code stricter, and require that they come in a specific place (canonical places: 1. as the first two arguments; 2. after the command name; 3. as the last two arguments)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it would be a bigger change, I wonder if the input should be a record, with keys like input :=, output := .. this would mean in future it would be easier to do things like add stderr support, and make it clear which output stream is stdout and which is stderr, things like that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that idea. It could then also be made backwards compatible: if there is a single argument which is a record, then use the new code; otherwise call the old code.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I don't like about it is that for simple uses, it is much more verbose. Compare: e.g.:

old code:

Exec( Concatenation("start ", winfilename ) );

this PR:

Exec2( "start", winfilename );

with a record (record entries subject to change, of course):

Exec(rec(cmd:= "start", args := [ winfilename ] );

A compromise would be to only use a rec for the "extra" arguments like input, output, etc., like so:

Exec2( "start", winfilename, rec( input := BLAH, output := BLEH ) );

but then of course I can't use this to differentiate old and new callers.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here's a possible way to go:

  • allow input and output redirects only via an option record
  • the option record must come last
  • otherwise it stays as it is in this PR, that is: first the command name, then the list of arguments
    • arguments must be strings
    • for convenience it might be nice to also allow integers (and turn them into strings automatically)
    • anything else would be an error (for now, at least; we can add more things in the future this way; what I don't want is to String everything automatically, that just paints us into another corner)

Does that sound reasonable @ChrisJefferson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds reasonable to me at least @fingolfin.

if IsBound(input) then
Error("must specify at most one input stream");
fi;
input := a;
elif IsOutputStream(a) then
if IsBound(output) then
Error("must specify at most one output stream");
fi;
output := a;
elif IsString(a) then
ConvertToStringRep(a);
if not IsBound(cmd) then
cmd := a;
else
Add(args, a);
fi;
else
Error("unsupported argument type");
fi;
od;

if not IsBound(cmd) then
Error("must specify a command to execute");
fi;

# determine full executable path if it is not already a path
if not '/' in cmd then
a := Filename( DirectoriesSystemPrograms(), cmd );
if a = fail and ARCH_IS_WINDOWS() then
a := Filename( DirectoriesSystemPrograms(), Concatenation( cmd, ".exe" ) );
fi;
if a = fail then
Error("could not locate executable for '", cmd, "'");
Comment thread
fingolfin marked this conversation as resolved.
fi;
cmd := a;
fi;

# set default working directory if necessary
if not IsBound(dir) then
dir := DirectoryCurrent();
fi;

# if no input stream was specified, pass no input to the command
if not IsBound(input) then
input := InputTextNone();
fi;

# if no output stream was specified, put output into the returned record
if not IsBound(output) then
result.output := "";
output := OutputTextString(result.output, false);
fi;

# execute the command
result.status := Process( dir, cmd, input, output, args );

return result;
end );
11 changes: 6 additions & 5 deletions lib/streams.gi
Original file line number Diff line number Diff line change
Expand Up @@ -1311,19 +1311,20 @@ InstallGlobalFunction( InputFromUser,
InstallGlobalFunction( OpenExternal, function(filename)
local file;
if ARCH_IS_MAC_OS_X() then
Exec(Concatenation("open \"",filename,"\""));
Exec2("open", filename);
elif ARCH_IS_WINDOWS() then
Exec(Concatenation("cmd /c start \"",filename,"\""));
Exec2("cmd", "/c", "start", filename);
elif ARCH_IS_WSL() then
# If users pass a URL, make sure if does not get mangled.
if ForAny(["https://", "http://"], {pre} -> StartsWith(filename, pre)) then
file := filename;
else
file := Concatenation("$(wslpath -a -w \"",filename,"\")");
file := "";
Exec2("wslpath", "-a", "-w", filename, OutputTextString(file, false));
fi;
Exec(Concatenation("explorer.exe \"", file, "\""));
Exec2("explorer.exe", file);
else
Exec(Concatenation("xdg-open \"",filename,"\""));
Exec2("xdg-open", filename);
fi;
end );

Expand Down