Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

393 lines
13 KiB

  1. /*
  2. * CSVParse.C
  3. *
  4. * CSV Parsing functions
  5. *
  6. * Copyright 1997 Microsoft Corporation. All Rights Reserved.
  7. */
  8. #include <windows.h>
  9. #include <wab.h>
  10. #include <wabguid.h>
  11. #include <wabdbg.h>
  12. #include "dbgutil.h"
  13. #include <shlwapi.h>
  14. #define CR_CHAR 0x0d
  15. #define LF_CHAR 0x0a
  16. #define CCH_READ_BUFFER 256
  17. #define NUM_ITEM_SLOTS 32
  18. /***************************************************************************
  19. Name : ReadCSVChar
  20. Purpose : Reads a single char from a file
  21. Parameters: hFile = file handle
  22. pcbBuffer = pointer to size of buffer
  23. lppBuffer = pointer to pointer to buffer
  24. lppRead = pointer to pointer to next location to use
  25. Returns : -1 = Out of memory
  26. 0 = End of file
  27. 1 = Char successfully read
  28. Comment : Dynamically grows *lppBuffer as necessary
  29. ***************************************************************************/
  30. int ReadCSVChar(HANDLE hFile, int *pcbBuffer, PUCHAR *lppBuffer, PUCHAR *lppRead)
  31. {
  32. int cbOffset;
  33. ULONG cbReadFile;
  34. PUCHAR lpBuffer;
  35. cbOffset = (int) (*lppRead - *lppBuffer);
  36. if (cbOffset >= *pcbBuffer)
  37. {
  38. // Buffer is too small. Reallocate!
  39. *pcbBuffer += CCH_READ_BUFFER;
  40. if (! (lpBuffer = LocalReAlloc(*lppBuffer, *pcbBuffer, LMEM_MOVEABLE | LMEM_ZEROINIT)))
  41. {
  42. DebugTrace("LocalReAlloc(%u) -> %u\n", *pcbBuffer, GetLastError());
  43. return(-1);
  44. }
  45. *lppBuffer = lpBuffer;
  46. *lppRead = *lppBuffer + cbOffset;
  47. }
  48. // 1 character at a time
  49. if (ReadFile(hFile, *lppRead, 1, &cbReadFile, NULL) && cbReadFile)
  50. return(1);
  51. return(0);
  52. }
  53. /***************************************************************************
  54. Name : ReadCSVItem
  55. Purpose : Reads an item from a CSV file
  56. Parameters: hFile = file handle
  57. pcbBuffer = pointer to size of buffer
  58. lppBuffer = pointer to pointer to buffer
  59. szSep = current separator string
  60. Returns : -1 = Out of memory
  61. 0 = Item read in, and none left
  62. 1 = Item read in, more items left
  63. Comment : CSV special characters are '"', szSep, CR and LF.
  64. Rules for quotes:
  65. 1. If an item starts with a '"', then the item is quoted
  66. and must end with a '"'.
  67. 2. Any '"' characters found in a non-quoted string will not
  68. be treated specially. Technically, there should not be
  69. quotes in a non-quoted string, but we have to do
  70. something if we find one.
  71. 3. A quoted item ends with:
  72. a) quote szSep
  73. b) quote newline
  74. or, c) quote <EOF>
  75. 4. Two quotes together in a quoted string are translated
  76. into a single quote.
  77. ***************************************************************************/
  78. int ReadCSVItem(HANDLE hFile, int *pcbBuffer, PUCHAR *lppBuffer, LPTSTR szSep)
  79. {
  80. BOOL fQuoted, fDone, fFoundSepCh;
  81. int cbReadFile;
  82. PUCHAR lpRead, szSepT;
  83. // This function is always called with one character already read
  84. lpRead = *lppBuffer;
  85. if (*lpRead == '"')
  86. {
  87. fQuoted = TRUE;
  88. cbReadFile = ReadCSVChar(hFile, pcbBuffer, lppBuffer, &lpRead);
  89. }
  90. else
  91. {
  92. fQuoted = FALSE;
  93. cbReadFile = 1;
  94. }
  95. szSepT = szSep;
  96. fDone = FALSE;
  97. do
  98. {
  99. if (cbReadFile <= 0)
  100. {
  101. // End of file means end of item
  102. if (cbReadFile == 0)
  103. *lpRead = '\0';
  104. break;
  105. }
  106. fFoundSepCh = FALSE;
  107. switch (*lpRead)
  108. {
  109. case CR_CHAR:
  110. case LF_CHAR:
  111. if (!fQuoted)
  112. {
  113. // End of line and item
  114. *lpRead = '\0';
  115. cbReadFile = 0;
  116. fDone = TRUE;
  117. }
  118. break;
  119. case '"':
  120. if (fQuoted)
  121. {
  122. // See if the next character is a quote, CR, or LF
  123. lpRead++;
  124. cbReadFile = ReadCSVChar(hFile, pcbBuffer, lppBuffer, &lpRead);
  125. if ((cbReadFile <= 0) || (*lpRead == '"') || (*lpRead == CR_CHAR) || (*lpRead == LF_CHAR))
  126. {
  127. if ((cbReadFile <= 0) || (*lpRead != '"'))
  128. {
  129. if (cbReadFile >= 0)
  130. {
  131. // End of file, or CR or LF
  132. *(lpRead - 1) = '\0';
  133. cbReadFile = 0;
  134. }
  135. // else out of memory
  136. fDone = TRUE;
  137. }
  138. else
  139. {
  140. // Embedded quote - get rid of one
  141. lpRead--;
  142. }
  143. break;
  144. }
  145. // We have read another character, and it is not a quote, CR, or LF
  146. // Two possibilities:
  147. // 1) Separator
  148. // 2) Something else - this is an error condition
  149. szSepT = szSep;
  150. while ((cbReadFile > 0) && (*szSepT != '\0') && (*lpRead == *szSepT))
  151. {
  152. szSepT++;
  153. if (*szSepT != '\0')
  154. {
  155. lpRead++;
  156. cbReadFile = ReadCSVChar(hFile, pcbBuffer, lppBuffer, &lpRead);
  157. }
  158. }
  159. if ((cbReadFile <= 0) || (*szSepT == '\0'))
  160. {
  161. if (cbReadFile >= 0)
  162. {
  163. // If cbReadFile is zero, we hit the end of file
  164. // before finding the complete separator. In this
  165. // case, we simply take every character we have
  166. // read, including the second quote, and use that
  167. // as the item.
  168. //
  169. // Otherwise, we found the complete separator.
  170. if (cbReadFile > 0)
  171. lpRead -= lstrlen(szSep);
  172. *lpRead = '\0';
  173. }
  174. fDone = TRUE;
  175. }
  176. else
  177. {
  178. // We found a second quote, but it was not followed by a
  179. // separator. In this case, we keep reading as if we are
  180. // in an unquoted string.
  181. fQuoted = FALSE;
  182. }
  183. }
  184. break;
  185. default:
  186. if (!fQuoted)
  187. {
  188. if (*lpRead == *szSepT)
  189. {
  190. szSepT++;
  191. if (*szSepT == '\0')
  192. {
  193. // End of separator, thus end of item
  194. lpRead -= (lstrlen(szSep) - 1);
  195. *lpRead = '\0';
  196. fDone = TRUE;
  197. }
  198. else
  199. fFoundSepCh = TRUE;
  200. }
  201. }
  202. break;
  203. }
  204. if (!fDone)
  205. {
  206. if (!fFoundSepCh)
  207. szSepT = szSep;
  208. lpRead++;
  209. cbReadFile = ReadCSVChar(hFile, pcbBuffer, lppBuffer, &lpRead);
  210. }
  211. }
  212. while (!fDone);
  213. return(cbReadFile);
  214. }
  215. /***************************************************************************
  216. Name : InsertItem
  217. Purpose : Takes an item read from the file and inserts it into the array
  218. Parameters: iItem = array index to insert
  219. pcItemSlots = number of currently allocated elements
  220. cGrow = number of items to grow the array by, if necessary
  221. prgItemSlots = pointer to the actual array
  222. lpBuffer = string to insert
  223. Returns : TRUE = item successfully inserted
  224. FALSE = out of memory
  225. ***************************************************************************/
  226. BOOL InsertItem(int iItem, int *pcItemSlots, int cGrow, PUCHAR **prgItemSlots, PUCHAR lpBuffer)
  227. {
  228. PUCHAR *rgItemSlotsNew, lpItem;
  229. // Make sure there's room, first
  230. if (iItem >= *pcItemSlots)
  231. {
  232. // Array is too small. Reallocate!
  233. *pcItemSlots += cGrow;
  234. rgItemSlotsNew = LocalReAlloc(*prgItemSlots, *pcItemSlots * sizeof(PUCHAR), LMEM_MOVEABLE | LMEM_ZEROINIT);
  235. if (!rgItemSlotsNew)
  236. {
  237. DebugTrace("LocalReAlloc(%u) -> %u\n", *pcItemSlots * sizeof(PUCHAR), GetLastError());
  238. return(FALSE);
  239. }
  240. *prgItemSlots = rgItemSlotsNew;
  241. }
  242. lpItem = LocalAlloc(LPTR, lstrlen(lpBuffer) + 1);
  243. if (!lpItem)
  244. {
  245. DebugTrace("LocalAlloc(%u) -> %u\n", lstrlen(lpBuffer) + 1, GetLastError());
  246. return(FALSE);
  247. }
  248. StrCpyN(lpItem, lpBuffer, lstrlen(lpBuffer) + 1);
  249. (*prgItemSlots)[iItem] = lpItem;
  250. return(TRUE);
  251. }
  252. /***************************************************************************
  253. Name : ReadCSVLine x
  254. Purpose : Reads a line from a CSV file with fixups for special characters
  255. Parameters: hFile = file handle
  256. szSep = list separator of the current regional settings
  257. lpcItems -> Returned number of items
  258. lprgItems -> Returned array of item strings. Caller is
  259. responsible for LocalFree'ing each string pointer and
  260. this array pointer.
  261. Returns : HRESULT
  262. Comment : Calls the above helper functions to do most of the work.
  263. ***************************************************************************/
  264. HRESULT ReadCSVLine(HANDLE hFile, LPTSTR szSep, ULONG * lpcItems, PUCHAR ** lpprgItems) {
  265. HRESULT hResult = hrSuccess;
  266. register ULONG i;
  267. PUCHAR lpBuffer = NULL, lpRead, lpItem;
  268. ULONG cbBuffer = 0;
  269. int cbReadFile = -1;
  270. UCHAR chLastChar;
  271. ULONG iItem = 0;
  272. ULONG cItemSlots = 0;
  273. PUCHAR * rgItemSlots = NULL;
  274. LPTSTR szSepT;
  275. // Start out with 1024 character buffer. Realloc as necesary.
  276. cbBuffer = CCH_READ_BUFFER;
  277. if (! (lpRead = lpBuffer = LocalAlloc(LPTR, cbBuffer))) {
  278. DebugTrace("LocalAlloc(%u) -> %u\n", cbBuffer, GetLastError());
  279. goto exit;
  280. }
  281. // Start out with 32 item slots. Realloc as necesary.
  282. cItemSlots = NUM_ITEM_SLOTS;
  283. if (! (rgItemSlots = LocalAlloc(LPTR, cItemSlots * sizeof(PUCHAR)))) {
  284. DebugTrace("LocalAlloc(%u) -> %u\n", cItemSlots * sizeof(PUCHAR), GetLastError());
  285. goto exit;
  286. }
  287. // Skip past leading CR/LF characters
  288. do
  289. cbReadFile = ReadCSVChar(hFile, &cbBuffer, &lpBuffer, &lpRead);
  290. while((cbReadFile > 0) && ((*lpBuffer == CR_CHAR) || (*lpBuffer == LF_CHAR)));
  291. if (cbReadFile == 0)
  292. {
  293. // Nothing to return
  294. DebugTrace("ReadFile -> EOF\n");
  295. hResult = ResultFromScode(MAPI_E_NOT_FOUND);
  296. }
  297. // Read items until end of line or EOF
  298. while (cbReadFile > 0)
  299. {
  300. cbReadFile = ReadCSVItem(hFile, &cbBuffer, &lpBuffer, szSep);
  301. if (cbReadFile >= 0)
  302. {
  303. // Dup the item into the next array slot.
  304. if (!InsertItem(iItem, &cItemSlots, cbReadFile ? NUM_ITEM_SLOTS : 1, &rgItemSlots, lpBuffer))
  305. cbReadFile = -1;
  306. else
  307. iItem++;
  308. if (cbReadFile > 0)
  309. {
  310. // More data to be read
  311. lpRead = lpBuffer;
  312. cbReadFile = ReadCSVChar(hFile, &cbBuffer, &lpBuffer, &lpRead);
  313. }
  314. }
  315. }
  316. exit:
  317. if (cbReadFile < 0)
  318. hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
  319. if (lpBuffer)
  320. {
  321. LocalFree(lpBuffer);
  322. }
  323. if (hResult)
  324. {
  325. // Clean up
  326. if (rgItemSlots)
  327. {
  328. for (i = 0; i < iItem; i++)
  329. {
  330. if (rgItemSlots[i])
  331. {
  332. LocalFree(rgItemSlots[i]);
  333. }
  334. }
  335. LocalFree(rgItemSlots);
  336. }
  337. *lpcItems = 0;
  338. *lpprgItems = NULL;
  339. }
  340. else
  341. {
  342. *lpcItems = iItem; // One based
  343. *lpprgItems = rgItemSlots;
  344. }
  345. return(hResult);
  346. }