escapechars.go raw

   1  // Copyright 2016 Charles Banning. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file
   4  
   5  package mxj
   6  
   7  import (
   8  	"bytes"
   9  )
  10  
  11  var xmlEscapeChars bool
  12  
  13  // XMLEscapeChars(true) forces escaping invalid characters in attribute and element values.
  14  // NOTE: this is brute force with NO interrogation of '&' being escaped already; if it is
  15  // then '&' will be re-escaped as '&'.
  16  //
  17  /*
  18  	The values are:
  19  	"   "
  20  	'   '
  21  	<   &lt;
  22  	>   &gt;
  23  	&   &amp;
  24  */
  25  //
  26  // Note: if XMLEscapeCharsDecoder(true) has been called - or the default, 'false,' value
  27  // has been toggled to 'true' - then XMLEscapeChars(true) is ignored.  If XMLEscapeChars(true)
  28  // has already been called before XMLEscapeCharsDecoder(true), XMLEscapeChars(false) is called
  29  // to turn escape encoding on mv.Xml, etc., to prevent double escaping ampersands, '&'.
  30  func XMLEscapeChars(b ...bool) {
  31  	var bb bool
  32  	if len(b) == 0 {
  33  		bb = !xmlEscapeChars
  34  	} else {
  35  		bb = b[0]
  36  	}
  37  	if bb == true && xmlEscapeCharsDecoder == false {
  38  		xmlEscapeChars = true
  39  	} else {
  40  		xmlEscapeChars = false
  41  	}
  42  }
  43  
  44  // Scan for '&' first, since 's' may contain "&amp;" that is parsed to "&amp;amp;"
  45  // - or "&lt;" that is parsed to "&amp;lt;".
  46  var escapechars = [][2][]byte{
  47  	{[]byte(`&`), []byte(`&amp;`)},
  48  	{[]byte(`<`), []byte(`&lt;`)},
  49  	{[]byte(`>`), []byte(`&gt;`)},
  50  	{[]byte(`"`), []byte(`&quot;`)},
  51  	{[]byte(`'`), []byte(`&apos;`)},
  52  }
  53  
  54  func escapeChars(s string) string {
  55  	if len(s) == 0 {
  56  		return s
  57  	}
  58  
  59  	b := []byte(s)
  60  	for _, v := range escapechars {
  61  		n := bytes.Count(b, v[0])
  62  		if n == 0 {
  63  			continue
  64  		}
  65  		b = bytes.Replace(b, v[0], v[1], n)
  66  	}
  67  	return string(b)
  68  }
  69  
  70  // per issue #84, escape CharData values from xml.Decoder
  71  
  72  var xmlEscapeCharsDecoder bool
  73  
  74  // XMLEscapeCharsDecoder(b ...bool) escapes XML characters in xml.CharData values
  75  // returned by Decoder.Token.  Thus, the internal Map values will contain escaped
  76  // values, and you do not need to set XMLEscapeChars for proper encoding.
  77  //
  78  // By default, the Map values have the non-escaped values returned by Decoder.Token.
  79  // XMLEscapeCharsDecoder(true) - or, XMLEscapeCharsDecoder() - will toggle escape
  80  // encoding 'on.'
  81  //
  82  // Note: if XMLEscapeCharDecoder(true) is call then XMLEscapeChars(false) is
  83  // called to prevent re-escaping the values on encoding using mv.Xml, etc.
  84  func XMLEscapeCharsDecoder(b ...bool) {
  85  	if len(b) == 0 {
  86  		xmlEscapeCharsDecoder = !xmlEscapeCharsDecoder
  87  	} else {
  88  		xmlEscapeCharsDecoder = b[0]
  89  	}
  90  	if xmlEscapeCharsDecoder == true && xmlEscapeChars == true {
  91  		xmlEscapeChars = false
  92  	}
  93  }
  94