############################################################################
#
#	File:     shquote.icn
#
#	Subject:  Procedures to quote word for UNIX-like shells
#
#	Author:   Robert J. Alexander
#
#	Date:     December 30, 1993
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  The following procedures are useful for writing Icon programs that
#  generate shell commands.  Certain characters cannot appear in the
#  open in strings that are to be interpreted as "words" by command
#  shells.  This family of procedures assists in quoting such strings so
#  that they will be interpreted as single words.  Quoting characters
#  are applied only if necessary -- if strings need no quoting they are
#  returned unchanged.
#
#  shquote(s1, s2, ..., sN) :  s -- Produces a string of words s1, s2,
#  ..., sN that are properly separated and quoted for the Bourne Shell
#  (sh).
#
#  cshquote(s1, s2, ..., sN) :  s -- Produces a string of words s1, s2, ..., sN 
#  that are properly separated and quoted for the C-Shell (csh).
#
#  mpwquote(s1, s2, ..., sN) :  s -- Produces a string of words s1, s2,
#  ..., sN that are properly separated and quoted for the Macintosh
#  Programmer's Workshop shell (MPW Shell).
#
#  dequote(s1,s2) : s3 -- Produces the UNIX-style command line word s1
#  with any quoting characters removed. s2 is the escape character
#  required by the shell (s2 defaults the the usual UNIX escape
#  character, the backslash "\\").
#
############################################################################

procedure shquote(s[])
   return shquote_words(s)
end

procedure cshquote(s[])
   s := shquote_words(s,'\t\n $"#&\'()*;<>?[\\`|~')
   #
   #  But backslashes before any bangs (!).
   #
   s ? {
      s := ""
      while s ||:= tab(find("!")) do {
     s ||:= "\\" || move(1)
     }
      s ||:= tab(0)
      }
   return s
end

procedure mpwquote(s[])
   #
   #  The following are Macintosh Option- characters that have special
   #  meaning to the MPW Shell.  They are represented here as Icon
   #  escape sequences rather than as themselves since some
   #  ASCII-oriented mailers change characters that have their
   #  high-order bits set.
   #
   #  \xa8  circled r
   #  \xb3  >= (I/O redirection)
   #  \xb6  lower case delta (escape character)
   #  \xb7  upper case sigma
   #  \xc5  lower case phi
   #  \xc7  << (I/O redirection)
   #  \xc8  >> (I/O redirection)
   #  \xc9  ...
   #
   local result
   result := ""
   #
   # If there is a "return" in the string, it must be replaced by an
   # escape sequence outside of the single quotes.
   #
   shquote_words(s,
     '\0\t\n\r "#&\'()*+/;<>?[\\]`{|}\xa8\xb3\xb6\xb7\xc5\xc7\xc8\xc9',
     "\xb6") ? {
      while result ||:= tab(find("\x0d")) do {
     result ||:= "'\xb6n'"
     move (1)
     }
      result ||:= tab(0)
      }
   return result
end

procedure shquote_words(wordList,quotedChars,escapeString,sepString)
   local s, result, sep
   /quotedChars := '\t\n\r $"#&\'()*;<>?[\\^`|'
   /escapeString := "\\"
   /sepString := " "
   result := sep := ""
   every s := !wordList do {
      if s == "" | upto(quotedChars,s) then {
     s ? {
        s := "'"
        while s ||:= tab(find("'")) || "'" || escapeString || "''" & move(1)
        s ||:= tab(0) || "'"
        }
     }
      result ||:= sep || s
      sep := sepString
      }
   return result 
end

procedure dequote(s,escapeString,escapeProc)
   local quoteChars,c,d
   /escapeString := "\\"
   /escapeProc := 1
   quoteChars := '"\'' ++ escapeString[1]
   s ? {
      s := ""
      while s ||:= tab(upto(quoteChars)) do {
     if =escapeString then s ||:= (if d === "'" then escapeString else 
escapeProc(move(1)))
     else {
        c := move(1)
        (/d := c) | (s ||:= d ~== c) | (d := &null)
        }
     }
      return s || tab(0)
      }
end

procedure mpwdequote(s)
   return dequote(s,"\xb6",mpw_escape_proc)
end

procedure mpw_escape_proc(ch)
   return case ch of {
      "n": "\n"
      "t": "\t"
      "f": "\f"
      default: ch
      }
end
