VCS_File.bas 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. Option Compare Database
  2. Option Private Module
  3. Option Explicit
  4. #If VBA7 Then
  5. Private Declare PtrSafe _
  6. Function getTempPath Lib "kernel32" _
  7. Alias "GetTempPathA" (ByVal nBufferLength As Long, _
  8. ByVal lpBuffer As String) As Long
  9. Private Declare PtrSafe _
  10. Function getTempFileName Lib "kernel32" _
  11. Alias "GetTempFileNameA" (ByVal lpszPath As String, _
  12. ByVal lpPrefixString As String, _
  13. ByVal wUnique As Long, _
  14. ByVal lpTempFileName As String) As Long
  15. #Else
  16. Private Declare _
  17. Function getTempPath Lib "kernel32" _
  18. Alias "GetTempPathA" (ByVal nBufferLength As Long, _
  19. ByVal lpBuffer As String) As Long
  20. Private Declare _
  21. Function getTempFileName Lib "kernel32" _
  22. Alias "GetTempFileNameA" (ByVal lpszPath As String, _
  23. ByVal lpPrefixString As String, _
  24. ByVal wUnique As Long, _
  25. ByVal lpTempFileName As String) As Long
  26. #End If
  27. ' --------------------------------
  28. ' Structures
  29. ' --------------------------------
  30. ' Structure to track buffered reading or writing of binary files
  31. Private Type BinFile
  32. file_num As Integer
  33. file_len As Long
  34. file_pos As Long
  35. buffer As String
  36. buffer_len As Integer
  37. buffer_pos As Integer
  38. at_eof As Boolean
  39. mode As String
  40. End Type
  41. ' --------------------------------
  42. ' Basic functions missing from VB 6: buffered file read/write, string builder, encoding check & conversion
  43. ' --------------------------------
  44. ' Open a binary file for reading (mode = 'r') or writing (mode = 'w').
  45. Private Function BinOpen(ByVal file_path As String, ByVal mode As String) As BinFile
  46. Dim f As BinFile
  47. f.file_num = FreeFile
  48. f.mode = LCase$(mode)
  49. If f.mode = "r" Then
  50. Open file_path For Binary Access Read As f.file_num
  51. f.file_len = LOF(f.file_num)
  52. f.file_pos = 0
  53. If f.file_len > &H4000 Then
  54. f.buffer = String$(&H4000, " ")
  55. f.buffer_len = &H4000
  56. Else
  57. f.buffer = String$(f.file_len, " ")
  58. f.buffer_len = f.file_len
  59. End If
  60. f.buffer_pos = 0
  61. Get f.file_num, f.file_pos + 1, f.buffer
  62. Else
  63. DelIfExist file_path
  64. Open file_path For Binary Access Write As f.file_num
  65. f.file_len = 0
  66. f.file_pos = 0
  67. f.buffer = String$(&H4000, " ")
  68. f.buffer_len = 0
  69. f.buffer_pos = 0
  70. End If
  71. BinOpen = f
  72. End Function
  73. ' Buffered read one byte at a time from a binary file.
  74. Private Function BinRead(ByRef f As BinFile) As Integer
  75. If f.at_eof = True Then
  76. BinRead = 0
  77. Exit Function
  78. End If
  79. BinRead = Asc(Mid$(f.buffer, f.buffer_pos + 1, 1))
  80. f.buffer_pos = f.buffer_pos + 1
  81. If f.buffer_pos >= f.buffer_len Then
  82. f.file_pos = f.file_pos + &H4000
  83. If f.file_pos >= f.file_len Then
  84. f.at_eof = True
  85. Exit Function
  86. End If
  87. If f.file_len - f.file_pos > &H4000 Then
  88. f.buffer_len = &H4000
  89. Else
  90. f.buffer_len = f.file_len - f.file_pos
  91. f.buffer = String$(f.buffer_len, " ")
  92. End If
  93. f.buffer_pos = 0
  94. Get f.file_num, f.file_pos + 1, f.buffer
  95. End If
  96. End Function
  97. ' Buffered write one byte at a time from a binary file.
  98. Private Sub BinWrite(ByRef f As BinFile, B As Integer)
  99. Mid(f.buffer, f.buffer_pos + 1, 1) = Chr$(B)
  100. f.buffer_pos = f.buffer_pos + 1
  101. If f.buffer_pos >= &H4000 Then
  102. Put f.file_num, , f.buffer
  103. f.buffer_pos = 0
  104. End If
  105. End Sub
  106. ' Close binary file.
  107. Private Sub BinClose(ByRef f As BinFile)
  108. If f.mode = "w" And f.buffer_pos > 0 Then
  109. f.buffer = Left$(f.buffer, f.buffer_pos)
  110. Put f.file_num, , f.buffer
  111. End If
  112. Close f.file_num
  113. End Sub
  114. ' Binary convert a UCS2-little-endian encoded file to UTF-8.
  115. Public Sub ConvertUcs2Utf8(ByVal Source As String, ByVal dest As String)
  116. Dim f_in As BinFile
  117. Dim f_out As BinFile
  118. Dim in_low As Integer
  119. Dim in_high As Integer
  120. logger "ConvertUcs2Utf8", "DEBUG", "Convert UCS2 file " & Source & " to Utf8 File " & dest
  121. f_in = BinOpen(Source, "r")
  122. f_out = BinOpen(dest, "w")
  123. Do While Not f_in.at_eof
  124. in_low = BinRead(f_in)
  125. in_high = BinRead(f_in)
  126. If in_high = 0 And in_low < &H80 Then
  127. ' U+0000 - U+007F 0LLLLLLL
  128. BinWrite f_out, in_low
  129. ElseIf in_high < &H8 Then
  130. ' U+0080 - U+07FF 110HHHLL 10LLLLLL
  131. BinWrite f_out, &HC0 + ((in_high And &H7) * &H4) + ((in_low And &HC0) / &H40)
  132. BinWrite f_out, &H80 + (in_low And &H3F)
  133. Else
  134. ' U+0800 - U+FFFF 1110HHHH 10HHHHLL 10LLLLLL
  135. BinWrite f_out, &HE0 + ((in_high And &HF0) / &H10)
  136. BinWrite f_out, &H80 + ((in_high And &HF) * &H4) + ((in_low And &HC0) / &H40)
  137. BinWrite f_out, &H80 + (in_low And &H3F)
  138. End If
  139. Loop
  140. BinClose f_in
  141. BinClose f_out
  142. End Sub
  143. ' Binary convert a UTF-8 encoded file to UCS2-little-endian.
  144. Public Sub ConvertUtf8Ucs2(ByVal Source As String, ByVal dest As String)
  145. Dim f_in As BinFile
  146. Dim f_out As BinFile
  147. Dim in_1 As Integer
  148. Dim in_2 As Integer
  149. Dim in_3 As Integer
  150. logger "ConvertUtf8Ucs2", "DEBUG", "Convert Utf8 file " & Source & " to UCS File " & dest
  151. f_in = BinOpen(Source, "r")
  152. f_out = BinOpen(dest, "w")
  153. Do While Not f_in.at_eof
  154. in_1 = BinRead(f_in)
  155. If (in_1 And &H80) = 0 Then
  156. ' U+0000 - U+007F 0LLLLLLL
  157. BinWrite f_out, in_1
  158. BinWrite f_out, 0
  159. ElseIf (in_1 And &HE0) = &HC0 Then
  160. ' U+0080 - U+07FF 110HHHLL 10LLLLLL
  161. in_2 = BinRead(f_in)
  162. BinWrite f_out, ((in_1 And &H3) * &H40) + (in_2 And &H3F)
  163. BinWrite f_out, (in_1 And &H1C) / &H4
  164. Else
  165. ' U+0800 - U+FFFF 1110HHHH 10HHHHLL 10LLLLLL
  166. in_2 = BinRead(f_in)
  167. in_3 = BinRead(f_in)
  168. BinWrite f_out, ((in_2 And &H3) * &H40) + (in_3 And &H3F)
  169. BinWrite f_out, ((in_1 And &HF) * &H10) + ((in_2 And &H3C) / &H4)
  170. End If
  171. Loop
  172. BinClose f_in
  173. BinClose f_out
  174. End Sub
  175. ' Determine if this database imports/exports code as UCS-2-LE. (Older file
  176. ' formats cause exported objects to use a Windows 8-bit character set.)
  177. Public Function UsingUcs2() As Boolean
  178. Dim obj_name As String
  179. Dim obj_type As Variant
  180. Dim fn As Integer
  181. Dim bytes As String
  182. Dim obj_type_split() As String
  183. Dim obj_type_name As String
  184. Dim obj_type_num As Integer
  185. If CurrentDb.QueryDefs.count > 0 Then
  186. obj_type_num = acQuery
  187. obj_name = CurrentDb.QueryDefs(0).name
  188. Else
  189. For Each obj_type In Split( _
  190. "Forms|" & acForm & "," & _
  191. "Reports|" & acReport & "," & _
  192. "Scripts|" & acMacro & "," & _
  193. "Modules|" & acModule _
  194. )
  195. DoEvents
  196. obj_type_split = Split(obj_type, "|")
  197. obj_type_name = obj_type_split(0)
  198. obj_type_num = val(obj_type_split(1))
  199. If CurrentDb.Containers(obj_type_name).Documents.count > 0 Then
  200. obj_name = CurrentDb.Containers(obj_type_name).Documents(0).name
  201. Exit For
  202. End If
  203. Next
  204. End If
  205. If obj_name = vbNullString Then
  206. ' No objects found that can be used to test UCS2 versus UTF-8
  207. UsingUcs2 = True
  208. Exit Function
  209. End If
  210. Dim tempFileName As String
  211. tempFileName = VCS_File.TempFile()
  212. Application.SaveAsText obj_type_num, obj_name, tempFileName
  213. fn = FreeFile
  214. Open tempFileName For Binary Access Read As fn
  215. bytes = " "
  216. Get fn, 1, bytes
  217. If Asc(Mid$(bytes, 1, 1)) = &HFF And Asc(Mid$(bytes, 2, 1)) = &HFE Then
  218. UsingUcs2 = True
  219. Else
  220. UsingUcs2 = False
  221. End If
  222. Close fn
  223. On Error Resume Next
  224. Kill tempFileName
  225. End Function
  226. Public Function ReadFile(filePath As String, Optional encoding As String = "utf-8") As String
  227. Dim objStream As ADODB.Stream
  228. Set objStream = New ADODB.Stream
  229. objStream.Charset = encoding
  230. objStream.Open
  231. objStream.LoadFromFile (filePath)
  232. ReadFile = objStream.ReadText()
  233. objStream.Close
  234. Set objStream = Nothing
  235. End Function
  236. Public Sub MakeFile(filePath As String, content As String, Optional encoding As String = "utf-8")
  237. Dim objStream As ADODB.Stream
  238. Set objStream = CreateObject("ADODB.Stream")
  239. objStream.Open
  240. objStream.Type = 2 'Text
  241. objStream.Charset = encoding
  242. objStream.WriteText content
  243. objStream.SaveToFile (filePath)
  244. objStream.Close
  245. End Sub
  246. ' Generate Random / Unique tempprary file name.
  247. Public Function TempFile(Optional ByVal sPrefix As String = "oa") As String
  248. Dim sTmpPath As String * 512
  249. Dim sTmpName As String * 576
  250. Dim nRet As Long
  251. Dim sFileName As String
  252. nRet = getTempPath(512, sTmpPath)
  253. nRet = getTempFileName(sTmpPath, sPrefix, 0, sTmpName)
  254. If nRet <> 0 Then sFileName = Left$(sTmpName, InStr(sTmpName, vbNullChar) - 1)
  255. TempFile = sFileName
  256. End Function
  257. Public Function IsValidFileName(ByVal sName As String) As Boolean
  258. IsValidFileName = (InStr(sName, "\") = 0 And InStr(sName, "/") = 0 And InStr(sName, "*") = 0 And InStr(sName, "?") = 0 And InStr(sName, Chr(34)) = 0 And InStr(sName, "|") = 0 And InStr(sName, ":") = 0 And InStr(sName, ">") = 0 And InStr(sName, "<") = 0)
  259. End Function