Source code of Windows XP (NT5)
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.

456 lines
14 KiB

  1. /*
  2. * Windows Calendar
  3. * Copyright (c) 1985 by Microsoft Corporation, all rights reserved.
  4. * Written by Mark L. Chamberlin, consultant to Microsoft.
  5. *
  6. ****** calfile.c
  7. *
  8. */
  9. #include "cal.h"
  10. /**** CreateChangeFile *****/
  11. VOID APIENTRY CreateChangeFile ()
  12. {
  13. /* If there is already a change file, delete it, ignoring any errors
  14. since we will be creating a new one, and that's the important one.
  15. */
  16. DeleteChangeFile ();
  17. /* Set the end-of-data of the change file to block 0. */
  18. vobkEODChange = 0;
  19. /* By passing the drive letter as 0 we tell GetTempFileName
  20. to decide where to put the temp file.
  21. */
  22. if (!(vfChangeFile = FCreateTempFile (IDFILECHANGE, 0)))
  23. {
  24. /* Post error saying edits will not be recorded. */
  25. OutputDebugString ("Message Box Is Broken\n");
  26. //AlertBox (vszNoCreateChangeFile, (CHAR *)NULL,
  27. //MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  28. }
  29. }
  30. /**** DeleteChangeFile - delete the change file if there is one. ****/
  31. VOID APIENTRY DeleteChangeFile ()
  32. {
  33. if (vfChangeFile)
  34. {
  35. /* Ignore errors since callers don't care about them. */
  36. vfChangeFile = FALSE;
  37. FDeleteFile (IDFILECHANGE);
  38. }
  39. }
  40. /**** FCreateTempFile ****/
  41. BOOL APIENTRY FCreateTempFile (
  42. INT idFile,
  43. INT iDrive) /* 0 means let GetTempFileName decide where to put
  44. the temp file.
  45. Otherwise, this is the drive letter, and it should
  46. also have the TF_FORCEDRIVE bit set to make sure that
  47. the temp file is created on the specified drive.
  48. */
  49. {
  50. CHAR szFileSpec [CCHFILESPECMAX];
  51. INT FileHandle;
  52. /* Create a temp file with a unique name.
  53. * 0 for the third parameter means GetTempFileName should
  54. * produce a unique file name and create the file.
  55. * GetTempFileName returns the random number it used, which Steve Wood
  56. * guarantees is 0 iff the call fails (he does not allow the random
  57. * number to be 0). If the file is created OK by GetTempFileName,
  58. * open it to set up the reopen buffer.
  59. */
  60. if (MGetTempFileName ((BYTE)iDrive, "CAL", 0, szFileSpec) == 0)
  61. {
  62. return (FALSE);
  63. }
  64. if ((FileHandle = MOpenFile (szFileSpec, &OFStruct [idFile],
  65. OF_READWRITE )) != -1)
  66. {
  67. /* File is OK. Close it, and return TRUE. */
  68. M_lclose (FileHandle);
  69. return (TRUE);
  70. }
  71. /* GetTempFileName failed or OpenFile failed. */
  72. return (FALSE);
  73. }
  74. /**** FFreeUpDr - Free up the specified DR. ****/
  75. BOOL APIENTRY FFreeUpDr (
  76. DR *pdr, /* Pointer to the DR to be written out. */
  77. DL *pdl) /* OUTPUT - DL indicating where the occupant was put. */
  78. {
  79. DL dlNew;
  80. if (!pdr -> fDirty)
  81. {
  82. /* It's not dirty so don't change the date's location. */
  83. *pdl = DLNOCHANGE;
  84. return (TRUE);
  85. }
  86. /* The DR is dirty. However, it may be empty, in which case
  87. we tell the caller it is not on disk.
  88. */
  89. if (pdr -> cbNotes + pdr -> cbTqr == 0)
  90. {
  91. *pdl = DLNIL;
  92. return (TRUE);
  93. }
  94. /* It's not empty. */
  95. if (!vfChangeFile)
  96. {
  97. /* Tell the user that the edits are not being recorded.
  98. We already warned the turkey when we couldn't create
  99. the change file, but he didn't listen. Now just
  100. throw away his edits.
  101. */
  102. AlertBox (vszNoChangeFile,
  103. (CHAR *)NULL, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  104. *pdl = DLNOCHANGE;
  105. return (TRUE);
  106. }
  107. /* Set up the new DL using vobkEODChange before FWriteDrToFile
  108. changes it.
  109. */
  110. dlNew = DLFCHANGEFILEMASK | vobkEODChange;
  111. if (FWriteDrToFile (TRUE, IDFILECHANGE, pdr))
  112. {
  113. /* The write was successful - tell the caller about the
  114. new DL.
  115. */
  116. *pdl = dlNew;
  117. return (TRUE);
  118. }
  119. else
  120. {
  121. /* An error occured while attempting to write the date. */
  122. AlertBox (vszErrorWritingChanges, (CHAR *)NULL,
  123. MB_APPLMODAL | MB_OK | MB_ICONHAND);
  124. return (FALSE);
  125. }
  126. }
  127. /**** FWriteDrToFile *****/
  128. BOOL APIENTRY FWriteDrToFile (
  129. BOOL fOpenClose, /* If TRUE, the file must be opened and then closed.
  130. If FALSE, the file is already open and should be
  131. left open.
  132. */
  133. INT idFile, /* Which file to write to. */
  134. DR *pdr) /* Which DR to write from. */
  135. {
  136. INT *pobkEOD;
  137. INT cbkTransfer;
  138. INT cbTransfer;
  139. INT FileHandle;
  140. BOOL fOk;
  141. /* Set up a pointer to the appropriate EOD. */
  142. pobkEOD = &vobkEODNew;
  143. if (idFile == IDFILECHANGE)
  144. pobkEOD = &vobkEODChange;
  145. /* Try to reopen the file. */
  146. if (!fOpenClose || FReopenFile (idFile, OF_PROMPT | OF_CANCEL | OF_REOPEN | OF_READWRITE))
  147. {
  148. /* Make a local copy of the file handle to save code below. */
  149. FileHandle = hFile [idFile];
  150. /* Calculate the minimum number of BKs we must
  151. write out. Do this by taking the count of
  152. bytes in use in the DR and adding the count
  153. of bytes in a BK minus 1 in order to round up
  154. to the next BK. Then divide by the count
  155. of bytes in a BK to get the number of BKs
  156. to be written.
  157. */
  158. cbkTransfer = (pdr -> cbNotes + pdr -> cbTqr
  159. + CBDRHEAD + CBBK - 1) / CBBK;
  160. /* Clear the reserved word. */
  161. pdr -> wReserved = 0;
  162. /* Seek to the current end of data, write the
  163. current DR, and close the file.
  164. */
  165. cbTransfer = CBBK * cbkTransfer;
  166. fOk = M_llseek (FileHandle, (LONG)(CBBK*(*pobkEOD)), 0) != -1
  167. && FWriteFile (FileHandle, (BYTE *)pdr, cbTransfer);
  168. if (FCondClose (fOpenClose, idFile) && fOk)
  169. {
  170. /* The DR has been successfully written to the file.
  171. Update the EOD of the file.
  172. */
  173. *pobkEOD += cbkTransfer;
  174. return (TRUE);
  175. }
  176. }
  177. return (FALSE);
  178. }
  179. /**** FReadDrFromFile ****/
  180. BOOL APIENTRY FReadDrFromFile (
  181. BOOL fOpenClose, /* If TRUE, the file must be opened and then closed.
  182. If FALSE, the file is already open and should be
  183. left open.
  184. */
  185. DR *pdr, /* Where to read it into. */
  186. DL dl) /* File location of date. */
  187. {
  188. INT idFile;
  189. INT FileHandle;
  190. WORD cbData;
  191. OBK obk;
  192. BOOL fOk;
  193. /* Assume we will be reading from the original file. */
  194. idFile = IDFILEORIGINAL;
  195. /* Separate the block offset, and switch to the change file if the
  196. change file flag is set in the DL.
  197. */
  198. obk = dl & DLOBKMASK;
  199. if (dl & DLFCHANGEFILEMASK)
  200. idFile = IDFILECHANGE;
  201. /* Try to reopen the file. */
  202. if (fOpenClose && !FReopenFile (idFile, OF_PROMPT | OF_CANCEL | OF_REOPEN
  203. | OF_READ))
  204. return (FALSE);
  205. /* Reopen was successful - seek to the beginning of the DR, and
  206. read its header in order to know how much data there is.
  207. */
  208. FileHandle = hFile [idFile];
  209. if (M_llseek (FileHandle, (LONG)(CBBK * obk), 0) == -1
  210. || M_lread (FileHandle, (LPSTR)pdr, CBDRHEAD) != CBDRHEAD)
  211. {
  212. FCondClose (fOpenClose, idFile);
  213. return (FALSE);
  214. }
  215. /* Header was successfully read. Now read in the rest. */
  216. cbData = pdr -> cbNotes + pdr -> cbTqr;
  217. fOk = (WORD)M_lread (FileHandle, (LPSTR)pdr + CBDRHEAD, cbData) == cbData;
  218. /* Close the file. */
  219. return (FCondClose (fOpenClose, idFile) && fOk);
  220. }
  221. /**** FGetDateDr ****/
  222. BOOL APIENTRY FGetDateDr (DT dtTarget)
  223. {
  224. DR *pdr;
  225. INT itdd;
  226. DD *pdd;
  227. DL dlTarget;
  228. WORD idrTarget;
  229. WORD idrNew;
  230. WORD idrKickOut;
  231. DL dlKickOut;
  232. DT dtKickOut;
  233. HWND hwndFocus;
  234. /* If this routine succeeds in getting the requested date, the
  235. focus is left NULL. If it fails, the focus is set back to its
  236. previous window. Remember who has the focus now, then set it
  237. NULL to record the current edits into the current date. This MUST be
  238. done before switching dates so the data goes into the correct
  239. date. It's important to leave the focus NULL in the success case
  240. so that the data doesn't get recorded again (into the wrong date)
  241. when the caller changes the focus later.
  242. */
  243. hwndFocus = GetFocus ();
  244. CalSetFocus ((HWND)NULL);
  245. /* See if this date is already in the tdd. */
  246. if (!FSearchTdd (dtTarget, &itdd))
  247. {
  248. /* Not found, try to insert it. */
  249. if (!FGrowTdd (itdd, 1))
  250. {
  251. /* Cannot grow tdd to include the new date. FGrowTdd has
  252. already put up the error message.
  253. */
  254. CalSetFocus (hwndFocus);
  255. return (FALSE);
  256. }
  257. /* Put the date into the new entry, say it is not marked,
  258. it has no alarms, it's not on disk, and it's not in memory.
  259. */
  260. pdd = TddLock () + itdd;
  261. pdd -> dt = dtTarget;
  262. pdd -> fMarked = FALSE;
  263. pdd -> cAlarms = 0;
  264. pdd -> dl = DLNIL;
  265. pdd -> idr = IDRNIL;
  266. TddUnlock ();
  267. }
  268. /* At this point itdd is the index of the target date
  269. within the tdd. See if the target date is already in memory.
  270. */
  271. dlTarget = (pdd = TddLock () + itdd) -> dl;
  272. idrTarget = pdd -> idr;
  273. TddUnlock ();
  274. if ((WORD)idrTarget != IDRNIL)
  275. {
  276. /* The target date is already in memory. Make the DR it
  277. is stored in the current DR, and return TRUE.
  278. */
  279. vidrCur = idrTarget;
  280. return (TRUE);
  281. }
  282. /* Find a free DR to put the target date in. */
  283. idrNew = IdrFree ();
  284. /* In order to comply with the rule that there is always one
  285. free DR, we have to kick out a date if the 2 DRs other
  286. than idrNew are both in use. Here's how we decide which, if
  287. any, date must be kicked out of memory:
  288. - Look at each DR:
  289. - If it's idrNew, or it's in use for today, skip it.
  290. So we end up either kicking nothing out (if we find a second
  291. free DR), or kicking out a date that's not today.
  292. Since the same date can't be in two DRs
  293. at the same time, we know that we will either find another
  294. free DR or one that contains a date other than today, so this
  295. loop will terminate.
  296. Note - 9/2/85 - MLC - I originally kept today in memory at all
  297. times because in month mode I always displayed the notes for
  298. today, regardless of which day was selected. Some time ago I
  299. changed month mode so it shows the notes for the selected date.
  300. I decided to still keep today around since I assume the user
  301. will be refering to it more than any other date, so it seemed
  302. better than just keeping the two most recently accessed dates
  303. around.
  304. */
  305. idrKickOut = CDR;
  306. do
  307. {
  308. idrKickOut--;
  309. dtKickOut = PdrLock (idrKickOut) -> dt;
  310. DrUnlock (idrKickOut);
  311. }
  312. while (idrKickOut == idrNew || dtKickOut == vftCur.dt);
  313. if (dtKickOut != DTNIL)
  314. {
  315. /* We must kick out a date to free up a DR. */
  316. pdr = PdrLock (idrKickOut);
  317. if (!FFreeUpDr (pdr, &dlKickOut))
  318. {
  319. /* If we just created the DD for the target date,
  320. it is still empty, so we will get rid of it if so.
  321. */
  322. DrUnlock (idrKickOut);
  323. DeleteEmptyDd (itdd);
  324. CalSetFocus (hwndFocus);
  325. return (FALSE);
  326. }
  327. pdr -> fDirty = FALSE;
  328. DrUnlock (idrKickOut);
  329. }
  330. pdr = PdrLock (idrNew);
  331. if (dlTarget == DLNIL)
  332. {
  333. /* No previous data for this date so create an empty DR for it. */
  334. pdr -> cbNotes = pdr -> cbTqr = 0;
  335. }
  336. else
  337. {
  338. if (!FReadDrFromFile (TRUE, pdr, dlTarget))
  339. {
  340. /* Mark the DR as still not in use. */
  341. pdr -> dt = DTNIL;
  342. DrUnlock (idrNew);
  343. /* If we just created the DD for the target date,
  344. it is still empty, so we will get rid of it if so.
  345. */
  346. DeleteEmptyDd (itdd);
  347. CalSetFocus (hwndFocus);
  348. return (FALSE);
  349. }
  350. }
  351. /* Could be here if no previous data or
  352. where date was successfully read from file.
  353. Therefore, set the dt, since in the first case it has not
  354. been set.
  355. */
  356. pdr -> dt = dtTarget;
  357. pdr -> fDirty = FALSE;
  358. DrUnlock (idrNew);
  359. (TddLock () + itdd) -> idr = idrNew;
  360. TddUnlock ();
  361. if (dtKickOut != DTNIL)
  362. {
  363. /* We kicked out a date. Mark that DR free. */
  364. PdrLock (idrKickOut) -> dt = DTNIL;
  365. DrUnlock (idrKickOut);
  366. /* Search for the DD of the date we kicked out. It's OK to
  367. ignore the return value of FSearchTdd since the date we
  368. kicked out must be in the tdd.
  369. */
  370. FSearchTdd (dtKickOut, &itdd);
  371. /* Say the kicked out date is no longer in memory, and change
  372. the DL if the date's location has changed.
  373. */
  374. (pdd = TddLock () + itdd) -> idr = IDRNIL;
  375. if (dlKickOut != DLNOCHANGE)
  376. pdd -> dl = dlKickOut;
  377. TddUnlock ();
  378. /* Get rid of the DD of the kicked out date if it's "empty". */
  379. DeleteEmptyDd (itdd);
  380. }
  381. vidrCur = idrNew;
  382. return (TRUE);
  383. }