utils.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. var jsencoding = require('../deps/encoding/encoding');
  2. var RE_ENCODED = /%([a-fA-F0-9]{2})/g;
  3. function encodedReplacer(match, byte) {
  4. return String.fromCharCode(parseInt(byte, 16));
  5. }
  6. function parseParams(str) {
  7. var res = [],
  8. state = 'key',
  9. charset = '',
  10. inquote = false,
  11. escaping = false,
  12. p = 0,
  13. tmp = '';
  14. for (var i = 0, len = str.length; i < len; ++i) {
  15. if (str[i] === '\\' && inquote) {
  16. if (escaping)
  17. escaping = false;
  18. else {
  19. escaping = true;
  20. continue;
  21. }
  22. } else if (str[i] === '"') {
  23. if (!escaping) {
  24. if (inquote) {
  25. inquote = false;
  26. state = 'key';
  27. } else
  28. inquote = true;
  29. continue;
  30. } else
  31. escaping = false;
  32. } else {
  33. if (escaping && inquote)
  34. tmp += '\\';
  35. escaping = false;
  36. if ((state === 'charset' || state === 'lang') && str[i] === "'") {
  37. if (state === 'charset') {
  38. state = 'lang';
  39. charset = tmp.substring(1);
  40. } else
  41. state = 'value';
  42. tmp = '';
  43. continue;
  44. } else if (state === 'key'
  45. && (str[i] === '*' || str[i] === '=')
  46. && res.length) {
  47. if (str[i] === '*')
  48. state = 'charset';
  49. else
  50. state = 'value';
  51. res[p] = [tmp, undefined];
  52. tmp = '';
  53. continue;
  54. } else if (!inquote && str[i] === ';') {
  55. state = 'key';
  56. if (charset) {
  57. if (tmp.length) {
  58. tmp = decodeText(tmp.replace(RE_ENCODED, encodedReplacer),
  59. 'binary',
  60. charset);
  61. }
  62. charset = '';
  63. }
  64. if (res[p] === undefined)
  65. res[p] = tmp;
  66. else
  67. res[p][1] = tmp;
  68. tmp = '';
  69. ++p;
  70. continue;
  71. } else if (!inquote && (str[i] === ' ' || str[i] === '\t'))
  72. continue;
  73. }
  74. tmp += str[i];
  75. }
  76. if (charset && tmp.length) {
  77. tmp = decodeText(tmp.replace(RE_ENCODED, encodedReplacer),
  78. 'binary',
  79. charset);
  80. }
  81. if (res[p] === undefined) {
  82. if (tmp)
  83. res[p] = tmp;
  84. } else
  85. res[p][1] = tmp;
  86. return res;
  87. };
  88. exports.parseParams = parseParams;
  89. function decodeText(text, textEncoding, destEncoding) {
  90. var ret;
  91. if (text && jsencoding.encodingExists(destEncoding)) {
  92. try {
  93. ret = jsencoding.TextDecoder(destEncoding)
  94. .decode(new Buffer(text, textEncoding));
  95. } catch(e) {}
  96. }
  97. return (typeof ret === 'string' ? ret : text);
  98. }
  99. exports.decodeText = decodeText;
  100. var HEX = [
  101. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  102. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  103. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  104. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
  105. 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  106. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  107. 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  108. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  109. ], RE_PLUS = /\+/g;
  110. function Decoder() {
  111. this.buffer = undefined;
  112. }
  113. Decoder.prototype.write = function(str) {
  114. // Replace '+' with ' ' before decoding
  115. str = str.replace(RE_PLUS, ' ');
  116. var res = '';
  117. var i = 0, p = 0, len = str.length;
  118. for (; i < len; ++i) {
  119. if (this.buffer !== undefined) {
  120. if (!HEX[str.charCodeAt(i)]) {
  121. res += '%' + this.buffer;
  122. this.buffer = undefined;
  123. --i; // retry character
  124. } else {
  125. this.buffer += str[i];
  126. ++p;
  127. if (this.buffer.length === 2) {
  128. res += String.fromCharCode(parseInt(this.buffer, 16));
  129. this.buffer = undefined;
  130. }
  131. }
  132. } else if (str[i] === '%') {
  133. if (i > p) {
  134. res += str.substring(p, i);
  135. p = i;
  136. }
  137. this.buffer = '';
  138. ++p;
  139. }
  140. }
  141. if (p < len && this.buffer === undefined)
  142. res += str.substring(p);
  143. return res;
  144. };
  145. Decoder.prototype.reset = function() {
  146. this.buffer = undefined;
  147. };
  148. exports.Decoder = Decoder;
  149. var RE_SPLIT_POSIX =
  150. /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/,
  151. RE_SPLIT_DEVICE =
  152. /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/,
  153. RE_SPLIT_WINDOWS =
  154. /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
  155. function splitPathPosix(filename) {
  156. return RE_SPLIT_POSIX.exec(filename).slice(1);
  157. }
  158. function splitPathWindows(filename) {
  159. // Separate device+slash from tail
  160. var result = RE_SPLIT_DEVICE.exec(filename),
  161. device = (result[1] || '') + (result[2] || ''),
  162. tail = result[3] || '';
  163. // Split the tail into dir, basename and extension
  164. var result2 = RE_SPLIT_WINDOWS.exec(tail),
  165. dir = result2[1],
  166. basename = result2[2],
  167. ext = result2[3];
  168. return [device, dir, basename, ext];
  169. }
  170. function basename(path) {
  171. var f = splitPathPosix(path)[2];
  172. if (f === path)
  173. f = splitPathWindows(path)[2];
  174. return f;
  175. }
  176. exports.basename = basename;