PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/src/core/s-file.c

https://github.com/WoodyLin/r3
C | 275 lines | 164 code | 25 blank | 86 comment | 65 complexity | d867d07e1686322601d80d3c0a90d72b MD5 | raw file
Possible License(s): Apache-2.0
  1. /***********************************************************************
  2. **
  3. ** REBOL [R3] Language Interpreter and Run-time Environment
  4. **
  5. ** Copyright 2012 REBOL Technologies
  6. ** REBOL is a trademark of REBOL Technologies
  7. **
  8. ** Licensed under the Apache License, Version 2.0 (the "License");
  9. ** you may not use this file except in compliance with the License.
  10. ** You may obtain a copy of the License at
  11. **
  12. ** http://www.apache.org/licenses/LICENSE-2.0
  13. **
  14. ** Unless required by applicable law or agreed to in writing, software
  15. ** distributed under the License is distributed on an "AS IS" BASIS,
  16. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. ** See the License for the specific language governing permissions and
  18. ** limitations under the License.
  19. **
  20. ************************************************************************
  21. **
  22. ** Module: s-file.c
  23. ** Summary: file and path string handling
  24. ** Section: strings
  25. ** Author: Carl Sassenrath
  26. ** Notes:
  27. **
  28. ***********************************************************************/
  29. #include "sys-core.h"
  30. #define FN_PAD 2 // pad file name len for adding /, /*, and /?
  31. /***********************************************************************
  32. **
  33. */ REBSER *To_REBOL_Path(void *bp, REBCNT len, REBINT uni, REBFLG dir)
  34. /*
  35. ** Convert local filename to a REBOL filename.
  36. **
  37. ** Allocate and return a new series with the converted path.
  38. ** Return 0 on error.
  39. **
  40. ** Reduces width when possible.
  41. ** Adds extra space at end for appending a dir /*
  42. **
  43. ** REBDIFF: No longer appends current dir to volume when no
  44. ** root slash is provided (that odd MSDOS c:file case).
  45. **
  46. ***********************************************************************/
  47. {
  48. REBOOL colon = 0; // have we hit a ':' yet?
  49. REBOOL slash = 0; // have we hit a '/' yet?
  50. REBUNI c;
  51. REBSER *dst;
  52. REBCNT n;
  53. REBCNT i;
  54. if (len == 0)
  55. len = uni ? wcslen((REBUNI*)bp) : LEN_BYTES((REBYTE*)bp);
  56. n = 0;
  57. dst = ((uni == -1) || (uni && Is_Wide((REBUNI*)bp, len)))
  58. ? Make_Unicode(len+FN_PAD) : Make_Binary(len+FN_PAD);
  59. for (i = 0; i < len;) {
  60. c = uni ? ((REBUNI*)bp)[i] : ((REBYTE*)bp)[i];
  61. i++;
  62. if (c == ':') {
  63. // Handle the vol:dir/file format:
  64. if (colon || slash) return 0; // no prior : or / allowed
  65. colon = 1;
  66. if (i < len) {
  67. c = uni ? ((REBUNI*)bp)[i] : ((REBYTE*)bp)[i];
  68. if (c == '\\' || c == '/') i++; // skip / in foo:/file
  69. }
  70. c = '/'; // replace : with a /
  71. }
  72. else if (c == '\\' || c== '/') {
  73. if (slash > 0) continue;
  74. c = '/';
  75. slash = 1;
  76. }
  77. else slash = 0;
  78. SET_ANY_CHAR(dst, n++, c);
  79. }
  80. if (dir && c != '/') { // watch for %/c/ case
  81. SET_ANY_CHAR(dst, n++, '/');
  82. }
  83. SERIES_TAIL(dst) = n;
  84. TERM_SERIES(dst);
  85. // Change C:/ to /C/ (and C:X to /C/X):
  86. if (colon) Insert_Char(dst, 0, (REBCNT)'/');
  87. return dst;
  88. }
  89. /***********************************************************************
  90. **
  91. */ REBSER *Value_To_REBOL_Path(REBVAL *val, REBOOL dir)
  92. /*
  93. ** Helper to above function.
  94. **
  95. ***********************************************************************/
  96. {
  97. ASSERT1(ANY_BINSTR(val), RP_MISC);
  98. return To_REBOL_Path(VAL_DATA(val), VAL_LEN(val), (REBOOL)!VAL_BYTE_SIZE(val), dir);
  99. }
  100. /***********************************************************************
  101. **
  102. */ REBSER *To_Local_Path(void *bp, REBCNT len, REBOOL uni, REBFLG full)
  103. /*
  104. ** Convert REBOL filename to a local filename.
  105. **
  106. ** Allocate and return a new series with the converted path.
  107. ** Return 0 on error.
  108. **
  109. ** Adds extra space at end for appending a dir /*
  110. ** Expands width for OS's that require it.
  111. **
  112. ***********************************************************************/
  113. {
  114. REBUNI c, d;
  115. REBSER *dst;
  116. REBCNT i = 0;
  117. REBCNT n = 0;
  118. REBUNI *out;
  119. REBCHR *lpath;
  120. REBCNT l = 0;
  121. if (len == 0)
  122. len = uni ? wcslen((REBUNI*)bp) : LEN_BYTES((REBYTE*)bp);
  123. // Prescan for: /c/dir = c:/dir, /vol/dir = //vol/dir, //dir = ??
  124. c = GET_CHAR_UNI(uni, bp, i);
  125. if (c == '/') { // %/
  126. dst = Make_Unicode(len+FN_PAD);
  127. out = UNI_HEAD(dst);
  128. #ifdef TO_WIN32
  129. i++;
  130. if (i < len) {
  131. c = GET_CHAR_UNI(uni, bp, i);
  132. i++;
  133. }
  134. if (c != '/') { // %/c or %/c/ but not %/ %// %//c
  135. // peek ahead for a '/':
  136. d = '/';
  137. if (i < len) d = GET_CHAR_UNI(uni, bp, i);
  138. if (d == '/') { // %/c/ => "c:/"
  139. i++;
  140. out[n++] = c;
  141. out[n++] = ':';
  142. }
  143. else {
  144. out[n++] = OS_DIR_SEP; // %/cc %//cc => "//cc"
  145. i--;
  146. }
  147. }
  148. #endif
  149. out[n++] = OS_DIR_SEP;
  150. }
  151. else {
  152. if (full) l = OS_GET_CURRENT_DIR(&lpath);
  153. dst = Make_Unicode(l + len + FN_PAD); // may be longer (if lpath is encoded)
  154. if (full) {
  155. #ifdef TO_WIN32
  156. Append_Uni_Uni(dst, lpath, l);
  157. #else
  158. REBINT clen = Decode_UTF8(UNI_HEAD(dst), lpath, l, FALSE);
  159. dst->tail = abs(clen);
  160. //Append_Bytes(dst, lpath);
  161. #endif
  162. Append_Byte(dst, OS_DIR_SEP);
  163. OS_FREE(lpath);
  164. }
  165. out = UNI_HEAD(dst);
  166. n = SERIES_TAIL(dst);
  167. }
  168. // Prescan each file segment for: . .. directory names:
  169. // (Note the top of this loop always follows / or start)
  170. while (i < len) {
  171. if (full) {
  172. // Peek for: . ..
  173. c = GET_CHAR_UNI(uni, bp, i);
  174. if (c == '.') { // .
  175. i++;
  176. c = GET_CHAR_UNI(uni, bp, i);
  177. if (c == '.') { // ..
  178. c = GET_CHAR_UNI(uni, bp, i+1);
  179. if (c == 0 || c == '/') { // ../ or ..
  180. i++;
  181. // backup a dir
  182. n -= (n > 2) ? 2 : n;
  183. for (; n > 0 && out[n] != OS_DIR_SEP; n--);
  184. c = c ? 0 : OS_DIR_SEP; // add / if necessary
  185. }
  186. // fall through on invalid ..x combination:
  187. }
  188. else { // .a or . or ./
  189. if (c == '/') {
  190. i++;
  191. c = 0; // ignore it
  192. }
  193. else if (c) c = '.'; // for store below
  194. }
  195. if (c) out[n++] = c;
  196. }
  197. }
  198. for (; i < len; i++) {
  199. c = GET_CHAR_UNI(uni, bp, i);
  200. if (c == '/') {
  201. if (n == 0 || out[n-1] != OS_DIR_SEP) out[n++] = OS_DIR_SEP;
  202. i++;
  203. break;
  204. }
  205. out[n++] = c;
  206. }
  207. }
  208. out[n] = 0;
  209. SERIES_TAIL(dst) = n;
  210. // TERM_SERIES(dst);
  211. // Debug_Uni(dst);
  212. return dst;
  213. }
  214. /***********************************************************************
  215. **
  216. */ REBSER *Value_To_Local_Path(REBVAL *val, REBFLG full)
  217. /*
  218. ** Helper to above function.
  219. **
  220. ***********************************************************************/
  221. {
  222. ASSERT1(ANY_BINSTR(val), RP_MISC);
  223. return To_Local_Path(VAL_DATA(val), VAL_LEN(val), (REBOOL)!VAL_BYTE_SIZE(val), full);
  224. }
  225. /***********************************************************************
  226. **
  227. */ REBSER *Value_To_OS_Path(REBVAL *val)
  228. /*
  229. ** Helper to above function.
  230. **
  231. ***********************************************************************/
  232. {
  233. REBSER *ser; // will be unicode size
  234. #ifndef TO_WIN32
  235. REBSER *bin;
  236. REBCNT n;
  237. #endif
  238. ASSERT1(ANY_BINSTR(val), RP_MISC);
  239. ser = To_Local_Path(VAL_DATA(val), VAL_LEN(val), (REBOOL)!VAL_BYTE_SIZE(val), TRUE);
  240. #ifndef TO_WIN32
  241. // Posix needs UTF8 conversion:
  242. n = Length_As_UTF8(UNI_HEAD(ser), SERIES_TAIL(ser), TRUE, OS_CRLF);
  243. bin = Make_Binary(n + FN_PAD);
  244. Encode_UTF8(BIN_HEAD(bin), n+FN_PAD, UNI_HEAD(ser), &n, TRUE, OS_CRLF);
  245. SERIES_TAIL(bin) = n;
  246. TERM_SERIES(bin);
  247. ser = bin;
  248. #endif
  249. return ser;
  250. }