(********************************************************************) (* *) (* shell.s7i Support for shell commands *) (* Copyright (C) 2009 - 2011 Thomas Mertes *) (* *) (* This file is part of the Seed7 Runtime Library. *) (* *) (* The Seed7 Runtime Library is free software; you can *) (* redistribute it and/or modify it under the terms of the GNU *) (* Lesser General Public License as published by the Free Software *) (* Foundation; either version 2.1 of the License, or (at your *) (* option) any later version. *) (* *) (* The Seed7 Runtime Library is distributed in the hope that it *) (* will be useful, but WITHOUT ANY WARRANTY; without even the *) (* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *) (* PURPOSE. See the GNU Lesser General Public License for more *) (* details. *) (* *) (* You should have received a copy of the GNU Lesser 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. *) (* *) (********************************************************************) include "utf8.s7i"; include "scanstri.s7i"; (** * Use the shell to execute a ''command'' with ''parameters''. * Parameters which contain a space must be enclosed in double * quotes (E.g.: shell("aCommand", "\"par 1\" par2"); ). The * commands supported and the format of the ''parameters'' are not * covered by the description of the ''shell'' function. Due to the * usage of the operating system shell and external programs, it is * hard to write portable programs, which use the ''shell'' function. * @param command Name of the command to be executed. A path must * use the standard path representation. * @param parameters Space separated list of parameters for the * ''command'', or "" if there are no parameters. * @return the return code of the executed command or of the shell. *) const func integer: shell (in string: command, in string: parameters) is action "CMD_SHELL"; (** * Use the shell to execute a ''command'' with ''parameters''. * Parameters which contain a space must be enclosed in double * quotes (E.g.: shellCmd("aCommand", "\"par 1\" par2"); ). The * commands supported and the format of the ''parameters'' are not * covered by the description of the ''shellCmd'' function. Due to the * usage of the operating system shell and external programs, it is * hard to write portable programs, which use the ''shellCmd'' function. * @param command Name of the command to be executed. A path must * use the standard path representation. * @param parameters Space separated list of parameters for the * ''command'', or "" if there are no parameters. * @exception FILE_ERROR The shell command returns an error. *) const proc: shellCmd (in string: command, in string: parameters) is func begin if shell(command, parameters) <> 0 then raise FILE_ERROR; end if; end func; # The function cmd_sh() is deprecated. Use shellCmd() instead. const proc: cmd_sh (in string: command, in string: parameters) is func begin ignore(shell(command, parameters)); end func; (** * Executes a command using the shell of the operating system. * The command path must use the standard path representation. * Spaces in the command must be preceded by a backslash * (E.g.: shell("do\\ it"); ). Alternatively the command can * be enclosed in double quotes (E.g.: shell("\"do it\""); ). * Command parameters containing a space must be enclosed in double * quotes (E.g.: shell("do_it \"par 1\" par2"); ). The commands * supported and the format of the ''parameters'' are not covered * by the description of the ''shell'' function. Due to the usage * of the operating system shell and external programs, it is hard * to write portable programs, which use the ''shell'' function. * @param cmdAndParams Command to be executed and optional space * separated list of parameters. Command and parameters * must be space separated. * @return the return code of the executed command or of the shell. *) const func integer: shell (in string: cmdAndParams) is func result var integer: returnCode is 0; local var string: command is ""; var string: parameters is ""; begin parameters := cmdAndParams; command := getCommandLineWord(parameters); returnCode := shell(command, parameters); end func; (** * Executes a command using the shell of the operating system. * The command path must use the standard path representation. * Spaces in the command must be preceded by a backslash * (E.g.: shellCmd("do\\ it"); ). Alternatively the command can * be enclosed in double quotes (E.g.: shellCmd("\"do it\""); ). * Command parameters containing a space must be enclosed in double * quotes (E.g.: shellCmd("do_it \"par 1\" par2"); ). The commands * supported and the format of the ''parameters'' are not covered * by the description of the ''shellCmd'' function. Due to the usage * of the operating system shell and external programs, it is hard * to write portable programs, which use the ''shellCmd'' function. * @param cmdAndParams Command to be executed and optional space * separated list of parameters. Command and parameters * must be space separated. * @exception FILE_ERROR The shell command returns an error. *) const proc: shellCmd (in string: cmdAndParams) is func begin if shell(cmdAndParams) <> 0 then raise FILE_ERROR; end if; end func; # The function cmd_sh() is deprecated. Use shellCmd() instead. const proc: cmd_sh (in string: cmdAndParams) is func begin ignore(shell(cmdAndParams)); end func; (** * Convert a [[string]], such that it can be used as shell parameter. * The function adds escape characters or quotations to a string. * The result is useable as parameter for the functions ''shell'', * ''shellCmd'', ''popen'' and ''popen8''. Shell parameters must be * escaped individually. Afterwards escaped parameters are * joined to a space separated list of parameters. * @return a string which can be used as shell parameter. * @exception MEMORY_ERROR Not enough memory to convert 'stri'. *) const func string: shellEscape (in string: stri) is action "CMD_SHELL_ESCAPE"; (** * Convert a standard path to the path of the operating system. * The result must be escaped with ''shellEscape'' to be useable as * parameter for the functions ''shell'', ''shellCmd'', ''popen'' and * ''popen8''. * @param standardPath Path in the standard path representation. * @return a string containing an operating system path. * @exception MEMORY_ERROR Not enough memory to convert ''standardPath''. * @exception RANGE_ERROR ''standardPath'' is not representable as operating * system path. *) const func string: toOsPath (in string: standardPath) is action "CMD_TO_OS_PATH"; (** * Convert a standard path such that it can be used as shell parameter. * The result is useable as parameter for the functions ''shell'', * ''shellCmd'', ''popen'' and ''popen8''. Shell parameters must be * converted individually. Afterwards converted parameters are * joined to a space separated list of parameters. * @param standardPath Path in the standard path representation. * @return a string containing an escaped operating system path. * @exception MEMORY_ERROR Not enough memory to convert ''standardPath''. * @exception RANGE_ERROR ''standardPath'' is not representable as operating * system path. *) const func string: toShellPath (in string: path) is return shellEscape(toOsPath(path)); const func string: shellParameters (in array string: paramList) is func result var string: parameters is ""; local var string: parameter is ""; begin for parameter range paramList do if parameters <> "" then parameters &:= " "; end if; parameters &:= shellEscape(parameter); end for; end func; const func integer: shell (in string: command, in array string: paramList) is return shell(command, shellParameters(paramList)); const func clib_file: popenClibFile (in string: command, in string: parameters, in string: mode) is action "FIL_POPEN"; const proc: pclose (in clib_file: fileToClose) is action "FIL_PCLOSE"; (** * [[file|File]] implementation type for operating system pipes. *) const type: popenFile is sub external_file struct end struct; type_implements_interface(popenFile, file); (** * Open a pipe to a shell ''command'' with ''parameters''. * The command reads, respectively writes with Latin-1 encoding. * Parameters which contain a space must be enclosed in double * quotes (E.g.: popen("aCommand", "\"par 1\" par2", "r"); ). The * function [[#shellEscape(in_string)|shellEscape]] converts a * [[string]], such that it can be used as parameter for ''popen'' * (E.g.: popen("aCmd", shellEscape("par 1") & " par2", "r"); ). The * commands supported and the format of the ''parameters'' are not * covered by the description of the ''popen'' function. Due to the * usage of the operating system shell and external programs, it is * hard to write portable programs, which use the ''popen'' function. * @param command Name of the command to be executed. A path must * use the standard path representation. * @param parameters Space separated list of parameters for * the ''command'', or "" if there are no parameters. * @param mode A pipe can be opened with the binary modes * "r" (read) and "w" (write) or with the text modes * "rt" (read) and "wt" (write). * @return the pipe file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception RANGE_ERROR ''command'' is not representable as * operating system path, or ''mode'' is illegal. *) const func file: popen (in string: command, in string: parameters, in string: mode) is func result var file: newPipe is STD_NULL; local var clib_file: open_file is CLIB_NULL_FILE; var popenFile: new_file is popenFile.value; begin open_file := popenClibFile(command, parameters, mode); if open_file <> CLIB_NULL_FILE then new_file.ext_file := open_file; new_file.name := command; newPipe := toInterface(new_file); end if; end func; (** * Open a pipe to a shell command. * The command reads, respectively writes with Latin-1 encoding. * Spaces in the command must be preceded by a backslash * (E.g.: popen("do\\ it"); ). Alternatively the command can * be enclosed in double quotes (E.g.: popen("\"do it\""); ). * Command parameters containing a space must be enclosed in * double quotes (E.g.: popen("do_it \"par 1\" par2"); ). * The commands supported and the format of the ''parameters'' * are not covered by the description of the ''popen'' function. * Due to the usage of the operating system shell and external * programs, it is hard to write portable programs, which use the * ''popen'' function. * @param cmdAndParams Command to be executed and optional space * separated list of parameters. Command and parameters * must be space separated. * @param mode A pipe can be opened with the binary modes * "r" (read) and "w" (write) or with the text modes * "rt" (read) and "wt" (write). * @return the pipe file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception RANGE_ERROR The command is not representable as * operating system path, or ''mode'' is illegal. *) const func file: popen (in string: cmdAndParams, in string: mode) is func result var file: newPipe is STD_NULL; local var string: command is ""; var string: parameters is ""; begin parameters := cmdAndParams; command := getCommandLineWord(parameters); newPipe := popen(command, parameters, mode); end func; (** * Open a pipe to a shell ''command'' with a ''paramList''. * The command reads, respectively writes with Latin-1 encoding. * @param command Name of the command to be executed. A path must * use the standard path representation. * @param paramList Array of argument strings passed to the command. * It is not necessary to quote the parameters, since this * function takes care of that. * @param mode A pipe can be opened with the binary modes * "r" (read) and "w" (write) or with the text modes * "rt" (read) and "wt" (write). * @return the pipe file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception RANGE_ERROR ''command'' is not representable as * operating system path, or ''mode'' is illegal. *) const func file: popen (in string: command, in array string: paramList, in string: mode) is func result var file: newPipe is STD_NULL; local var string: parameter is ""; var string: parameters is ""; begin for parameter range paramList do if parameters <> "" then parameters &:= " "; end if; parameters &:= shellEscape(parameter); end for; newPipe := popen(command, parameters, mode); end func; (** * Wait for the process associated with aPipe to terminate. * @param aFile Pipe to be closed (created by 'popen'). * @exception FILE_ERROR A system function returned an error. *) const proc: close (in popenFile: aPipe) is func begin pclose(aPipe.ext_file); end func; (** * [[file|File]] implementation type for UTF-8 encoded operating system pipes. *) const type: popen8File is sub utf8File struct end struct; type_implements_interface(popen8File, file); (** * Open an UTF-8 encoded pipe to a shell ''command'' with ''parameters''. * The command reads, respectively writes with UTF-8 encoding. * Parameters which contain a space must be enclosed in double * quotes (E.g.: popen8("aCommand", "\"par 1\" par2", "r"); ). The * function [[#shellEscape(in_string)|shellEscape]] converts a * [[string]], such that it can be used as parameter for ''popen'' * (E.g.: popen8("aCmd", shellEscape("par 1") & " par2", "r"); ). The * commands supported and the format of the ''parameters'' are not * covered by the description of the ''popen8'' function. Due to the * usage of the operating system shell and external programs, it is * hard to write portable programs, which use the ''popen8'' function. * @param command Name of the command to be executed. A path must * use the standard path representation. * @param parameters Space separated list of parameters for * the ''command'', or "" if there are no parameters. * @param mode A pipe can be opened with the binary modes * "r" (read) and "w" (write) or with the text modes * "rt" (read) and "wt" (write). * @return the pipe file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception RANGE_ERROR ''command'' is not representable as * operating system path, or ''mode'' is illegal. *) const func file: popen8 (in string: command, in string: parameters, in string: mode) is func result var file: newPipe is STD_NULL; local var clib_file: open_file is CLIB_NULL_FILE; var popen8File: new_file is popen8File.value; begin open_file := popenClibFile(command, parameters, mode); if open_file <> CLIB_NULL_FILE then new_file.ext_file := open_file; new_file.name := command; newPipe := toInterface(new_file); end if; end func; (** * Open an UTF-8 encoded pipe to a shell command. * The command reads, respectively writes with UTF-8 encoding. * Spaces in the command must be preceded by a backslash * (E.g.: popen8("do\\ it"); ). Alternatively the command can * be enclosed in double quotes (E.g.: popen8("\"do it\""); ). * Command parameters containing a space must be enclosed in * double quotes (E.g.: popen8("do_it \"par 1\" par2"); ). * The commands supported and the format of the ''parameters'' * are not covered by the description of the ''popen8'' function. * Due to the usage of the operating system shell and external * programs, it is hard to write portable programs, which use the * ''popen8'' function. * @param cmdAndParams Command to be executed and optional space * separated list of parameters. Command and parameters * must be space separated. * @param mode A pipe can be opened with the binary modes * "r" (read) and "w" (write) or with the text modes * "rt" (read) and "wt" (write). * @return the pipe file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception RANGE_ERROR The command is not representable as * operating system path, or ''mode'' is illegal. *) const func file: popen8 (in string: cmdAndParams, in string: mode) is func result var file: newPipe is STD_NULL; local var string: command is ""; var string: parameters is ""; begin parameters := cmdAndParams; command := getCommandLineWord(parameters); newPipe := popen8(command, parameters, mode); end func; (** * Open an UTF-8 encoded pipe to a shell ''command'' with a ''paramList''. * The command reads, respectively writes with UTF-8 encoding. * @param command Name of the command to be executed. A path must * use the standard path representation. * @param paramList Array of argument strings passed to the command. * It is not necessary to quote the parameters, since this * function takes care of that. * @param mode A pipe can be opened with the binary modes * "r" (read) and "w" (write) or with the text modes * "rt" (read) and "wt" (write). * @return the pipe file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception RANGE_ERROR ''command'' is not representable as * operating system path, or ''mode'' is illegal. *) const func file: popen8 (in string: command, in array string: paramList, in string: mode) is func result var file: newPipe is STD_NULL; local var string: parameter is ""; var string: parameters is ""; begin for parameter range paramList do if parameters <> "" then parameters &:= " "; end if; parameters &:= shellEscape(parameter); end for; newPipe := popen8(command, parameters, mode); end func; (** * Wait for the process associated with aPipe to terminate. * @param aPipe UTF-8 encoded pipe to be closed (created by 'popen8'). * @exception FILE_ERROR A system function returned an error. *) const proc: close (in popen8File: aPipe) is func begin pclose(aPipe.ext_file); end func;