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.

1211 lines
40 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. ****** calfile2.c
  7. *
  8. */
  9. #include "cal.h"
  10. #include "memory.h"
  11. extern BOOL f24Time;
  12. CHAR * APIENTRY PFileInPath(
  13. CHAR *sz);
  14. /**** FCopyToNewFile - copy the active dates from the specified file
  15. (original or change) to the new file.
  16. ****/
  17. BOOL APIENTRY FCopyToNewFile (
  18. INT idFileSource, /* The id of the source file. */
  19. DR *pdr, /* Pointer to DR to use as a buffer. */
  20. DD *pddFirst, /* Pointer to locked tdd. */
  21. DD *pddMax) /* Pointer beyond locked tdd. */
  22. {
  23. register BOOL fSwap;
  24. register DD *pddCur;
  25. DL dl;
  26. /* If the source file does not exist, there is no work to be done,
  27. so return right away, indicating success. (The original file
  28. doesn't exist if we're as yet untitled, and the change file
  29. doesn't exist if it was impossible to create it (and the user
  30. was told of the error when the attempt to create the change
  31. file failed).)
  32. */
  33. if (idFileSource == IDFILEORIGINAL && !vfOriginalFile
  34. || idFileSource == IDFILECHANGE && !vfChangeFile)
  35. return (TRUE);
  36. /* Try to reopen the source file, returning an error if the open fails. */
  37. if (!FReopenFile (idFileSource, OF_PROMPT | OF_CANCEL | OF_REOPEN | OF_READWRITE))
  38. goto error1;
  39. /* See if the destination file is available without swapping diskettes.
  40. If it is, leave the source and destination files both open.
  41. If it is not available, leave both files closed.
  42. */
  43. if (fSwap = !FReopenFile (IDFILENEW, OF_REOPEN | OF_READWRITE))
  44. {
  45. /* Don't worry about errors closing the files.
  46. If there's really a problem, it will be detected when we try
  47. to use the files below.
  48. */
  49. FCloseFile (idFileSource);
  50. FCloseFile (IDFILENEW);
  51. }
  52. /* Make a pass through the tdd looking for dates that are
  53. in the source file. Copy these dates into the destination file.
  54. */
  55. for (pddCur = pddFirst; pddCur < pddMax; pddCur++)
  56. {
  57. /* If this date has not already been transferred, and it's not
  58. a special DL, and it resides in the source file we're working
  59. on, copy the data for the date.
  60. */
  61. if (pddCur -> dlSave == DLNOCHANGE
  62. && (dl = pddCur -> dl) < DLSPECIALLOW
  63. && ((dl & DLFCHANGEFILEMASK) && idFileSource == IDFILECHANGE
  64. || !(dl & DLFCHANGEFILEMASK) && idFileSource == IDFILEORIGINAL))
  65. {
  66. if (!FReadDrFromFile (fSwap, pdr, dl))
  67. {
  68. /* An error occurred,
  69. so we tell the caller to give up the Save.
  70. */
  71. goto error1;
  72. }
  73. /* Remember the current DL, and set the new one to be the
  74. place where we are about to write the date to in the
  75. new file.
  76. */
  77. pddCur -> dlSave = dl;
  78. pddCur -> dl = (DL)vobkEODNew;
  79. /* Try to write the date to the new file. If an error
  80. occurs, the Save will be aborted. It may be due
  81. to a disk full condition or some I/O error.
  82. */
  83. if (!FWriteDrToFile (fSwap, IDFILENEW, pdr))
  84. goto error1;
  85. }
  86. }
  87. /* If we weren't swapping, we need to close the files (and it's harmless
  88. to call FCloseFile for an unopen file).
  89. We ignore errors closing the source file since no modifications
  90. were made to it, we are done with it, and an error here has no
  91. effect on the integrity of the new file (which is what the user
  92. really cares about now). And anyway, what sort of error could
  93. occur when closing a file that has only been read from?
  94. As for closing the new file, if an error occurs, we return it
  95. as an I/O error, which will cause the Save to be aborted.
  96. (Can't ignore error closing the new file, since an error here
  97. means there may be something wrong with it.) By the way, according
  98. to Chris Peters, close cannot cause a Disk Full error since it
  99. cannot cause any disk space allocation. Close does update the
  100. directory entry though. Therefore, the only the only
  101. kind of error that can occur during a close is an I/O error.
  102. Chris says most programmers do not bother to check for errors
  103. when closing files. I still think it's the prudent thing to
  104. to when closing a file that one has written to.
  105. */
  106. FCloseFile (idFileSource);
  107. return (FCloseFile (IDFILENEW));
  108. error1: /* An error occurred - close all files, and return FALSE. */
  109. FCloseFile (idFileSource);
  110. FCloseFile (IDFILENEW);
  111. return (FALSE);
  112. }
  113. /**** FSaveFile - the guts of Save and Save As. ****/
  114. BOOL APIENTRY FSaveFile (
  115. CHAR *szFileSpec, /* Name of the file to save to. ANSI STRING. */
  116. BOOL fOverwrite) /* TRUE means the old copy of the file with
  117. same name in the same directory as the new
  118. file should be overwritten by the new one
  119. if the Save is successful.
  120. */
  121. {
  122. DD *pddFirst, *pddMax, *pddCur;
  123. WORD idr;
  124. INT FileHandle, fp;
  125. DR *pdr;
  126. BOOL fOk;
  127. DL dl;
  128. /* Show the hour glass cursor. */
  129. HourGlassOn ();
  130. if ((fp = M_lopen((LPSTR)szFileSpec, 2)) < 0) /* fgd 9/20/89 */
  131. fp = M_lcreat((LPSTR)szFileSpec, 0); /* _lopen & _lcreat */
  132. /* use ANSI chrs */
  133. if (fp > 0)
  134. M_lclose(fp);
  135. else
  136. {
  137. AlertBox (vrgsz[IDS_NOCREATE], szFileSpec,
  138. MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  139. HourGlassOff();
  140. return FALSE;
  141. }
  142. /* Force edits to be recorded prior to flushing the DRs. Note that
  143. leaving the focus as is will work OK since the DR corresponding
  144. to vidrCur remains in memory during the Save. This means that
  145. if the focus is changed later, the correct date will still be
  146. around for use by StoreQd or StoreNotes. Since Save does not
  147. change the mode (day or month) we want the focus to stay where
  148. it was, so we don't do a CalSetFocus ((HWND)NULL) here.
  149. */
  150. RecordEdits ();
  151. /* Create the new file as a temporary - we will rename it when the
  152. save is finished. Note that we must force the temp file to
  153. be on the drive we want the new file on, since, of course, we can't
  154. rename across drives. Surprizingly though, we can rename across
  155. directories.
  156. */
  157. if (!FCreateTempFile (IDFILENEW, GetDrive (szFileSpec) | TF_FORCEDRIVE))
  158. goto error1;
  159. /* Calculate the number of BK required to hold the header and the
  160. index. Set the end of data of the new file in
  161. order to reserve the required space. Note that we don't actually
  162. write the header or the index until after the dates have been
  163. successfully written to the new file. This is done for two reasons:
  164. 1) By not writing the magic number until the end, we reduce the
  165. risk of ever trying to use a file that has the correct magic
  166. number but which is corrupt. (In other words, if an error
  167. causes the Save to be aborted, and for some reason the new
  168. file cannot be deleted, it probably won't look like a valid
  169. calendar file (although I suppose the disk space allocated
  170. to the file could contain the correct magic number - but I
  171. am not going to to bother to write something into the first byte
  172. of the file just to handle this unbelievably pathological case.
  173. 2) More importantly - we cannot write the index now because
  174. it does not yet contain the new DLs. These will be set as
  175. the dates are moved into the new file.
  176. Note that adding CBBK - 1 prior to dividing by CBBK has the
  177. effect of rounding up to the next bk.
  178. */
  179. vobkEODNew = 1 + (vcddUsed * sizeof (DD) + CBBK - 1) / CBBK;
  180. /* Mark each DD to indicate that its data has not yet been copied to
  181. the new file.
  182. */
  183. for (pddMax = (pddCur = TddLock ()) + vcddUsed; pddCur < pddMax; pddCur++)
  184. pddCur -> dlSave = DLNOCHANGE;
  185. TddUnlock ();
  186. /* Now we flush any dates that are in memory to the new file. */
  187. fOk = FFlushDr ();
  188. /* The tdd may have become smaller during FFlushDr since we may
  189. have deleted some empty DDs. Lock it, and set up First and Max
  190. pointers. These pointers are needed for error recovery, so we
  191. must do this prior to bailing out if FFlushDr failed.
  192. */
  193. pddMax = (pddFirst = TddLock ()) + vcddUsed;
  194. /* Bail out if FFlushDr encountered an error trying to write out
  195. one of the DRs.
  196. */
  197. if (!fOk)
  198. goto error2;
  199. /* Find a free DR to use as a buffer, and lock it. */
  200. idr = IdrFree ();
  201. pdr = PdrLock (idr);
  202. /* Copy the dates from the change file to the new file, then
  203. copy the dates from the original file to the new file.
  204. */
  205. fOk = FCopyToNewFile (IDFILECHANGE, pdr, pddFirst, pddMax)
  206. && FCopyToNewFile (IDFILEORIGINAL, pdr, pddFirst, pddMax);
  207. /* Make sure the DR we used as a buffer looks free, then
  208. unlock it.
  209. */
  210. pdr -> dt = DTNIL;
  211. DrUnlock (idr);
  212. /* If dates were copied OK, then try to write the header. If either
  213. operation failed, abort the Save.
  214. */
  215. if (!fOk || !FWriteHeader (pddFirst))
  216. goto error2;
  217. /* Finished with the tdd - unlock it. */
  218. TddUnlock ();
  219. /* Reconnect the DRs with their DDs. */
  220. Reconnect (TRUE);
  221. /* Delete the file being overwritten. Note that this is the
  222. file on the same device and directory with the same name
  223. as what our new file is to be named - it is not necessarily
  224. the original file. However, we use the reo of the original
  225. file since it is available at this point. So we call
  226. OpenFile to get the user to swap diskettes if necessary,
  227. and then we delete the file.
  228. If an error occurs during the delete, ignore it.
  229. The new file is in good shape, the only problem is that
  230. we won't be able to rename it to it's correct name since
  231. we couldn't delete the old copy. This will get detected
  232. when we attempt the rename (below), and the user will be
  233. told about the problem then. Bear in mind that if we can't
  234. delete the old file, we probably can't access it at all,
  235. and since it could be the original file, it would be a bad
  236. idea to abort the Save now.
  237. */
  238. if ((FileHandle = MOpenFile ((LPSTR)szFileSpec,
  239. (OFSTRUCT FAR *)&OFStruct [IDFILEORIGINAL],
  240. OF_PROMPT | OF_CANCEL | OF_READWRITE))
  241. != -1)
  242. {
  243. M_lclose (FileHandle);
  244. FDosDelete (OFStruct [IDFILEORIGINAL].szPathName);
  245. }
  246. /* Rename the new file. */
  247. fOk = FReopenFile (IDFILENEW, OF_REOPEN | OF_PROMPT | OF_CANCEL | OF_READWRITE);
  248. FCloseFile (IDFILENEW);
  249. if (!fOk || FDosRename (OFStruct [IDFILENEW].szPathName, szFileSpec))
  250. {
  251. /* Could not rename the file. Tell the user that his data is
  252. in the temp file with the funny name. Also change szFileSpec
  253. to point to the temporary file name since this is what we will
  254. be using now.
  255. This should rarely, if ever, occur. What could
  256. have gone wrong? An I/O error perhaps while trying to
  257. rewrite the directory entry? Anyway, we do our best
  258. to recover from the situation.
  259. */
  260. /* delete the file we created which the user wanted to save as.
  261. * bug fix. we were leaving it in existence with 0 length.
  262. * don't delete if we were just doing a save.
  263. * 21-Jun-1987. davidhab
  264. */
  265. if (!fOverwrite)
  266. FDosDelete(szFileSpec);
  267. szFileSpec = OFStruct [IDFILENEW].szPathName;
  268. AlertBox (vszRenameFailed, szFileSpec,
  269. MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  270. }
  271. /* The new file is now the original file. We need to set up
  272. OFStruct [IDFILEORIGINAL] accordingly. The simplest way to do this
  273. is to call OpenFile using szFileSpec - this will set up the reo.
  274. Call with OF_EXIST since this means the file will not be left open.
  275. Don't bother to check for errors since all we care about at this
  276. point is getting the reo set up, and this will presumably happen
  277. no matter what.
  278. */
  279. MOpenFile ((LPSTR)szFileSpec, (OFSTRUCT FAR *)&OFStruct [IDFILEORIGINAL], OF_EXIST);
  280. /* Delete and recreate the change file. If the delete fails, ignore
  281. it. If the new one can't be created CreateChangeFile will tell
  282. the user about the problem. (Note that it may be possible to
  283. create the new change file even if the old one can't be deleted
  284. since a different temporary file name will be used.)
  285. The point is, the Save has successfully completed, and not being
  286. able to delete the old change file or create the new change file
  287. should not abort the Save at this point.
  288. The only problem is, the user may think the Save was not successful
  289. if he sees the error message generated by CreateChangeFile. I am
  290. assuming that the chances of this failure occuring are extremely
  291. remote, and in any case the result is not catastrophic.
  292. */
  293. CreateChangeFile ();
  294. /* Everything is clean now. */
  295. vfDirty = FALSE;
  296. /* Set the new title. */
  297. SetTitle (szFileSpec);
  298. /* The waiting is over. */
  299. HourGlassOff ();
  300. return (TRUE);
  301. error2: /* Error while trying to flush the DRs, copy the dates,
  302. or write the header.
  303. */
  304. /* Set the DLs back to their old values. */
  305. for (pddCur = pddFirst; pddCur < pddMax; pddCur++)
  306. {
  307. if ((dl = pddCur -> dlSave) != DLNOCHANGE)
  308. pddCur -> dl = dl;
  309. }
  310. /* Unlock the tdd. */
  311. TddUnlock ();
  312. /* Try to delete the new file. If an error occurs, ignore it.
  313. We are already going to tell the user that a problem has occurred,
  314. and there is nothing we can do about it if the new file (which
  315. has a funny name) can't be deleted.
  316. */
  317. FDeleteFile (IDFILENEW);
  318. /* delete the file we created which the user wanted to save as.
  319. * bug fix. we were leaving it in existence with 0 length.
  320. * don't delete if we were doing just a save.
  321. * 21-Jun-1987. davidhab
  322. */
  323. if (!fOverwrite)
  324. FDosDelete(szFileSpec);
  325. /* Reconnect the DRs with their DDs. */
  326. Reconnect (FALSE);
  327. error1: /* Error attempting to create the new file. */
  328. /* Tell the user that the Save failed. */
  329. AlertBox (vszSaveFailed, (CHAR *)NULL,
  330. MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  331. /* The waiting is over. */
  332. HourGlassOff ();
  333. return (FALSE);
  334. }
  335. /**** Reconnect - reconnect DRs with their DDs, and make sure the
  336. selected date is in memory.
  337. ****/
  338. VOID APIENTRY Reconnect (BOOL fSaveOk)
  339. {
  340. register WORD idr;
  341. register DR *pdr;
  342. INT itdd;
  343. HWND hwndFocus;
  344. /* Look at all the DRs. */
  345. for (idr = 0; idr < CDR; idr++)
  346. {
  347. /* Get a pointer to the DR. */
  348. pdr = PdrLock (idr);
  349. /* Skip over DRs that are not in use. */
  350. if (pdr -> dt != DTNIL)
  351. {
  352. /* Reconnect it to its DD. Note that there must be a
  353. DD for it since when FFlushDr deleted a DD it had
  354. already marked the DR as free (which this one is not).
  355. */
  356. FSearchTdd (pdr -> dt, &itdd);
  357. (TddLock () + itdd) -> idr = idr;
  358. TddUnlock ();
  359. /* If the Save succeeded then the DR is now clean. If
  360. it failed we leave the dirty flag alone (it could be
  361. be either dirty or clean, depending on it's state
  362. before the Save was attempted).
  363. */
  364. if (fSaveOk)
  365. pdr -> fDirty = FALSE;
  366. }
  367. DrUnlock (idr);
  368. }
  369. /* We need to make sure the selected date is in memory. We know
  370. that this call to FGetDateDr cannot fail since the date is
  371. already in one of the DRs we just reconnected, or it has no
  372. data associated with it. In either case, it cannot require
  373. a disk read so no error can occur. It may no longer be in the
  374. tdd (FFlushDr may have deleted it), but in that case FGetDateDr
  375. will build a new DD for it, and we know there will be room for
  376. the new DD since it was in the tdd before the Save started
  377. and the DD could only have gotten smaller not larger.
  378. Also note that FGetDateDr will not have to kick out another
  379. date since the selected date was in a DR before the Save and
  380. we have not reassigned any of them, so no disk writes will
  381. be done either. So no errors can occur.
  382. */
  383. hwndFocus = GetFocus();
  384. FGetDateDr (DtFromPd3 (&vd3Sel));
  385. CalSetFocus(hwndFocus);
  386. }
  387. /**** GetDrive - extract the drive letter from a file spec.
  388. if none specified, return the current drive.
  389. ****/
  390. INT APIENTRY GetDrive (CHAR *szFileSpec)
  391. {
  392. CHAR *pch;
  393. /* Skip leading spaces. */
  394. pch = szFileSpec;
  395. SkipSpace (&pch);
  396. /* If the second character is a colon, the first character is a drive
  397. letter. Otherwise return the current drive.
  398. */
  399. #ifdef DBCS
  400. if( IsDBCSLeadByte(*pch) )
  401. return GetCurDrive();
  402. else
  403. return (*(pch + 1) == ':' ? *pch : GetCurDrive ());
  404. #else
  405. return (*(pch + 1) == ':' ? *pch : GetCurDrive ());
  406. #endif
  407. }
  408. /**** FFlushDr - iterate the DRs to:
  409. 1) free up empty ones (no data) and get rid of the associated DD
  410. if it too is empty.
  411. 2) write out non-empty dirty DRs to the new file.
  412. Return TRUE if no errors, FALSE if an error
  413. occurs while trying to flush one of the DRs.
  414. ****/
  415. BOOL APIENTRY FFlushDr ()
  416. {
  417. WORD idr;
  418. register DR *pdr;
  419. BOOL fOk;
  420. INT itdd;
  421. register DD *pdd;
  422. /* Look at all the DRs - stop if an error occurs while trying
  423. to write one of them to the new file.
  424. */
  425. for (idr = 0, fOk = TRUE; idr < CDR && fOk; idr++)
  426. {
  427. /* Get a pointer to the DR. */
  428. pdr = PdrLock (idr);
  429. /* Skip over DRs that are not in use. */
  430. if (pdr -> dt != DTNIL)
  431. {
  432. /* Break the connection between the DD and the DR.
  433. Note - the owner of the DR must be in the
  434. tdd, so don't bother checking the return value
  435. of FSearchTdd.
  436. */
  437. FSearchTdd (pdr -> dt, &itdd);
  438. (pdd = TddLock () + itdd) -> idr = IDRNIL;
  439. if (pdr -> cbNotes + pdr -> cbTqr == 0)
  440. {
  441. /* The DR is empty. Free it up. */
  442. pdr -> dt = DTNIL;
  443. /* Say the date is no longer on disk either. Since
  444. there is no longer any data associated with the
  445. date, even if the Save fails we want the DL to
  446. be DLNIL, so make both dlSave and dl DLNIL.
  447. */
  448. pdd -> dlSave = pdd -> dl = DLNIL;
  449. /* The DD may now be empty (if the data is not marked).
  450. If this is the case, get rid of
  451. the DD since we don't want to write it out to
  452. the new file. Even if the Save fails this is OK.
  453. If it's the selected date it will get reinserted
  454. into the DD, and if it's not the selected date it
  455. it not needed and will get reinserted if the user
  456. ever switches to it.
  457. Note that it's OK to call DeleteEmptyDd even though
  458. the tdd is locked since if it does a ReAlloc it
  459. will only be to make the tdd smaller.
  460. */
  461. DeleteEmptyDd (itdd);
  462. }
  463. else
  464. {
  465. /* The DR is not empty - see if it's dirty. */
  466. if (pdr -> fDirty)
  467. {
  468. /* Remember old disk location, set new one. */
  469. pdd -> dlSave = pdd -> dl;
  470. pdd -> dl = (DL)vobkEODNew;
  471. /* Write it to the new file. Note that we
  472. intentionally leave fDirty TRUE. This is
  473. necessary for putting things back the way
  474. they were if the Save fails.
  475. */
  476. fOk = FWriteDrToFile (TRUE, IDFILENEW, pdr);
  477. }
  478. }
  479. /* Unlock the tdd. */
  480. TddUnlock ();
  481. }
  482. /* Unlock the DR. */
  483. DrUnlock (idr);
  484. }
  485. return (fOk);
  486. }
  487. /**** FCloseFile - close he specified file if it's open. Return TRUE
  488. if the file is successfully closed. Assume this happens since
  489. M_lclose does not give us an error return anyway.
  490. Note -
  491. According to Chris Peters, close cannot cause a Disk Full error since
  492. it cannot cause any disk space allocation. Close does update the
  493. directory entry though. Therefore, the only the only
  494. kind of error that can occur during a close is an I/O error.
  495. Chris says most programmers do not bother to check for errors
  496. when closing files. I still think it's the prudent thing to
  497. to when closing a file that one has written to.
  498. ****/
  499. BOOL APIENTRY FCloseFile (INT idFile)
  500. {
  501. INT FileHandle;
  502. if ((FileHandle = hFile[idFile]) == -1) // Q: no valid handle?
  503. return (TRUE); // Y: no need to close file
  504. else
  505. {
  506. hFile[idFile] = -1;
  507. M_lclose(FileHandle); // close file,
  508. return TRUE; // return success always
  509. }
  510. }
  511. /**** FWriteHeader - write out the file header and index.
  512. Return TRUE if successful, FALSE if an error occurs.
  513. ****/
  514. BOOL APIENTRY FWriteHeader (DD*pddFirst)
  515. {
  516. BYTE bkBuf [CBBK];
  517. register BYTE *pb;
  518. register BOOL fOk;
  519. INT FileHandle;
  520. /* Reopen the file, seek to the beginning of the second BK, and write
  521. the index (tdd).
  522. */
  523. fOk=FReopenFile(IDFILENEW, OF_REOPEN | OF_PROMPT | OF_CANCEL | OF_READWRITE)
  524. && M_llseek ((FileHandle = hFile [IDFILENEW]), (LONG)CBBK, 0) != -1
  525. && FWriteFile (FileHandle, (BYTE *)pddFirst, (WORD)(vcddUsed * sizeof (DD)));
  526. if (fOk)
  527. {
  528. //- Save Header: Need a 16 bit word to copy the header info into.
  529. WORD wTemp;
  530. /* Build a header bk containing the magic number, the
  531. size of the index, and the options.
  532. Unused bytes are set to 0 and are reserved for future use.
  533. */
  534. FillBuf (bkBuf, CBBK, 0);
  535. pb = BltByte ((BYTE *)vrgbMagic, (BYTE *)bkBuf, CBMAGIC);
  536. //- Save Header: Copy to temporary 16 bit storage then assign.
  537. wTemp = (WORD)vcddUsed;
  538. pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBCDD);
  539. wTemp = (WORD)vcMinEarlyRing;
  540. pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBMINEARLYRING);
  541. wTemp = (WORD)vfSound;
  542. pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBFSOUND);
  543. wTemp = (WORD)vmdInterval;
  544. pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBMDINTERVAL);
  545. wTemp = (WORD)vcMinInterval;
  546. pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBMININTERVAL);
  547. wTemp = (WORD)vfHour24;
  548. pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBFHOUR24);
  549. pb = BltByte ((BYTE *)&vtmStart, (BYTE *)pb, CBTMSTART);
  550. /* Write the header into the first BK of the file. */
  551. fOk = M_llseek (FileHandle, (LONG)0, 0) != -1
  552. && FWriteFile (FileHandle, bkBuf, CBBK);
  553. }
  554. /* Close the file. Note that calling FCloseFile is Ok even if the
  555. file was not successfully opened. Also note that the FCloseFile
  556. call must be separate from the other operations and must be done
  557. before checking fOk since we want the file to be closed regardless
  558. of whether an error has occurred.
  559. */
  560. return (FCloseFile (IDFILENEW) && fOk);
  561. }
  562. /**** FWriteFile - write to file.
  563. If a disk full error occurs, put up a message box for the user.
  564. If some other type of error occurs (I/O error), assume that the user
  565. has already seen the INT 24 Abort, Retry, Ignore dialog, so no need
  566. to put up another message. Return FALSE if error occurs, TRUE if
  567. write is successful.
  568. ****/
  569. BOOL APIENTRY FWriteFile (
  570. INT FileHandle, /* Handle of file to write to. */
  571. BYTE *pb, /* Pointer to bytes to be writeen. */
  572. UINT cb) /* COunt of bytes to write. */
  573. {
  574. /* Do the write. Return TRUE if it's successful.
  575. Note that due to some ambiquity about what write returns
  576. if it runs out of disk space (the C manual talks about -1 for
  577. an error, but at the same time says the count of bytes written
  578. could be less than specified but positive if the write runs
  579. out of disk space), we call it a bad write if the return value
  580. (count of bytes written) is not cb (the number of bytes we say to
  581. write). This works for both cases.
  582. */
  583. if ((WORD)_lwrite (FileHandle, (LPSTR)pb, cb) == cb)
  584. return (TRUE);
  585. /* Put up disk full message if that's the error that occurred. */
  586. /* Assume out of disk space. */
  587. #ifdef DISABLE
  588. if (_errno == ENOSPC)
  589. {
  590. #endif
  591. /* Need to make this system modal since the file is still
  592. open and it's against the rules to relinquish control with
  593. files open.
  594. */
  595. AlertBox (vszDiskFull, (CHAR *)NULL,
  596. MB_SYSTEMMODAL | MB_OK | MB_ICONEXCLAMATION);
  597. #ifdef DISABLE
  598. }
  599. #endif
  600. return (FALSE);
  601. }
  602. /**** FDeleteFile - delete the specified file. Return TRUE if successful,
  603. FALSE if an error occurs.
  604. ****/
  605. BOOL APIENTRY FDeleteFile (INT idFile)
  606. {
  607. register BOOL fOk;
  608. /* Make sure the file exists, prompting for it if necessary. Then
  609. try to delete it.
  610. */
  611. fOk=FReopenFile(idFile, OF_REOPEN | OF_PROMPT | OF_CANCEL | OF_READWRITE);
  612. FCloseFile (idFile);
  613. return (fOk && FDosDelete (OFStruct [idFile].szPathName));
  614. }
  615. /**** FReopenFile - */
  616. BOOL APIENTRY FReopenFile (
  617. INT idFile,
  618. WORD wFlags)
  619. {
  620. hFile[idFile]= MOpenFile ((LPSTR)vrgsz [IDS_GIVEMEFIRST + idFile],
  621. (OFSTRUCT FAR *)&OFStruct[idFile], wFlags);
  622. return (hFile[idFile]!=-1);
  623. }
  624. /**** SetTitle ****/
  625. VOID APIENTRY SetTitle (CHAR *sz)
  626. {
  627. /* We know that the sz we are being passed has been through
  628. DlgCheckFilename at some point, so it has to contain a valid
  629. filename and extension and no longer has any trailing blanks.
  630. Therefore we know that this buffer is large enough.
  631. */
  632. CHAR szWindowText [CCHSZWINDOWTEXTMAX];
  633. /* Set the flag indicating if there is an open calendar file. */
  634. if (vfOriginalFile = sz != vszUntitled)
  635. {
  636. /* Convert file name to upper case. Note - "(untitled)" should
  637. not be converted to upper case.
  638. */
  639. lstrcpy( vszFileSpec, sz);
  640. AnsiUpper ((LPSTR)vszFileSpec);
  641. }
  642. else
  643. lstrcpy (vszFileSpec, sz);
  644. /* Strip the path name, build the title string,
  645. and tell Windows about it.
  646. */
  647. lstrcat (lstrcpy (szWindowText, vszCalendarDash), PFileInPath (vszFileSpec));
  648. SetWindowText (vhwnd0, (LPSTR)szWindowText);
  649. }
  650. /**** FCondClose - conditionally close the specified file. */
  651. BOOL APIENTRY FCondClose (
  652. BOOL fClose, /* FALSE means don't close the file, TRUE means do. */
  653. INT idFile) /* The id of the file to close. */
  654. {
  655. /* If we're not supposed to close the file, just return TRUE.
  656. If we are, return the result of FCloseFile.
  657. */
  658. return (!fClose || FCloseFile (idFile));
  659. }
  660. /**** CleanSlate ****/
  661. VOID APIENTRY CleanSlate (BOOL fShowDate)
  662. {
  663. register WORD idr;
  664. /* Show the hour glass cursor. */
  665. HourGlassOn ();
  666. /* Take the focus away so that the current edits get recorded now
  667. and not later when the slate is suppose to be clean. (We don't
  668. care about the current edits since we are about to throw away
  669. everything, but if we were to leave the focus on an edit control,
  670. its contents would get stored when the focus gets set later. This
  671. would make the file dirty with data that's supposed to
  672. be gone.)
  673. */
  674. CalSetFocus ((HWND)NULL);
  675. /* Say everything is clean. */
  676. vfDirty = FALSE;
  677. /* Say there is no next alarm, and forget about any unacknowledged
  678. ones.
  679. */
  680. vftAlarmNext.dt = vftAlarmFirst.dt = DTNIL;
  681. /* Mark all DR as available. */
  682. for (idr = 0; idr < CDR; idr++)
  683. {
  684. PdrLock (idr) -> dt = DTNIL;
  685. DrUnlock (idr);
  686. }
  687. /* Get rid of all entries in the tdd. */
  688. InitTdd ();
  689. /* Set all options to their default values. */
  690. vcMinEarlyRing = 0;
  691. vfSound = TRUE;
  692. vmdInterval = MDINTERVAL60;
  693. vcMinInterval = 60;
  694. InitTimeDate(vhInstance, 0);
  695. vfHour24 = f24Time;
  696. vtmStart = 7 * 60; /* changed from 8 to 7 11/27/88 */
  697. /* Say there is no current file. */
  698. SetTitle (vszUntitled);
  699. /* Delete old change file (if there is one), and create the new
  700. one. If a Save has just been done, the change file has
  701. already been recreated, but I guess this is OK. We must
  702. recreate the change file now because we don't know whether
  703. the the user said to ignore old edits (in which case the
  704. Save didn't get done and the old change file is still around).
  705. This CreateChangeFile is also needed here for the call from
  706. CalInit, in which case it creates the change file for the
  707. first time.
  708. */
  709. CreateChangeFile ();
  710. /* Go into day mode for today. Set the selected month to an
  711. impossible one - this will cause SwitchToDate (called by
  712. DayMode) to call SetUpMonth. Note that the SwitchToDate call
  713. cannot fail since it will not attempt to read from a file.
  714. */
  715. if (fShowDate)
  716. {
  717. vd3Sel.wMonth = MONTHDEC + 1;
  718. DayMode (&vd3Cur);
  719. }
  720. /* The waiting is over. */
  721. HourGlassOff ();
  722. }
  723. #if 0
  724. /* old version commented out - L.Raman */
  725. /**** OpenCal - open a calendar file. ****/
  726. VOID APIENTRY OpenCal ()
  727. {
  728. CHAR szNewFileSpec [CCHFILESPECMAX];
  729. INT wResult;
  730. CHAR szExtension[8];
  731. lstrcpy((LPSTR)szExtension, (LPSTR)"\\*");
  732. lstrcat((LPSTR)szExtension, (LPSTR)vrgsz[IDS_FILEEXTENSION]);
  733. wResult = cDlgOpen (vhInstance, vhwnd0, IDD_OPEN, IDCN_EDIT,
  734. IDCN_LISTBOX, IDCN_PATH, szExtension, CCHFILESPECMAX,
  735. szNewFileSpec, (OFSTRUCT *)&OFStruct[IDFILEORIGINAL], &hFile[IDFILEORIGINAL]);
  736. switch (wResult)
  737. {
  738. case 1: /* hit ok, legal name */
  739. LoadCal ();
  740. break;
  741. case 2: /*hit ok, illegal name */
  742. AlertBox (vszBadFileName, szNewFileSpec, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  743. break;
  744. case -1: /*out of memory */
  745. AlertBox (vszOutOfMemory, (CHAR *) NULL, MB_SYSTEMMODAL | MB_OK | MB_ICONHAND);
  746. break;
  747. }
  748. }
  749. #endif
  750. VOID APIENTRY OpenCal ()
  751. {
  752. CHAR szNewFileSpec [CCHFILESPECMAX] = "";
  753. extern CHAR szLastDir[];
  754. /* set up the variable fields of the OPENFILENAME struct. (the constant
  755. * fields have been sel in CalInit()
  756. */
  757. vOFN.lpstrFile = szNewFileSpec;
  758. vOFN.lpstrInitialDir = szLastDir;
  759. vOFN.lpstrTitle = vszOpenCaption;
  760. vOFN.Flags = vfOpenFileReadOnly ? OFN_READONLY : 0L;
  761. /* All long pointers should be defined immediately before the call.
  762. * L.Raman - 2/12/91
  763. */
  764. vOFN.lpstrFilter = (LPSTR)vszFilterSpec;
  765. vOFN.lpstrCustomFilter = (LPSTR)vszCustFilterSpec;
  766. vOFN.lpstrDefExt = vszFileExtension + 1; /* point to "CAL" */
  767. if ( GetOpenFileName ((LPOPENFILENAME)&vOFN))
  768. {
  769. /* set the read-only flag depending on the state of Flags field */
  770. vfOpenFileReadOnly = (vOFN.Flags & OFN_READONLY ? TRUE : FALSE);
  771. hFile [IDFILEORIGINAL] = MOpenFile ( vOFN.lpstrFile,
  772. (LPOFSTRUCT)&OFStruct [IDFILEORIGINAL],
  773. OF_PROMPT+OF_CANCEL);
  774. LoadCal ();
  775. }
  776. /* GetOpenFilename doesn't return if the supplied filename is bad */
  777. }
  778. /**** LoadCal ****/
  779. VOID APIENTRY LoadCal ()
  780. {
  781. INT i;
  782. INT FileHandle;
  783. BYTE bkBuf [CBBK];
  784. BOOL fOk;
  785. UINT cb;
  786. INT cdd;
  787. DD * pdd;
  788. D3 d3First;
  789. BOOL fLoadToday = TRUE; /* Intilaise it; Fix for a Bug by SANKAR */
  790. CHAR szNewFileSpec [CCHFILESPECMAX];
  791. //- Save Header: Need temporary 16 bit buffer.
  792. WORD wTemp;
  793. /* Show the hour glass cursor. */
  794. HourGlassOn ();
  795. /* Start with a clean slate. Note that if the load fails, we will
  796. end up in untitled mode, and this is OK since even if there is
  797. another file open, it has either been saved or the user said not
  798. to save the changes. In other words, the user has said it is
  799. Ok to switch to another file, so there is no reason to be concerned
  800. about what happens to the current file if the new file can't be
  801. loaded.
  802. */
  803. CleanSlate(FALSE);
  804. if ((FileHandle = hFile[IDFILEORIGINAL]) == -1)
  805. goto loadfin;
  806. /* try opening file in READWRITE mode. If it fails, try opening it
  807. in READ mode. If it works the file must have been marked read only
  808. Set vfOpenFileReadOnly so that Calendar knows about this and does
  809. not attempt to save in the end */
  810. /* the file must be closed before a BLOCKWRITE call can successfully
  811. be executed. The call to FCloseFile() was moved from inside the
  812. following if conditional to in front of it. 1 Sept 1989 clarkc */
  813. FCloseFile (IDFILEORIGINAL);
  814. lstrcpy((LPSTR)szNewFileSpec, (LPSTR)OFStruct[IDFILEORIGINAL].szPathName);
  815. // on win32, don't do the OemToAnsi -- ianja
  816. // OemToAnsi((LPSTR)OFStruct[IDFILEORIGINAL].szPathName, (LPSTR)szNewFileSpec);
  817. if((FileHandle = MOpenFile (szNewFileSpec,
  818. (OFSTRUCT FAR *)&OFStruct[IDFILEORIGINAL],
  819. OF_READWRITE|BLOCKWRITE))== -1)
  820. {
  821. FileHandle = MOpenFile (szNewFileSpec,
  822. (OFSTRUCT FAR *)&OFStruct[IDFILEORIGINAL],
  823. OF_READ);
  824. vfOpenFileReadOnly = TRUE;
  825. }
  826. /*
  827. * The opened file's handle must be stored sothat the file will get
  828. * closed when FCloseFile() is called latter in this function;
  829. * Otherwise the file handle in hFile[] will be -1 and the file will
  830. * remain open cause "sharing violations" when run under SHARE.EXE.
  831. * Fix for Bugs #5135, #5305 and #1848 --SANKAR-- 10-13-89
  832. */
  833. hFile[IDFILEORIGINAL] = FileHandle;
  834. if (!(fOk = M_llseek (FileHandle, (LONG)0, 0) != -1
  835. && _lread (FileHandle, bkBuf, CBBK) == CBBK))
  836. goto error1;
  837. /* Check the magic number. */
  838. for (i = 6; i < CBMAGIC; i++)
  839. {
  840. if (bkBuf [i] != vrgbMagic [i])
  841. {
  842. /* File is open so this must be system modal. */
  843. AlertBox (vszNotCalFile, (CHAR *)NULL,
  844. MB_SYSTEMMODAL | MB_OK | MB_ICONEXCLAMATION);
  845. goto error0;
  846. }
  847. }
  848. /* Try to make the tdd large enough to hold the DDs from the file.
  849. Note that if successful, FGrowTdd will set vcddUsed to cdd since
  850. CleanSlate had set it to 0 by calling InitTdd.
  851. */
  852. BltByte ((BYTE *)(bkBuf + OBCDD), (BYTE *)&wTemp, CBCDD);
  853. cdd = (INT)wTemp;
  854. if (!FGrowTdd (0, cdd))
  855. {
  856. /* Couldn't grow the tdd. The error message has already been
  857. displayed by FGrowTdd.
  858. */
  859. goto error0;
  860. }
  861. /* Set up the rest of the items from the header. */
  862. //- Get Header: Store the values in temporary 16-bit buffer.
  863. BltByte ((BYTE *)(bkBuf + OBMINEARLYRING),(BYTE *)&wTemp, CBMINEARLYRING);
  864. vcMinEarlyRing = (INT)wTemp;
  865. BltByte ((BYTE *)(bkBuf + OBFSOUND), (BYTE *)&wTemp, CBFSOUND);
  866. vfSound = (INT)wTemp;
  867. BltByte ((BYTE *)(bkBuf + OBMDINTERVAL), (BYTE *)&wTemp, CBMDINTERVAL);
  868. vmdInterval = (INT)wTemp;
  869. BltByte ((BYTE *)(bkBuf + OBMININTERVAL), (BYTE *)&wTemp, CBMININTERVAL);
  870. vcMinInterval = (INT)wTemp;
  871. BltByte ((BYTE *)(bkBuf + OBFHOUR24), (BYTE *)&wTemp, CBFHOUR24);
  872. vfHour24 = (INT)wTemp;
  873. BltByte ((BYTE *)(bkBuf + OBTMSTART), (BYTE *)&vtmStart, CBTMSTART);
  874. /* Set format of time display according to vfHour24 */
  875. InitTimeDate(vhInstance, vfHour24 ? GTS_24HOUR : GTS_12HOUR);
  876. /* Read in the tdd, and close the file. Ignore errors on close
  877. (very unlikely) because if the data has been successfully read
  878. there is no reason to give up now. If there's a real problem
  879. we should find out about it when we try to read dates from the
  880. file later on.
  881. */
  882. cb = cdd * sizeof (DD);
  883. fOk = _lread (FileHandle, TddLock (), cb) == cb;
  884. TddUnlock ();
  885. FCloseFile (IDFILEORIGINAL);
  886. if (!fOk)
  887. goto error2;
  888. /* if can't load today, goto first record in file. 27-Oct-1987. davidhab */
  889. fLoadToday = TRUE;
  890. if (!FGetDateDr (vftCur.dt))
  891. {
  892. fLoadToday = FALSE;
  893. pdd = TddLock();
  894. if (!FGetDateDr(pdd->dt))
  895. {
  896. TddUnlock();
  897. goto error2;
  898. }
  899. GetD3FromDt(pdd->dt, &d3First);
  900. TddUnlock();
  901. }
  902. /* Arm the first alarm >= the current time.
  903. Also see if the first alarm must go off immediately.
  904. */
  905. GetNextAlarm (&vftCur, &vftCur, TRUE, (HWND)NULL);
  906. AlarmCheck ();
  907. /* The load went Ok - make this the open calendar file. */
  908. SetTitle ((CHAR *)OFStruct [IDFILEORIGINAL].szPathName);
  909. loadfin:
  910. /* Go into day mode for today. Set the selected month to an
  911. impossible one - this will cause SwitchToDate (called by
  912. DayMode) to call SetUpMonth. Note that the SwitchToDate call
  913. cannot fail since we have already read in the data for today
  914. if there is any.
  915. */
  916. vd3Sel.wMonth = MONTHDEC + 1;
  917. if (fLoadToday)
  918. DayMode (&vd3Cur);
  919. else
  920. DayMode (&d3First);
  921. /* The waiting is over. */
  922. HourGlassOff ();
  923. return;
  924. error2: /* Error occurred while attempting to read the tdd or the data
  925. for today.
  926. */
  927. /* Get rid of the partially loaded garbage. */
  928. CleanSlate (FALSE);
  929. error1: /* Error occurred attempting to read the header - nothing
  930. has been changed, so no need to call CleanSlate again.
  931. */
  932. /* Error attempting to read file. File is still open, so this
  933. alert must be system modal.
  934. */
  935. MessageBeep(0);
  936. AlertBox (vszCannotReadFile, (CHAR *)NULL,
  937. MB_SYSTEMMODAL | MB_OK | MB_ICONEXCLAMATION);
  938. error0: /* Not a calendar file, or couldn't make tdd big enough. The
  939. error message has already been displayed so the only thing
  940. we must do is close the file. It's OK for other cases
  941. (where the file has already been closed) to come through
  942. here since FCloseFile properly handles an already closed file.
  943. */
  944. /* Make sure the file is closed. */
  945. FCloseFile (IDFILEORIGINAL);
  946. goto loadfin;
  947. }
  948. /* ** Given filename which may or maynot include path, return pointer to
  949. filename (not including path part.) */
  950. CHAR * APIENTRY PFileInPath(register CHAR *sz)
  951. {
  952. register CHAR *pch;
  953. /* Strip path/drive specification from name if there is one */
  954. pch = (CHAR *)AnsiPrev((LPSTR)sz, (LPSTR)(sz + lstrlen((LPSTR)sz)));
  955. while (pch > sz)
  956. {
  957. pch = (CHAR *)AnsiPrev((LPSTR)sz, (LPSTR)pch);
  958. if (*pch == '\\' || *pch == ':')
  959. {
  960. pch = (CHAR *)AnsiNext((LPSTR)pch);
  961. break;
  962. }
  963. }
  964. return(pch);
  965. }