home *** CD-ROM | disk | FTP | other *** search
/ PDA Software Library / pdasoftwarelib.iso / PSION / 1997 / 969.ZIP / HOL.OPL < prev    next >
Encoding:
Text File  |  1997-01-19  |  93.7 KB  |  3,738 lines

  1. REM  HOL - calculate and write to agenda file
  2. REM  Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994 Free Software
  3. REM  Foundation, Inc.
  4. REM  Copyright (C) 1996 Odd Gripenstam
  5. REM
  6. REM  This program is free software; you can redistribute it and/or modify
  7. REM  it under the terms of the GNU General Public License as published by
  8. REM  the Free Software Foundation; either version 2 of the License, or
  9. REM  (at your option) any later version.
  10. REM
  11. REM  This program is distributed in the hope that it will be useful,
  12. REM  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. REM  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. REM  GNU General Public License for more details.
  15. REM
  16. REM  You should have received a copy of the GNU General Public License
  17. REM  along with this program; if not, write to the Free Software
  18. REM  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  
  20.  
  21. REM  Magic numbers:
  22. REM    101   size of vectors for loaded holidays
  23. REM    33       characters in alias
  24. REM   -36       end-of-file error
  25. REM     2       size of vectors for simultaneously calculated holidays
  26.  
  27.  
  28. APP Hol
  29.     TYPE $9003
  30.     EXT  "HOL"
  31.     ICON "holapp.pic"
  32.     PATH "\HOL\"
  33. ENDA
  34.     
  35. PROC holmain%:
  36.  
  37.     REM  Global variables for getting tokens from the rulefile
  38.     GLOBAL  Grow$(255)              REM the row from the rulefile we are processing
  39.     GLOBAL  Gtoken$(255)    REM string value of the last read token
  40.     GLOBAl  Gtoken&                 REM integer value (if possible) of the token
  41.     GLOBAL  Gtoktyp$(10)    REM type of the last token
  42.  
  43.     REM  Global variables for calculated dates
  44.     GLOBAL  Gstyle%                 REM style-bits for current date
  45.     GLOBAL  Gyrsymb%                REM year view symbol
  46.     GLOBAL  Gdaytxt$(2,254) REM the text for the day entry
  47.     GLOBAL  Gdaynum&(2)             REM days since 1-JAN-1970 for current date (this is really a UWORD)
  48.     GLOBAL  Gabsdat&(2)             REM the calculated date as days since 31/12/-1 (?)
  49.     GLOBAL  Gdattot%                REM number of used elements in arrays
  50.     GLOBAL  Galias$(32)             REM name of alias for current date
  51.     GLOBAL  Gbool                   REM the current value of a boolean value
  52.     GLOBAL  Grepmon%                REM month date repeats on
  53.     GLOBAL  Grepday%                REM day in month / weekday date repeats on
  54.     GLOBAL  Grepnbr%                REM nth weekday date repeats on
  55.     GLOBAL  Greptyp%                REM type of repeat; 1 = can't repeat; 2 = FIX; 3 = FLOAT
  56.                             REM i.e. FLOAT(Grepmon%, Grepday%, Grepnbr%)
  57.                             REM      FIX(Grepmon%, Grepday%)
  58.     GLOBAL    Genttyp%                REM entry type: 0 = Don't write; 2 = untimed day entry; 3 = anniversary
  59.     GLOBAL    Gshoign%                REM if true show ignored entries
  60.  
  61.     REM  Global variables for loaded holidays
  62.     GLOBAL  Gyrsyma%(101)    REM array of Gyrsymb%
  63.     GLOBAL    Gdaytxa%(101)    REM array of pointers to Gdaytxt$
  64.     GLOBAL  Gstylea%(101)    REM array of Gstyle%
  65.     GLOBAL  Gselcta(101)             REM True if holiday shall be used
  66.     GLOBAL  Gflags%(101)             REM Bit 0 = repeatable
  67.                             REM Bit 1 = can't be skipped (sets alias)
  68.     GLOBAL  Gholtot%                REM Number of used elements in arrays
  69.     GLOBAL  Gholidx%                REM Current element in arrays
  70.  
  71.     REM  Global variables for default values from rule file 
  72.     GLOBAL  Gdstyle%                REM default style
  73.     GLOBAL  Gdyrsym%                REM default year view symbol
  74.     GLOBAL    Gdenttp%                REM default entry type
  75.     
  76.     REM  Global variables for aliases
  77.     GLOBAL  Galias$(10,33)  REM symbolic names
  78.     GLOBAL  Galias&(10)             REM absolute date
  79.     GLOBAL  Galinxt%                REM index into alias arrays to next free
  80.  
  81.     REM  Global variables for reading rule file
  82.     GLOBAL  Grulhnd%                REM handle to open file
  83.     GLOBAL  Grulfil$(128)   REM Name of rule file
  84.     GLOBAL  Growno%                 REM row number
  85.     GLOBAL  Gyear%                  REM year to create holidays for
  86.     GLOBAL  Gyear1%                 REM first year to create holidays for
  87.     GLOBAL  Gyear2%                 REM last year to create holidays for
  88.  
  89.     GLOBAL  Gagnfil$(128)   REM Name of agenda file
  90.     GLOBAL  Grepmod%                REM 1 = Don't write repeating entries
  91.                             REM 2 = Write repeating entries if possible
  92.     GLOBAL  Grepsho%                REM 1 = Show all occurrences
  93.                             REM 2 = Next only
  94.     GLOBAL    Grepend%                REM 1 = Repeat forever; 2 = Repeat until end year
  95.     
  96.     REM  Global variables for fonts and screens
  97.     GLOBAL  Gcurfnt%                REM gFONT number of the current font
  98.     GLOBAL  Gscrows%                REM number of text rows on screen with current font
  99.     GLOBAL  Gscrowh%                REM height of one text row
  100.     GLOBAL  Gscrowd%                REM descent of characters in font
  101.     GLOBAL  Gscchrw%                REM max character width
  102.     GLOBAL  Gstatwn%                REM current type of status window
  103.     GLOBAL  Gscheit%                REM screen height
  104.     GLOBAL  Gscwidt%                REM screen width
  105.     GLOBAL  Gscreen%                REM screen type: 1 = 3a, 2=Siena
  106.  
  107.     REM  Global variables for scrolling
  108.     GLOBAL  Gcurrow%                REM current screen row for cursor
  109.  
  110.     REM  Global variables for event handling
  111.     GLOBAL  Gevent%(6)              REM events from GETEVENT
  112.     GLOBAL  Gevent                  REM true if unhandled events in Gevent
  113.  
  114.     GLOBAL  Gvers$(8)               REM version number string
  115.     GLOBAL  Gabshow
  116.     GLOBAL  Gcalc                   REM really do calculate
  117.     GLOBAL  Gusrmod$(50)    REM current loaded user module
  118.     GLOBAL    Gdtfmt%                    REM date format: 0 = MDY, 1 = DMY, 2 = YMD
  119.     GLOBAL  Gdtsep$(1)                REM date separator character
  120.  
  121.     TRAP CACHE 2000,12000
  122.  
  123.     init:    
  124.     mainloop:
  125.     
  126.     IF Grulhnd% <> 0
  127.         IOCLOSE(Grulhnd%)
  128.     ENDIF
  129.  
  130. ENDP
  131.  
  132. REM =======================================================================================
  133. REM  UI routines
  134. REM =======================================================================================
  135.  
  136. PROC init:
  137.     LOCAL name$(9)
  138.     LOCAL disk$(2)
  139.  
  140.     REM Rename process
  141.     REM $88 $00 ProcId
  142.     REM $88 $0C PrcRename 
  143.     name$ = "HOL"+chr$(0)
  144.     CALL($0C88, CALL($88), 0, 0, 0, UADD(ADDR(name$),1))
  145.  
  146.     Gvers$   = "V2.1b1"
  147.     Grulhnd% = 0                    REM no file open
  148.     Gholidx% = 0                    REM nothing to show on screen
  149.     Gscheit% = gHEIGHT
  150.     Gscwidt% = gWIDTH
  151.     IF gWIDTH > 240
  152.         Gscreen% = 1
  153.     ELSE
  154.         Gscreen% = 2
  155.     ENDIF
  156.     Gyear1%  = YEAR
  157.     Gyear2%  = YEAR
  158.     Grepmod% = 2
  159.     Grepsho% = 1                    REM Show all
  160.     Grepend% = 2                    REM Repeat until end year
  161.     Gabshow  = -1                   REM show the about window once
  162.     Gcalc    = -1
  163.     Gusrmod$ = ""
  164.     Gshoign% = 0
  165.  
  166.     REM Assume the only "HOL."-file is a directory.
  167.     REM If there is no HOL directory the program can't be started,
  168.     REM so we don't need to check that.
  169.     disk$ = findfil$:("MAB", "\HOL.")
  170.     SETPATH disk$+"\HOL\"
  171.     getdtfmt:
  172.     setfont%:(10)
  173.     REM Start with no status window on small screens
  174.     IF Gscreen% = 2
  175.         setstat:(0)
  176.     ELSE
  177.         setstat:(2)
  178.     ENDIF
  179.  
  180.     REM  Get initial file to open if we seem to be an APP
  181.     IF CMD$(3) = "O" AND CMD$(1) <> CMD$(2)
  182.         rulload%:(CMD$(2))
  183.     ELSE
  184.         REM Show open dialog
  185.         xHUo:
  186.     ENDIF
  187.  
  188. ENDP
  189.  
  190. PROC mainloop:
  191.     REM                            Help  Menu C-Menu Down  Up    PgDn  PgUp  Home  End   Space
  192.     evloop$:("acmnoptuwxz","IOZ","|xU291|xU290|xC290|xU257|xU256|xP261|xP260|xP262|xP263|xU032|", -1)
  193. ENDP
  194.  
  195. REM  Get and process events.
  196. REM  For each event create a cmdcode.
  197. REM
  198. REM  If the event is a hotkey (PSION-<menuchar>) and the <menuchar>
  199. REM  is found in HOTKEYU$ (unshifted keys) or HOTKEYS$ (shifted),
  200. REM  call the cmdcode as a function.
  201. REM
  202. REM  If the event is a system command, call the cmdcode as
  203. REM  a function with the end of the system command as a parameter.
  204. REM
  205. REM  If the cmdcode is found in CMDS$, call the cmdcode as a function.
  206. REM  If the MENU-key was pressed, call the cmdcode (e.g. xU290) with
  207. REM  HOTKEYU$ and HOTKEYS$ as parameters.
  208. REM
  209. REM  If an unknown event is read and IGNORE is true, just ignore the
  210. REM  event, otherwise return the cmdcode.
  211. REM
  212. REM  Continue to read events until a cmdcode-function returns <>0 and
  213. REM  then return "".
  214. PROC evloop$:(hotkeyu$, hotkeys$, cmds$, ignore%)
  215.  
  216.     LOCAL   done
  217.     LOCAL   k%                              REM code for key pressed
  218.     LOCAL   m%                              REM code for modifier pressed
  219.     LOCAL   cmdcode$(7)             REM code for command to perform:
  220.                             REM x<H,C,S,P,A>...<char,code>
  221.                             REM x    prefix
  222.                             REM H    hotkey (menu-char or equiv. psion-<char>)
  223.                             REM C    control pressed
  224.                             REM S    shift pressed
  225.                             REM P    psion pressed
  226.                             REM A    system command ("argument")
  227.                             REM char printable representation of
  228.                             REM      key pressed/system command
  229.                             REM code decimal key code for nonprintable char
  230.     LOCAL   cmdret                  REM return value from cmdcode proc
  231.     LOCAL   c$(127)                 REM system command
  232.     LOCAL   k$(3)
  233.     LOCAL   m$(5)
  234.     LOCAL   docall
  235.  
  236.     Gevent = 0
  237.     done = 0
  238.  
  239.     WHILE NOT done
  240.  
  241.         IF NOT Gevent
  242.             GETEVENT Gevent%()
  243.         ENDIF
  244.         
  245.         REM Assume someone will deal with this event
  246.         Gevent = 0
  247.         docall = 0
  248.  
  249.         IF Gevent%(1) = $404            REM system command
  250.             c$ = GETCMD$
  251.             cmdcode$ = "xA" + MID$(c$, 1, 1)
  252.             docall = -1
  253.             cmdret = @(cmdcode$):(RIGHT$(c$, LEN(c$)-1))
  254.         ELSEIF (Gevent%(1) AND $400) = 0        REM keypress
  255.             k% = Gevent%(1)
  256.             m% = Gevent%(2) AND $00FF
  257.             
  258.             m$ = ""
  259.             IF m% AND 2                             REM shift pressed
  260.                 m$ = "S"
  261.             ENDIF
  262.             IF m% AND 4                             REM control pressed
  263.                 m$ = m$ + "C"
  264.             ENDIF
  265.             IF m% AND 8                             REM psion pressed
  266.                 m$ = m$ + "P"
  267.             ENDIF
  268.             IF m$ = ""                              REM unshifted
  269.                 m$ = "U"
  270.             ENDIF
  271.  
  272.             IF k% AND $200                  REM Psion hotkey character
  273.                 REM ignore other modifiers
  274.                 m$ = "H"
  275.                 k% = k% - $200
  276.                 IF m% AND 2                     REM Psion-SHIFT-
  277.                     IF LOC(hotkeys$,CHR$(k%))
  278.                         docall = -1
  279.                         m$ = m$ + "S"
  280.                     ENDIF
  281.                 ELSE
  282.                     IF LOC(hotkeyu$,CHR$(k%))
  283.                         docall = -1
  284.                         m$ = m$ + "U"
  285.                     ENDIF
  286.                 ENDIF
  287.             ENDIF
  288.             
  289.             REM is char OK in procedure name?
  290.             IF ((%0 <= k%) AND (k%<=%9)) OR ((%a<=k%) AND (k%<=%z)) OR ((%A<=k%) AND (k%<=%Z))
  291.                 k$ = CHR$(k%)
  292.             ELSE
  293.                 k$ = RIGHT$("000"+NUM$(k%, 3), 3)
  294.             ENDIF
  295.             cmdcode$ = "x"+m$+k$
  296.  
  297.             IF LOC(cmds$, "|"+cmdcode$+"|")
  298.                 docall = -1
  299.             ENDIF
  300.             
  301.             IF docall
  302.                 IF k% = 290                     REM special treatment for Menu key
  303.                     cmdret = @(cmdcode$):(hotkeyu$, hotkeys$)
  304.                 ELSE
  305.                     cmdret = @(cmdcode$):
  306.                 ENDIF
  307.             ENDIF
  308.         ENDIF
  309.  
  310.         IF (NOT docall) AND (NOT ignore%)
  311.             RETURN cmdcode$
  312.         ENDIF
  313.         IF docall AND (cmdret <> 0)
  314.             RETURN " "
  315.         ENDIF
  316.     ENDWH
  317. ENDP
  318.  
  319.  
  320. REM  Act upon hotkey
  321. PROC evmenu:(hotkeyu$, hotkeys$, k%)
  322.  
  323.     LOCAL  docall
  324.     LOCAL  m$(4)
  325.     LOCAL  k$(3)
  326.     LOCAL  cmdcode$(10)
  327.  
  328.     m$ = "H"
  329.     IF k% <= %Z                     REM Psion-SHIFT-
  330.         IF LOC(hotkeys$,CHR$(k%))
  331.             docall = -1
  332.             m$ = m$ + "S"
  333.         ENDIF
  334.     ELSE
  335.         IF LOC(hotkeyu$,CHR$(k%))
  336.             docall = -1
  337.             m$ = m$ + "U"
  338.         ENDIF
  339.     ENDIF
  340.  
  341.     REM is char OK in procedure name?
  342.     IF ((%0 <= k%) AND (k%<=%9)) OR ((%a<=k%) AND (k%<=%z)) OR ((%A<=k%) AND (k%<=%Z))
  343.         k$ = CHR$(k%)
  344.     ELSE
  345.         k$ = RIGHT$("000"+NUM$(k%, 3), 3)
  346.     ENDIF
  347.     cmdcode$ = "x"+m$+k$
  348.  
  349.     IF docall
  350.         return @(cmdcode$):
  351.     ELSE
  352.         return 0
  353.     ENDIF
  354. ENDP
  355.  
  356. REM system command exit
  357. PROC xAX:(file$)
  358.     RETURN xHUx:
  359. ENDP
  360.  
  361. REM system command open
  362. PROC xAO:(file$)
  363.     rulload%:(file$)
  364. ENDP
  365.  
  366. REM Space - toggle mark
  367. PROC xU032:
  368.     IF Gholidx%<>0
  369.         Gselcta(Gholidx%) = NOT Gselcta(Gholidx%)
  370.         scupdate:(Gcurrow%, Gholidx%, 1, "gshowrow")
  371.     ELSE
  372.         gIPRINT "No holiday file open"
  373.     ENDIF
  374.     RETURN 0
  375. ENDP
  376.  
  377. REM  Down 
  378. PROC xU257:
  379.     sccurmov:(Gholidx%+1)
  380.     RETURN 0
  381. ENDP
  382.  
  383. REM  Up
  384. PROC xU256:
  385.     sccurmov:(Gholidx%-1)
  386.     RETURN 0
  387. ENDP
  388.  
  389. REM  PgDown 
  390. PROC xP261:
  391.     sccurmov:(Gholidx%+Gscrows%-1)
  392.     RETURN 0
  393. ENDP
  394.  
  395. REM  PgUp
  396. PROC xP260:
  397.     sccurmov:(Gholidx%-Gscrows%+1)
  398.     RETURN 0
  399. ENDP
  400.  
  401. REM  Home 
  402. PROC xP262:
  403.     sccurmov:(1)
  404.     RETURN 0
  405. ENDP
  406.  
  407. REM  End
  408. PROC xP263:
  409.     sccurmov:(Gholtot%)
  410.     RETURN 0
  411. ENDP
  412.  
  413. REM  Menu key pressed
  414. PROC xU290:(hotkeyu$, hotkeys$)
  415.  
  416.     LOCAL  k%
  417.  
  418.     LOCK ON
  419.     mINIT
  420.     mCARD "File","Open file",%o,"Close file",%c
  421.     IF Gholidx%<>0
  422.         mCARD "Holidays","Preview",%p,"Write to agenda",-%w,"Toggle mark",%t,"Mark all",%m,"Unmark all",%u,"Mark not repeatable",%n
  423.     ENDIF
  424.     mCARD "Special","Zoom in",%z,"Zoom out",-%Z,"About Hol",%a,"Exit",%x
  425.     k% = MENU
  426.     LOCK OFF
  427.  
  428.     RETURN evmenu:(hotkeyu$, hotkeys$, k%)
  429. ENDP
  430.  
  431. PROC xC290:(hotkeyu$, hotkeys$)
  432.     IF Gstatwn% = 1
  433.         setstat:(0)
  434.     ELSEIF Gstatwn% = 2
  435.         setstat:(1)
  436.     ELSE
  437.         REM Small screens don't have large status window
  438.         IF Gscreen% = 2
  439.             setstat:(1)
  440.         ELSE
  441.             setstat:(2)
  442.         ENDIF
  443.     ENDIF
  444.     RETURN 0
  445. ENDP
  446.  
  447. PROC xU291:
  448.     LOCAL helpfil$(12), file$(14), r%, disk$(2)
  449.     helpfil$ = "\HOL\HOL.RSC"
  450.  
  451.     disk$ = findfil$:("MAB", helpfil$)
  452.     IF disk$ = ""
  453.         r% = ALERT("Sorry, no help available", "Could not find file "+helpfil$)
  454.         RETURN 0
  455.     ENDIF
  456.  
  457.     showhelp:(disk$+helpfil$)
  458.     RETURN 0
  459. ENDP
  460.  
  461. PROC xHUa:
  462.     LOCAL   winid%
  463.  
  464.     winid% = abcre%:
  465.     evloop$:("","","",0)
  466.  
  467.     IF Gevent%(1) = $404            REM system command
  468.         REM let the main event loop handle this
  469.         Gevent = -1
  470.     ENDIF
  471.     gCLOSE winid%
  472. ENDP
  473.  
  474. REM  Close current rule file
  475. PROC xHUc:
  476.     rulclos%:
  477.     nofile:
  478. ENDP
  479.  
  480. REM  Toggle show ignore
  481. PROC xHSI:
  482.     Gshoign% = NOT Gshoign%
  483. ENDP
  484.  
  485. REM Select all holidays
  486. PROC xHUm:
  487.     LOCAL   i%
  488.     i% = 1
  489.     IF Gholidx%<>0
  490.         WHILE i%<=Gholtot%
  491.             Gselcta(i%) = -1
  492.             i% = i% + 1
  493.         ENDWH
  494.         scredraw:(1)
  495.     ELSE
  496.         gIPRINT "No holiday file open"
  497.     ENDIF
  498. ENDP
  499.  
  500. REM Select holidays that can't be written as repeating entries
  501. PROC xHUn:
  502.     LOCAL   i%
  503.     i% = 1
  504.     IF Gholidx%<>0
  505.         WHILE i%<=Gholtot%
  506.             REM bit 0 not set
  507.             IF (Gflags%(i%) AND $1) = 0
  508.                 Gselcta(i%) = -1
  509.             ELSE
  510.                 Gselcta(i%) = 0
  511.             ENDIF
  512.             i% = i% + 1
  513.         ENDWH
  514.         scredraw:(1)
  515.     ELSE
  516.         gIPRINT "No holiday file open"
  517.     ENDIF
  518. ENDP
  519.  
  520. REM Open rule file
  521. REM Prompt for a rule file. Open it and read into arrays
  522. REM Save file handle in Grulhnd%
  523. PROC xHUo:
  524.  
  525.     LOCAL   d%
  526.     LOCAL   stat%                   REM return status
  527.     LOCAL   offset&
  528.     LOCAL    file$(128)
  529.  
  530.     file$ = "*.hol"
  531.  
  532.     LOCK ON
  533.     dINIT "Open holiday file"
  534.     dFILE file$, "File:", 8
  535.     d% = DIALOG
  536.     LOCK OFF
  537.  
  538.     IF d%
  539.         Grulfil$ = file$
  540.         rulload%:(Grulfil$)
  541.     ELSEIF Gholidx% = 0
  542.         nofile:
  543.     ENDIF
  544.  
  545.     RETURN 0
  546. ENDP
  547.  
  548. REM  Preview holidays
  549. PROC xHUp:
  550.     RETURN holdo:("holview",0)
  551. ENDP
  552.  
  553. REM Toggle mark. Menu option synonymous with space
  554. PROC xHUt:
  555.     RETURN xU032:
  556. ENDP
  557.  
  558. REM  write holidays
  559. PROC xHUw:
  560.     RETURN holdo:("holwrit",-1)
  561. ENDP
  562.  
  563. REM De-select all holidays
  564. PROC xHUu:
  565.     LOCAL   i%
  566.  
  567.     IF Gholidx%<>0
  568.         i% = 1
  569.         WHILE i%<=Gholtot%
  570.             Gselcta(i%) = 0
  571.             i% = i% + 1
  572.         ENDWH
  573.         scredraw:(1)
  574.     ELSE
  575.         gIPRINT "No holiday file open"
  576.     ENDIF
  577. ENDP
  578.  
  579. PROC xHUx:
  580.     REM  return <> 0 to exit event loop
  581.     RETURN 1
  582. ENDP
  583.  
  584. REM Zoom in
  585. PROC xHUz:
  586.     RETURN zoom:(1)
  587. ENDP
  588.  
  589. REM  Zoom out
  590. PROC xHSZ:
  591.     RETURN zoom:(-1)
  592. ENDP
  593.  
  594.  
  595.  
  596. REM =======================================================================================
  597. REM  Misc routines
  598. REM =======================================================================================
  599.  
  600. PROC abort%:(err%, errmsg$)
  601.     ALERT("Error when "+errmsg$, ERR$(err%), "Abort")
  602.     RETURN err%
  603. ENDP
  604.  
  605. PROC quit%:(msg1$, msg2$)
  606.     ALERT(msg1$, msg2$, "Quit")
  607.     RETURN -1
  608. ENDP
  609.  
  610. REM  Create and show the about-window.
  611. REM  Return window id.
  612. PROC abcre%:
  613.     LOCAL   winid%
  614.     LOCAL   oldfont%
  615.     LOCAL   xmargin%, ymargin%
  616.     LOCAL   xmax%, ymax%
  617.  
  618.     xmargin% = 12    REM  move out of the border
  619.     ymargin% = 9
  620.     xmax% = 0
  621.     
  622.     REM Create max sized invisible gray window. Resize and move later.
  623.     winid% = gCREATE(0, 0, Gscwidt%, Gscheit%, 0, 1)
  624.     gUSE winid%
  625.  
  626.     REM Make room for the centered title, write it later when we know the width
  627.     REM double height
  628.     gSTYLE 8
  629.     oldfont% = setfont%:(10)
  630.     gAT xmargin%,Gscrowh%+8
  631.  
  632.     gSTYLE 1
  633.     setfont%:(9)
  634.  
  635.     gAT xmargin%,gY+Gscrowh%*1.5
  636.     gPRINT "HOL version ",Gvers$
  637.     xmax% = MAX(xmax%, gX)
  638.  
  639.     gAT xmargin%,gY+Gscrowh%
  640.     gPRINT "Copyright ",CHR$(184),"1996 Odd Gripenstam"
  641.     xmax% = MAX(xmax%, gX)
  642.  
  643.     gAT xmargin%,gY+Gscrowh%
  644.     gPRINT "Copyright ",CHR$(184),"1988, 1989, 1990, 1991,"
  645.     xmax% = MAX(xmax%, gX)
  646.  
  647.     gAT xmargin%,gY+Gscrowh%
  648.     gPRINT "1992, 1993, 1994 Free Software"
  649.     xmax% = MAX(xmax%, gX)
  650.     
  651.     gAT xmargin%,gY+Gscrowh%
  652.     gPRINT "Foundation, Inc."
  653.     xmax% = MAX(xmax%, gX)
  654.  
  655.     gSTYLE 0
  656.     setfont%:(9)
  657.     gAT xmargin%,gY+Gscrowh%*1.5
  658.     gPRINT "HOL comes with ABSOLUTELY NO WARRANTY."
  659.     xmax% = MAX(xmax%, gX)
  660.  
  661.     gAT xmargin%,gY+Gscrowh%
  662.     gPRINT "This is free software, and you are welcome"
  663.     xmax% = MAX(xmax%, gX)
  664.  
  665.     gAT xmargin%,gY+Gscrowh%
  666.     gPRINT "to redistribute it under certain conditions."
  667.     xmax% = MAX(xmax%, gX)
  668.  
  669.     gAT xmargin%,gY+Gscrowh%
  670.     gPRINT "See the file COPYING.TXT for details."
  671.     xmax% = MAX(xmax%, gX)
  672.  
  673.     REM  Center the window now that we know how much we used.
  674.     xmax% = xmax% + xmargin%
  675.     ymax% = gY + ymargin%
  676.     gSETWIN MAX((Gscwidt%-xmax%)/2,0),(Gscheit%-ymax%)/2, xmax%, ymax%
  677.  
  678.     REM  Write the centered title. Let's assume it will fit in the new width.
  679.     gSTYLE 8
  680.     setfont%:(10)
  681.     gAT xmargin%,Gscrowh%+8
  682.     gPRINTB "About HOL "+Gvers$,gWIDTH,3
  683.  
  684.     REM 3a 3D-border, extra rounded
  685.     gXBORDER 1, $203
  686.  
  687.     gVISIBLE ON
  688.     
  689.     gUSE 1
  690.     gSTYLE 0
  691.     setfont%:(oldfont%)
  692.     RETURN winid%
  693. ENDP
  694.  
  695. PROC agnchek%:(agnhand%)
  696.     LOCAL   buf$(16), buf%
  697.     LOCAL   stat%
  698.  
  699.     REM Read the header and check:
  700.     stat% = IOREAD(agnhand%, UADD(ADDR(buf$),1), 16)
  701.     IF stat% < 0
  702.         RETURN abort%:(stat%, "reading ID in "+Gagnfil$)
  703.     ENDIF
  704.     POKEB ADDR(buf$), stat%
  705.  
  706.     IF buf$<>"AgendaFileType*"+CHR$(0)
  707.         IF Gscreen% = 1
  708.             RETURN quit%:("Wrong file label in "+Gagnfil$, buf$)
  709.         ELSE
  710.             RETURN quit%:("Wrong file label", buf$)
  711.         ENDIF
  712.     ENDIF
  713.     
  714.     stat% = IOREAD(agnhand%, ADDR(buf%), 2)
  715.     IF stat% < 0
  716.         RETURN abort%:(stat%, "reading version in "+Gagnfil$)
  717.     ENDIF
  718.  
  719.     IF buf%<>$100f
  720.         IF Gscreen% = 1
  721.             RETURN quit%:("Wrong file version in "+Gagnfil$, "Found "+HEX$(buf%)+", expected "+HEX$($100f))
  722.         ELSE
  723.             RETURN quit%:("Wrong file version", "Found "+HEX$(buf%)+", expected "+HEX$($100f))
  724.         ENDIF
  725.     ENDIF
  726. ENDP
  727.  
  728. PROC agnwrit%:(agnhand%, daynum&, yrsymb%, style%, txt$, reptyp%, repday%, repnbr%, repsho%, repend&, enttyp%)
  729.     LOCAL   buf%(8)
  730.     LOCAL   stat%, off&
  731.     LOCAL   daytxt$(254), day%
  732.  
  733.     IF enttyp% <> 2 AND enttyp% <> 3
  734.         RETURN 0
  735.     ENDIF
  736.     
  737.     daytxt$ = txt$
  738.  
  739.     IF enttyp% = 2
  740.         REM Untimed day entry
  741.         REM record type 2, length
  742.         buf%(1) = &2000 + 8 + LEN(daytxt$)
  743.     ELSEIF enttyp% = 3
  744.         REM Anniversary
  745.         REM record type 3, length
  746.         buf%(1) = &3000 + 11 + LEN(daytxt$)
  747.     ENDIF
  748.  
  749.     REM daynum
  750.     buf%(2) = daynum&
  751.  
  752.     REM slot
  753.     buf%(3) = $FFFF
  754.  
  755.     REM attributes (low byte), year symbol (high byte)
  756.     IF yrsymb%>32
  757.         REM with year symbol
  758.         buf%(4) = 2 + 4 + 8 + 16
  759.         POKEB UADD(ADDR(buf%()), 7), yrsymb%
  760.     ELSE
  761.         buf%(4) = 2 + 8 + 16
  762.     ENDIF
  763.     
  764.     REM no repeat, then set bit 1
  765.     IF reptyp%=1
  766.         buf%(4) = buf%(4) OR 1
  767.     ENDIF
  768.  
  769.     IF enttyp% = 2
  770.         REM title style (low byte)
  771.         buf%(5) = style%
  772.     ELSEIF enttyp% = 3
  773.         REM start year: 0 = none
  774.         buf%(5) = 0
  775.         REM show base/elapsed (low byte)
  776.         REM title style (high byte)
  777.         buf%(6) = 0
  778.         POKEB UADD(ADDR(buf%()), 11), style%
  779.     ENDIF
  780.  
  781.     REM find current offset by seeking to the current position
  782.     off& = 0
  783.     stat% = IOSEEK(agnhand%, 3, off&)
  784.     IF stat% < 0
  785.         RETURN abort%:(stat%, "getting offset")
  786.     ENDIF
  787.  
  788.     IF enttyp% = 2
  789.         stat% = IOWRITE(agnhand%, ADDR(buf%()), 9)
  790.     ELSE
  791.         stat% = IOWRITE(agnhand%, ADDR(buf%()), 12)
  792.     ENDIF
  793.     IF stat% < 0
  794.         RETURN abort%:(stat%, "writing header")
  795.     ENDIF
  796.  
  797.     REM title text
  798.     stat% = IOWRITE(agnhand%, ADDR(daytxt$), LEN(daytxt$)+1)
  799.     IF stat% < 0
  800.         RETURN abort%:(stat%, "writing text")
  801.     ENDIF
  802.  
  803.     IF reptyp%=2    REM repeat FIX date as annual repeat
  804.         REM record type 5, length
  805.         buf%(1) = &5000 + 9
  806.         IF repsho%=1
  807.             REM no interval, show all, annual repeat
  808.             buf%(2) = 4
  809.         ELSE
  810.             REM no interval, show next, annual repeat
  811.             buf%(2) = 12
  812.         ENDIF
  813.         REM ending date
  814.         buf%(3) = repend&
  815.         REM associated entry type
  816.         buf%(4) = enttyp%
  817.         REM offset of associated entry
  818.         POKEL UADD(ADDR(buf%()), 7) , off&
  819.         stat% = IOWRITE(agnhand%, ADDR(buf%()), 11)
  820.         IF stat% < 0
  821.             RETURN abort%:(stat%, "writing repeat")
  822.         ENDIF
  823.  
  824.     ELSEIF reptyp%=3 REM repeat FLOAT date as monthly by day every 12 months
  825.         buf%(4) = 0
  826.         buf%(5) = 0
  827.         buf%(6) = 0
  828.         
  829.         REM record type 5, length
  830.         buf%(1) = &5000 + 14
  831.         IF repsho% = 1
  832.             REM interval 12-1 (B), show all , monthly by days (3)
  833.             buf%(2) = $0B03
  834.         ELSE
  835.             REM interval 12-1 (B), show next (8) , monthly by days (3)
  836.             buf%(2) = $0B0B
  837.         ENDIF
  838.         REM ending date
  839.         buf%(3) = repend&
  840.         REM associated entry type
  841.         buf%(4) = enttyp%
  842.  
  843.         REM day to repeat on
  844.         IF repday% = 0  REM sunday = bit 6
  845.             day% = 2**6
  846.         ELSE
  847.             day% = 2**(repday%-1)
  848.         ENDIF
  849.  
  850.         IF repnbr%=-1                   REM last days
  851.             POKEB UADD(ADDR(buf%()),11), day%
  852.         ELSE                                    REM 1st to 4th
  853.             POKEB UADD(ADDR(buf%()),6+repnbr%), day%
  854.         ENDIF
  855.  
  856.         REM offset of associated entry
  857.         POKEL UADD(ADDR(buf%()),12), off&
  858.         stat% = IOWRITE(agnhand%, ADDR(buf%()), 16)
  859.         IF stat% < 0
  860.             RETURN abort%:(stat%, "writing repeat")
  861.         ENDIF
  862.     ENDIF
  863.     
  864.     RETURN stat%
  865. ENDP
  866.  
  867. REM  Add an alias to the list.
  868. REM  We have a hard-coded limit of 10 aliases
  869. PROC aliadd%:(name$, value&)
  870.     IF Galinxt%>10
  871.         RETURN quit%:("Too many alias defined", "")
  872.     ENDIF
  873.     Galias$(Galinxt%) = name$
  874.     Galias&(Galinxt%) = value&
  875.     
  876.     Galinxt% = Galinxt%+1
  877.     RETURN 0
  878. ENDP
  879.  
  880. REM  Return the index into the alias arrays for
  881. REM  the alias name$ or 0 if not found
  882. PROC aliget%:(name$)
  883.     LOCAL   i%
  884.     i% = 1
  885.     WHILE i%<Galinxt%
  886.         IF Galias$(i%)=name$
  887.             RETURN i%
  888.         ENDIF
  889.         i%=i%+1
  890.     ENDWH
  891.     RETURN 0
  892. ENDP
  893.  
  894. REM Ask if the file should be backed up and do the backup.
  895. REM Return %y unless the user cancelled the dialogs
  896. PROC backup%:(file$)
  897.     LOCAL orig$(128), bck$(128), d%, off%(6)
  898.  
  899.     orig$ = file$
  900.     PARSE$(orig$, "", off%())
  901.     LOCK ON
  902.     dINIT "Make a backup copy of """+MID$(orig$, off%(4), off%(5)-off%(4))+"""?"
  903.     dBUTTONS "No",%N,"Yes",%Y
  904.     d% = DIALOG
  905.     LOCK OFF
  906.     
  907.     IF d% = %y
  908.         bck$ = "M:\AGN\BACKUP.AGN"
  909.         LOCK ON
  910.         dINIT "Backup file"
  911.         dTEXT "From file:", MID$(orig$, off%(4), LEN(orig$))
  912.         dFILE bck$, "To file:", 1+2+8
  913.         d% = DIALOG
  914.         IF d% > 0
  915.             d% = %y
  916.             COPY orig$,bck$
  917.         ENDIF
  918.     ELSEIF d% = %n
  919.         REM Only skip the backup
  920.         d% = %y
  921.     ENDIF
  922.     RETURN d%
  923. ENDP
  924.  
  925. REM  Calculate a date. Return it in Gdaynum& as days since 1970 and 
  926. REM  in Gabsdat& as days since 31/12/-1.
  927. REM  Expect '=' { EASTER | FIX(m,d) | HFIX(m,d) | IFIX(m,d) | FLOAT(???) |
  928. REM               DONEIF (...) | IF | ELSEIF | ELSE | '+' <value> |
  929. REM               '-' <value>}... ';'
  930. PROC calcdat%:(rulhand%)
  931.     LOCAL   stat%
  932.     LOCAL   i%
  933.  
  934.     REM skip over the '='
  935.     stat% = expect%:(rulhand%, "CALCDAT", "char", "=")
  936.     IF stat% < 0
  937.         RETURN stat%
  938.     ENDIF
  939.  
  940.     Gdaynum&(1) = 0
  941.     Gabsdat&(1) = 0
  942.     Gdattot% = 1
  943.     Greptyp% = 0
  944.  
  945.     REM  Calculate date as absolute date
  946.     DO
  947.         stat% = gettok%:(rulhand%)
  948.         IF stat% >= 0
  949.             IF Gtoken$ = "EASTER"
  950.                 stat% = opeastr%:(rulhand%)
  951.             ELSEIF Gtoken$ = "FIX"
  952.                 stat% = opfix%:(rulhand%)
  953.             ELSEIF Gtoken$ = "HFIX"
  954.                 stat% = ophfix%:(rulhand%)
  955.             ELSEIF Gtoken$ = "IFIX"
  956.                 stat% = opifix%:(rulhand%)
  957.             ELSEIF Gtoken$ = "FLOAT"
  958.                 stat% = opfloat%:(rulhand%)
  959.             ELSEIF Gtoken$ = "LAST"
  960.                 stat% = oplast%:(rulhand%)
  961.             ELSEIF Gtoken$ = "IGNORE"
  962.                 stat% = opignor%:(rulhand%)
  963.             ELSEIF Gtoken$ = "DONEIF"
  964.                 stat% = opdif%:(rulhand%)
  965.             ELSEIF Gtoken$ = "IF"
  966.                 stat% = opif%:(rulhand%)
  967.             ELSEIF Gtoken$ = "ELSEIF"
  968.                 stat% = opelif%:(rulhand%)
  969.             ELSEIF Gtoken$ = "ELSE"
  970.                 stat% = opelse%:(rulhand%)
  971.             ELSEIF Gtoken$ = "@"
  972.                 stat% = opcall%:(rulhand%)
  973.             ELSEIF Gtoken$ = "+"
  974.                 stat% = opadd%:(rulhand%, 1)
  975.             ELSEIF Gtoken$ = "-"
  976.                 stat% = opadd%:(rulhand%, -1)
  977.             ELSEIF Gtoken$ = "}"
  978.                 REM It is the end of a if-block: nothing...
  979.             ELSEIF Gtoken$ = ";"
  980.                 REM nothing...
  981.             ELSE
  982.                 REM might be an alias
  983.                 i% = aliget%:(Gtoken$)
  984.                 IF i%>0
  985.                     Gabsdat&(1) = Galias&(i%)
  986.                     Greptyp% = 1
  987.                 ELSE
  988.                     synterr%:("CALCDAT: found unexpected token '"+Gtoken$+"'" )
  989.                     RETURN skipto%:(rulhand%, ";", 0)
  990.                 ENDIF
  991.             ENDIF
  992.         ENDIF
  993.     UNTIL stat%<0 OR Gtoken$=";"
  994.  
  995.     REM For all calculated dates
  996.     i% = 1
  997.     WHILE i% <= Gdattot%
  998.         REM Convert to Psion daynum
  999.         IF Gabsdat&(i%) <> 0 AND Gcalc
  1000.             Gdaynum&(i%) = dnumabs&:(Gabsdat&(i%))
  1001.         ENDIF
  1002.  
  1003.         IF Gtoken$=";" AND Gcalc
  1004.             Gdaytxt$(i%) = fixtxt$:(Gabsdat&(i%), Gdaytxt$(i%))
  1005.         ENDIF
  1006.         i% = i% + 1
  1007.     ENDWH
  1008.  
  1009.     IF Greptyp% = 0
  1010.         RETURN quit%:("Greptyp% not set","")
  1011.     ENDIF
  1012.     
  1013.     RETURN stat%
  1014. ENDP
  1015.  
  1016. REM  Convert date (year, month, day) to daynum
  1017. PROC dt2dnm&:(year%, month%, day%)
  1018.     RETURN DATETOSECS(year%, month%, day%, 0, 0, 0) / 60/60/24
  1019. ENDP
  1020.  
  1021. REM  Get the next token. Check that it is of type type$.
  1022. REM  If value$ is nonempty, check the token value as well.
  1023. REM  Report and return a syntax error if not OK.
  1024. PROC expect%:(rulhand%, where$, type$, value$)
  1025.     LOCAl   stat%, negative
  1026.  
  1027.     negative = 0
  1028.     
  1029.     stat% = gettok%:(rulhand%)
  1030.     IF stat% >= 0
  1031.  
  1032.         REM Check for "synthetic tokens" (really syntactic constructs)
  1033.         IF type$="negvalue"
  1034.             REM Expect {"-"} <value>
  1035.             IF Gtoken$="-"
  1036.                 negative = -1
  1037.                 REM Get the value
  1038.                 stat% = gettok%:(rulhand%)
  1039.                 IF stat%<0
  1040.                     RETURN stat%
  1041.                 ENDIF
  1042.             ENDIF
  1043.             IF Gtoktyp$<>"value"
  1044.                 RETURN synterr%:(where$+": expected a "+type$+", found "+Gtoktyp$+" '"+Gtoken$+"'")
  1045.             ENDIF
  1046.             Gtoktyp$ = type$
  1047.             IF negative
  1048.                 Gtoken$ = "-" + Gtoken$
  1049.                 Gtoken& = -Gtoken&
  1050.             ENDIF
  1051.         ENDIF
  1052.     
  1053.         IF Gtoktyp$<>type$
  1054.             RETURN synterr%:(where$+": expected a "+type$+", found "+Gtoktyp$+" '"+Gtoken$+"'")
  1055.         ENDIF
  1056.         IF value$<>"" AND value$<>Gtoken$
  1057.             RETURN synterr%:(where$+": expected '" +value$+ "', found '"+Gtoken$+"'")
  1058.         ENDIF
  1059.     ENDIF
  1060.     RETURN stat%
  1061. ENDP
  1062.  
  1063. PROC expectr%:(rulhand%, where$, type$, low%, high%)
  1064.     LOCAL   stat%
  1065.  
  1066.     stat% = expect%:(rulhand%, where$, type$, "")
  1067.     IF stat% < 0
  1068.         RETURN stat%
  1069.     ENDIF
  1070.     IF type$="value" OR type$="negvalue"
  1071.         IF Gtoken& < INT(low%) OR Gtoken& > INT(high%)
  1072.             RETURN synterr%:(where$+": expected a value between " +NUM$(low%,5) +" and "+NUM$(high%,5)+", found "+NUM$(Gtoken&,5))
  1073.         ENDIF
  1074.     ENDIF
  1075.     RETURN stat%
  1076. ENDP
  1077.  
  1078. REM  Fixup daytext.
  1079. REM     %iy             islamic year, 4 digits
  1080. REM     %hy             hebrew year, 4 digits
  1081. PROC fixtxt$:(absdat&, txt$)
  1082.     LOCAL   newtxt$(254), daytxt$(254)
  1083.     LOCAL   pos%
  1084.     LOCAL   tmp$(8)
  1085.  
  1086.     newtxt$ = ""
  1087.     daytxt$ = txt$
  1088.     pos% = LOC(daytxt$, "%")
  1089.     WHILE pos% <> 0
  1090.         REM copy text up to %
  1091.         newtxt$ = newtxt$ + LEFT$(daytxt$, pos%-1)
  1092.         daytxt$ = MID$(daytxt$, pos%, LEN(daytxt$))
  1093.  
  1094.         REM substitute %-code with text
  1095.         IF LOC(daytxt$, "%iy") = 1
  1096.             tmp$ = islabs$:(absdat&)
  1097.             newtxt$ = newtxt$ + LEFT$(tmp$, 4)
  1098.             daytxt$ = MID$(daytxt$, 4, LEN(daytxt$))
  1099.         ELSEIF LOC(daytxt$, "%hy") = 1
  1100.             tmp$ = hebabs$:(absdat&)
  1101.             newtxt$ = newtxt$ + LEFT$(tmp$, 4)
  1102.             daytxt$ = MID$(daytxt$, 4, LEN(daytxt$))
  1103.         ENDIF
  1104.         pos% = LOC(daytxt$, "%")
  1105.     ENDWH
  1106.     newtxt$ = newtxt$ + daytxt$
  1107.     RETURN newtxt$
  1108. ENDP
  1109.  
  1110. REM  Look at the disk-letters in disk$ for file$
  1111. REM  Return the first disk (with ":") where it was found or an
  1112. REM  empty string if not found.
  1113. PROC findfil$:(disks$, file$)
  1114.     LOCAL i%, disk$(1)
  1115.     i% = 1
  1116.     WHILE i% <= LEN(disks$)
  1117.         disk$ = MID$(disks$, i%, 1)
  1118.         IF EXIST(disk$+":"+file$)
  1119.             RETURN disk$+":"
  1120.         ENDIF
  1121.         i% = i% + 1
  1122.     ENDWH
  1123.     RETURN ""
  1124. ENDP
  1125.  
  1126. REM  Get an alias. Returns the alias name in global Galias$
  1127. REM  Expect: '=' <symb> ':'
  1128. PROC getali%:(rulhand%)
  1129.  
  1130.     LOCAL stat%
  1131.  
  1132.     REM skip over the '='
  1133.     stat% = expect%:(rulhand%, "GETALI", "char", "=")
  1134.     IF stat% < 0
  1135.         RETURN stat%
  1136.     ENDIF
  1137.  
  1138.     REM Get the symbol
  1139.     stat% = expect%:(rulhand%, "GETALI", "symb", "")
  1140.     IF stat% < 0
  1141.         RETURN stat%
  1142.     ENDIF
  1143.  
  1144.     REM  Save the name
  1145.     IF LEN(Gtoken$)<=33
  1146.         Galias$ = Gtoken$
  1147.     ELSE
  1148.         RETURN quit%:("Alias too long:", Gtoken$)
  1149.     ENDIF
  1150.  
  1151.     REM skip over the ';'
  1152.     stat% = expect%:(rulhand%, "GETALI", "char", ";")
  1153.     IF stat% < 0
  1154.         RETURN stat%
  1155.     ENDIF
  1156.  
  1157.     REM  Check that the name hasn't been used yet
  1158.     IF aliget%:(Galias$)<>0
  1159.         RETURN synterr%:("GETALI: alias '"+Galias$+"' already defined")
  1160.     ENDIF
  1161.     
  1162.     RETURN stat%
  1163. ENDP
  1164.  
  1165. REM Get a boolean expression. Return its value in Gbool
  1166. REM Expect 'WEEKDAY' | 'OR' | 'NOT' 
  1167. REM Expect  'WEEKDAY' '(' day ')' { 'OR' 'WEEKDAY' '(' day ')' } ')'
  1168. PROC getbool%:(rulhand%)
  1169.     LOCAL   stat%, result, state$(3)
  1170.  
  1171.     Gbool = 0
  1172.     result = 0
  1173.     state$ = ""
  1174.     DO
  1175.         stat% = gettok%:(rulhand%)
  1176.         IF stat% >= 0
  1177.             IF Gtoken$ = "WEEKDAY"
  1178.                 stat% = opwd%:(rulhand%)
  1179.                 IF state$ = ""
  1180.                     result = Gbool
  1181.                 ELSEIF state$ = "OR"
  1182.                     result = result OR Gbool
  1183.                 ENDIF
  1184.                 state$ = ""
  1185.                 REM don't confuse the ending ) in WEEKDAY with the last
  1186.                 Gtoken$ = "" 
  1187.             ELSEIF Gtoken$ = "OR"
  1188.                 state$ = "OR"
  1189.             ELSEIF Gtoken$ = "NOT"
  1190.                 REM TBD: check for state
  1191.                 stat% = opnot%:(rulhand%)
  1192.                 result = Gbool
  1193.                 Gtoken$ = "" 
  1194.             ELSEIF Gtoken$ = "@"
  1195.                 stat% = opcall%:(rulhand%)
  1196.             ELSEIF Gtoken$ = ")"
  1197.                 REM nothing...
  1198.             ELSE
  1199.                 synterr%:("GETBOOL: found unexpected token '"+Gtoken$+"'" )
  1200.                 RETURN skipto%:(rulhand%, ";", 0)
  1201.             ENDIF
  1202.         ENDIF
  1203.     UNTIL stat%<0 OR Gtoken$=")"
  1204.  
  1205.     Gbool = result
  1206.     
  1207.     RETURN stat%
  1208.  
  1209. ENDP
  1210.  
  1211. REM  Get and calculate this date. The '{' is already in Gtoken$.
  1212. REM  Expect { ['style' '=' <keyword> ';'] | ['year_symbol' '=' <char> ';']  |
  1213. REM           ['alias' '=' <symbol> ';']  | <dayname> '=' <calculation> ';' |
  1214. REM           ['entry_type' '=' <keyword> ';' ] }...
  1215. PROC getdate%:(rulhand%)
  1216.     LOCAL   stat%, i%
  1217.     LOCAL   gotdate
  1218.  
  1219.     REM  Init with defaults
  1220.     Gstyle%  = Gdstyle%
  1221.     Gyrsymb% = Gdyrsym%
  1222.     Galias$  = ""
  1223.     Gabsdat&(1) = 0
  1224.     Gdattot% = 1
  1225.  
  1226.     gotdate  = 0
  1227.     DO
  1228.         stat% = gettok%:(rulhand%)
  1229.         IF stat% >= 0
  1230.             REM  handle top level tokens: 'style', 'year_symbol', alias, <dayname>, 'entry_type'
  1231.             IF Gtoken$ = "STYLE"
  1232.                 stat% = getstyl%:(rulhand%)
  1233.             ELSEIF Gtoken$ = "YEAR_SYMBOL"
  1234.                 stat% = getyrsm%:(rulhand%)
  1235.             ELSEIF Gtoken$ = "ALIAS"
  1236.                 stat% = getali%:(rulhand%)
  1237.             ELSEIF Gtoken$ = "ENTRY_TYPE"
  1238.                 stat% = getetyp%:(rulhand%)
  1239.             ELSEIF Gtoken$ = "@"
  1240.                 stat% = opcall%:(rulhand%)
  1241.             ELSEIF Gtoken$ = "}"
  1242.                 IF NOT gotdate
  1243.                     RETURN synterr%:("GETDATE: found no date")
  1244.                 ENDIF
  1245.                 BREAK
  1246.             ELSEIF Gtoktyp$ = "text"
  1247.                 gotdate = -1
  1248.  
  1249.                 REM We don't know yet how many dates that will be calculated,
  1250.                 REM so we will save the text in the whole array
  1251.                 i% = 1
  1252.                 WHILE i% <= 2
  1253.                     Gdaytxt$(i%) = Gtoken$
  1254.                     i% = i% + 1
  1255.                 ENDWH
  1256.                 REM Returns the date in Gabsdat& and Gdaynum&
  1257.                 stat% = calcdat%:(rulhand%)
  1258.             ELSE
  1259.                 RETURN synterr%:("GETDATE: expected a date, found '"+Gtoken$+"'")
  1260.             ENDIF
  1261.         ENDIF
  1262.     UNTIL stat% < 0
  1263.  
  1264.     REM  Save the date if there is an alias
  1265.     IF Galias$<>"" AND stat%>=0
  1266.         stat% = aliadd%:(Galias$, Gabsdat&(1))
  1267.     ENDIF
  1268.  
  1269.     RETURN stat%
  1270. ENDP
  1271.  
  1272. REM Get date format, store in global variables.
  1273. PROC getdtfmt:
  1274.     LOCAL buf%(20)        REM 40 byte
  1275.  
  1276.     REM Call GenGetCountryData
  1277.     CALL($058B,ADDR(buf%()))
  1278.  
  1279.     Gdtfmt% = PEEKB(UADD(ADDR(buf%()),4))
  1280.     Gdtsep$ = CHR$(PEEKB(UADD(ADDR(buf%()),13)))
  1281. ENDP
  1282.  
  1283. REM  Read entry type into Genttyp%.
  1284. REM  The keyword ENTRY_TYPE is already in Gtoken$
  1285. REM  Expect '=' {"IGNORE"|"UNTIMED"|"ANNIVERSARY"} ';'
  1286. PROC getetyp%:(rulhand%)
  1287.  
  1288.     LOCAL stat%
  1289.  
  1290.     REM skip over the '='
  1291.     stat% = expect%:(rulhand%, "GETETYP", "char", "=")
  1292.     IF stat% < 0
  1293.         RETURN stat%
  1294.     ENDIF
  1295.  
  1296.     REM Get the keyword
  1297.     stat% = expect%:(rulhand%, "GETETYP", "symb", "")
  1298.     IF stat% < 0
  1299.         RETURN stat%
  1300.     ENDIF
  1301.  
  1302.     IF Gtoken$="IGNORE"
  1303.         Genttyp% = 0
  1304.     ELSEIF Gtoken$="UNTIMED"
  1305.         Genttyp% = 2
  1306.     ELSEIF Gtoken$="ANNIVERSARY"
  1307.         Genttyp% = 3
  1308.     ELSE
  1309.         RETURN synterr%:("GETETYP: found '"+Gtoken$+"', expected IGNORE,UNTIMED or ANNIVERSARY")
  1310.     ENDIF
  1311.  
  1312.     REM skip over the ';'
  1313.     stat% = expect%:(rulhand%, "GETETYP", "char", ";")
  1314.     IF stat% < 0
  1315.         RETURN stat%
  1316.     ENDIF
  1317.     
  1318.     RETURN stat%
  1319. ENDP
  1320.  
  1321.  
  1322. REM  Get next holiday from file rulhand%.
  1323. REM  Return end-of-file at end or error.
  1324. PROC gethol%:(rulhand%)
  1325.     LOCAL   stat%, i%
  1326.  
  1327.     REM  Set the default values
  1328.     Gyrsymb% = Gdyrsym%
  1329.     Gstyle%  = Gdstyle%
  1330.     Galias$  = ""
  1331.     Genttyp% = Gdenttp%
  1332.     DO
  1333.         stat% = gettok%:(rulhand%)
  1334.         IF stat% >= 0
  1335.             REM  handle top level tokens: 'style', 'year_symbol', '{', <dayname>, 'entry_type'
  1336.             IF Gtoken$ = "STYLE"
  1337.                 stat% = getstyl%:(rulhand%)
  1338.                 Gdstyle% = Gstyle%
  1339.             ELSEIF Gtoken$ = "YEAR_SYMBOL"
  1340.                 stat% = getyrsm%:(rulhand%)
  1341.                 Gdyrsym% = Gyrsymb%
  1342.             ELSEIF Gtoken$ = "ENTRY_TYPE"
  1343.                 stat% = getetyp%:(rulhand%)
  1344.             ELSEIF Gtoken$ = "@"
  1345.                 stat% = opcall%:(rulhand%)
  1346.             ELSEIF Gtoken$ = "{"
  1347.                 stat% = getdate%:(rulhand%)
  1348.                 BREAK
  1349.             ELSEIF Gtoktyp$ = "text"
  1350.                 i% = 1
  1351.                 WHILE i% <= 2
  1352.                     Gdaytxt$(i%) = Gtoken$
  1353.                     i% = i% + 1
  1354.                 ENDWH
  1355.                 REM Returns the date in Gabsdat& and Gdaynum&
  1356.                 stat% = calcdat%:(rulhand%)
  1357.                 BREAK
  1358.             ELSE
  1359.                 RETURN synterr%:("GETHOL: found unexpected token '"+Gtoken$+"'" )
  1360.             ENDIF
  1361.         ENDIF
  1362.     UNTIL stat% < 0
  1363.     
  1364.     RETURN stat%
  1365. ENDP
  1366.  
  1367.  
  1368. REM  Read style(s) into Gstyle%.
  1369. REM  The keyword STYLE is already in Gtoken$
  1370. REM  Expect '=' {"BOLD"|"ITALIC"|"UNDERLINE|NORMAL"} [","] ... ';'
  1371. PROC getstyl%:(rulhand%)
  1372.  
  1373.     LOCAL stat%
  1374.  
  1375.     REM skip over the '='
  1376.     stat% = expect%:(rulhand%, "GETSTYL", "char", "=")
  1377.     IF stat% < 0
  1378.         RETURN stat%
  1379.     ENDIF
  1380.  
  1381.     Gstyle% = 0
  1382.     DO
  1383.         REM Get the keyword
  1384.         stat% = expect%:(rulhand%, "GETSTYL", "symb", "")
  1385.         IF stat% < 0
  1386.             RETURN stat%
  1387.         ENDIF
  1388.  
  1389.         IF Gtoken$="BOLD"
  1390.             Gstyle% = Gstyle% OR 1
  1391.         ELSEIF Gtoken$="UNDERLINE"
  1392.             Gstyle% = Gstyle% OR 2
  1393.         ELSEIF Gtoken$="ITALIC"
  1394.             Gstyle% = Gstyle% OR 32
  1395.         ELSEIF Gtoken$="NORMAL"
  1396.             Gstyle% = 0
  1397.         ELSE
  1398.             RETURN synterr%:("GETSTYL: found '"+Gtoken$+"', expected BOLD,ITALIC,UNDERLINE or NORMAL")
  1399.         ENDIF
  1400.  
  1401.         REM Get a ',' or ';'
  1402.         stat% = gettok%:(rulhand%)
  1403.         IF stat% < 0
  1404.             RETURN stat%
  1405.         ENDIF
  1406.         IF Gtoken$<>"," AND Gtoken$<>";"
  1407.             RETURN synterr%:("GETSTYL: found '"+Gtoken$+"', expected ',' or ';'")
  1408.         ENDIF
  1409.     UNTIL Gtoken$=";"
  1410.     
  1411.     RETURN stat%
  1412. ENDP
  1413.  
  1414. REM Get a non-comment token
  1415. PROC gettok%:(rulhand%)
  1416.     LOCAL   stat%
  1417.     LOCAL   done
  1418.  
  1419.     done = 0
  1420.     DO
  1421.         stat% = gettok0%:(rulhand%)
  1422.         IF stat%<0
  1423.             RETURN stat%
  1424.         ENDIF
  1425.  
  1426.         IF (Gtoktyp$="symb" AND Gtoken$="REM") OR (Gtoktyp$="char" AND Gtoken$="!")
  1427.             REM comment; skip rest of line
  1428.             stat% = readrow%:(rulhand%)
  1429.             IF stat% < 0
  1430.                 RETURN stat%
  1431.             ENDIF
  1432.         ELSE
  1433.             done = -1
  1434.         ENDIF
  1435.     UNTIL done
  1436.     RETURN stat%
  1437. ENDP
  1438.  
  1439. REM  Get the next token from the file rulhand%
  1440. REM  The token is returned in global variables Gtoktyp$, Gtoken$ and Gtoken&
  1441. PROC gettok0%:(rulhand%)
  1442.  
  1443.     LOCAL   stat%, c%
  1444.     LOCAL   last%
  1445.     
  1446.     Gtoktyp$ = ""
  1447.     Gtoken$  = ""
  1448.     Gtoken&  = 0
  1449.  
  1450.     REM remove leading blanks and read a new row if necessary
  1451.     stat% = skipspc%:(rulhand%)
  1452.     IF stat% < 0
  1453.         RETURN stat%
  1454.     ENDIF
  1455.  
  1456.     REM If the first character is a letter, get a symbol
  1457.     REM If the first character is a number or '-', get a numeric value
  1458.     REM If the first character is a quote, get a text value
  1459.     REM Otherwise, get a single character
  1460.  
  1461.     c% = ASC(Grow$)
  1462.     IF (c%>=%a AND c%<=%z) OR (c%>=%A AND c%<=%Z)
  1463.         last%  = 0
  1464.         WHILE (c%>=%a AND c%<=%z) OR (c%>=%A AND c%<=%Z) OR (c%>=%0 AND c%<=%9) OR c%=%_
  1465.             last% = last% + 1
  1466.             c% = ASC(MID$(Grow$, last%+1, 1))
  1467.         ENDWH
  1468.         Gtoktyp$ = "symb"
  1469.         Gtoken$  = UPPER$(LEFT$(Grow$, last%))
  1470.         Grow$    = MID$(Grow$, last%+1, LEN(Grow$))
  1471.     ELSEIF (c%>=%0 AND c%<= %9)
  1472.         last%  = 0
  1473.         WHILE (c%>=%0 AND c%<=%9)
  1474.             last% = last% + 1
  1475.             c% = ASC(MID$(Grow$, last%+1, 1))
  1476.         ENDWH
  1477.         Gtoktyp$ = "value"
  1478.         Gtoken$  = LEFT$(Grow$, last%)
  1479.         Gtoken&  = VAL(Gtoken$)
  1480.         Grow$    = MID$(Grow$, last%+1, LEN(Grow$))
  1481.     ELSEIF c%=ASC("""")
  1482.         last%  = 1
  1483.         c% = ASC(MID$(Grow$, last%+1, 1))
  1484.         WHILE c%<>ASC("""") AND c%<>0
  1485.             last% = last% + 1
  1486.             c% = ASC(MID$(Grow$, last%+1, 1))
  1487.         ENDWH
  1488.         IF c%<>ASC("""")
  1489.             RETURN synterr%:("GETTOK0: no terminating "" found")
  1490.         ENDIF                   
  1491.         Gtoktyp$ = "text"
  1492.         Gtoken$  = MID$(Grow$, 2, last%-1)
  1493.         Grow$    = MID$(Grow$, last%+2, LEN(Grow$))
  1494.     ELSE
  1495.         Gtoktyp$ = "char"
  1496.         Gtoken$  = LEFT$(Grow$, 1)
  1497.         Grow$    = MID$(Grow$, 2, LEN(Grow$))
  1498.     ENDIF
  1499.  
  1500.     RETURN stat%
  1501. ENDP
  1502.  
  1503. REM  Read year symbol into  Gyrsymb%.
  1504. REM  The keyword YEAR_SYMBOL is already in Gtoken$
  1505. REM  Expect '=' <TEXT> ';'
  1506. PROC getyrsm%:(rulhand%)
  1507.  
  1508.     LOCAL stat%
  1509.  
  1510.     REM skip over the '='
  1511.     stat% = expect%:(rulhand%, "GETYRSM", "char", "=")
  1512.     IF stat% < 0
  1513.         RETURN stat%
  1514.     ENDIF
  1515.  
  1516.     Gyrsymb% = 0
  1517.     REM Get the character
  1518.     stat% = expect%:(rulhand%, "GETYRSM", "text", "")
  1519.     IF stat% < 0
  1520.         RETURN stat%
  1521.     ENDIF
  1522.  
  1523.     IF LEN(Gtoken$)>1
  1524.         RETURN synterr%:("GETYRSM: found '"+Gtoken$+"', expected one character")
  1525.     ENDIF
  1526.  
  1527.     Gyrsymb% = ASC(Gtoken$)
  1528.  
  1529.     REM skip over the ';'
  1530.     stat% = expect%:(rulhand%, "GETYRSM", "char", ";")
  1531.     IF stat% < 0
  1532.         RETURN stat%
  1533.     ENDIF
  1534.  
  1535.     RETURN stat%
  1536. ENDP
  1537.  
  1538. REM  Move the current position down one character line, scrolling if necessary
  1539. REM  Move the position down one font height. If the position + font descent 
  1540. REM  then is below the windows lower border then scroll the window up and
  1541. REM  move the position up one row.
  1542. PROC gNL:
  1543.     REM move down one line
  1544.     gMOVE -gX,Gscrowh%
  1545.     IF gY+Gscrowd%>gHEIGHT
  1546.         gSCROLL 0,-Gscrowh%
  1547.         gMOVE 0,-Gscrowh%
  1548.     ENDIF
  1549. ENDP
  1550.  
  1551. REM  Get absolute date for hebrew date hday&/hmonth& in the hebrew
  1552. REM  year that occurs during gregorian year year&
  1553. REM  Dates returned in Gabsdat,Gdattot
  1554. PROC hebfix:(hday&, hmonth&, year&)
  1555.     LOCAL   hglim$(8)   REM  hebrew date for gregorian 1/1 in year&
  1556.     LOCAL   md$(4)              REM  hmonth&/hday& as numeric string
  1557.     LOCAL   hyear&      REM  hebrew year to use
  1558.     LOCAL       abs&
  1559.  
  1560.     REM Get the hebrew date for Jan 1st in selected gregorian year
  1561.     hglim$ = hebabs$:(absgreg&:(INT(1), INT(1), year&))
  1562.     
  1563.     md$ = RIGHT$("00"+NUM$(hmonth&,2), 2) + RIGHT$("00"+NUM$(hday&,2), 2)
  1564.  
  1565.     REM Is the requested hebrew month/day in the first part of
  1566.     REM the gregorian year?
  1567.     IF md$ >= RIGHT$(hglim$, 4) OR md$ < "0701"
  1568.         REM yes, we can use the hebrew year from Jan 1st
  1569.         hyear& = VAL(LEFT$(hglim$, 4)) 
  1570.     ELSE
  1571.         REM no, use the next year (after hebrew new year)
  1572.         hyear& = VAL(LEFT$(hglim$, 4)) + 1
  1573.     ENDIF
  1574.  
  1575.     Gdattot% = 1
  1576.     Gabsdat&(1) = absheb&:(hday&, hmonth&, hyear&)
  1577. ENDP
  1578.  
  1579. REM  Apply the selected rule file to a sequence of years.
  1580. REM  Prompt for start year, end year.
  1581. REM  Prompt for agenda file if AGN is true.
  1582. REM  Call FUNC$ for each year.
  1583. PROC holdo:(func$, agn%)
  1584.  
  1585.     LOCAL   stat%
  1586.     LOCAL   d%, dorep%
  1587.     LOCAL   year1&, year2&, year%, repend&
  1588.  
  1589.     year1& = Gyear1%
  1590.     year2& = Gyear2%
  1591.     dorep% = -1             REM write repeats for first year
  1592.  
  1593.     Gagnfil$ = "m:\agn\*.agn"
  1594.     IF Grulhnd% <> 0
  1595.         LOCK ON
  1596.         REM read the file and put the holidays in the arrays
  1597.         IF agn%
  1598.             dINIT "Write holidays"
  1599.         ELSE
  1600.             dINIT "View holidays"
  1601.         ENDIF
  1602.         dLONG year1&, "Start year", 1980, 2049
  1603.         dLONG year2&, "End year", 1980, 2049
  1604.         
  1605.         IF Gscreen% = 2
  1606.             dCHOICE Grepmod%, "Repeat entries", "No,If possible"
  1607.             dCHOICE Grepsho%, "Repeat: Show","All occurrences,Next only"
  1608.             dCHOICE Grepend%, "Repeat: Until","Forever,End year"
  1609.         ELSE
  1610.             dCHOICE Grepmod%, "Use repeating entries", "No,If possible"        
  1611.             dCHOICE Grepsho%, "Repeating entry: Show","All occurrences,Next only"
  1612.             dCHOICE Grepend%, "Repeating entry: Until","Forever,End year"
  1613.         ENDIF
  1614.         IF agn%
  1615.             IF Gscreen% = 2
  1616.                 dFILE Gagnfil$, "File:", 8
  1617.             ELSE
  1618.                 dFILE Gagnfil$, "Agenda file:", 8
  1619.             ENDIF
  1620.         ENDIF
  1621.         d% = DIALOG
  1622.         LOCK OFF
  1623.         IF d%
  1624.             Gyear1% = year1&
  1625.             Gyear2% = year2&
  1626.  
  1627.             IF Grepend% = 1
  1628.                 REM Repeat forever
  1629.                 repend& = $FFFF
  1630.             ELSE
  1631.                 REM Repeat until end year
  1632.                 repend& = dt2dnm&:(Gyear2%, 12, 31)
  1633.             ENDIF
  1634.  
  1635.             IF agn%
  1636.                 LOCK ON
  1637.                 dINIT "Write to this file?"
  1638.                 dTEXT "",RIGHT$(Gagnfil$,25)
  1639.                 dBUTTONS "No",%N,"Yes",%Y
  1640.                 d% = DIALOG
  1641.                 LOCK OFF
  1642.                 IF d% = %y
  1643.                     d% = backup%:(Gagnfil$)
  1644.                 ENDIF
  1645.             ELSE
  1646.                 d% = %y
  1647.             ENDIF
  1648.             IF d% = %y
  1649.                 gCLS
  1650.     
  1651.                 year% = Gyear1%
  1652.                 WHILE year%<=Gyear2% AND (stat%>=0 OR stat%=-36)
  1653.                     stat% = rulrew:
  1654.                     IF stat% >= 0
  1655.                         Gyear% = year%
  1656.                         stat% = @%(func$):(Grulhnd%, Gyear%, dorep%, Grepsho%, repend&)
  1657.                         year% = year% + 1
  1658.                         dorep% = 0      REM any repeat only on first year
  1659.                     ENDIF
  1660.                 ENDWH
  1661.                 REM let the user view the result for a while unless there was an error
  1662.                 IF stat%>=0 OR stat%=-36
  1663.                     BUSY "Press any key",3
  1664.                     LOCK ON
  1665.                     GET
  1666.                     LOCK OFF
  1667.                 ENDIF
  1668.                 BUSY OFF
  1669.                 scredraw:(1)
  1670.             ELSE
  1671.                 gIPRINT("Cancelled")
  1672.             ENDIF
  1673.         ENDIF
  1674.     ELSE
  1675.         gIPRINT "No holiday file open"
  1676.     ENDIF
  1677.     RETURN 0
  1678. ENDP
  1679.  
  1680. REM Load the holidays as specified in file rulhand% for year%
  1681. PROC holload%:(rulhand%, year%)
  1682.     LOCAL   stat%
  1683.     LOCAL   i%
  1684.  
  1685.     BUSY "Loading..."
  1686.     i% = 1
  1687.     Gholtot% = 0
  1688.     DO
  1689.         stat% = gethol%:(rulhand%)
  1690.         IF stat% >= 0
  1691.             IF i% <= 101
  1692.                 Gflags%(i%) = 0
  1693.                 Gholtot% = i%
  1694.                 Gyrsyma%(i%) = Gyrsymb%
  1695.                 REM Allocate memory for the day text
  1696.                 Gdaytxa%(i%) = ALLOC(LEN(Gdaytxt$(1))+1)
  1697.                 IF Gdaytxa%(i%) = 0
  1698.                     Gholtot% = Gholtot% - 1
  1699.                     BUSY OFF
  1700.                     RETURN quit%:("Not enough memory", "")
  1701.                 ENDIF
  1702.                 POKE$ Gdaytxa%(i%), Gdaytxt$(1)
  1703.                 Gstylea%(i%) = Gstyle%
  1704.                 Gselcta(i%)  = -1
  1705.                 IF Greptyp%<>1
  1706.                     Gflags%(i%) = 1
  1707.                 ENDIF
  1708.                 IF Galias$<>""
  1709.                     Gflags%(i%) = Gflags%(i%) OR 2
  1710.                 ENDIF
  1711.                 BUSY Gdaytxt$(1)
  1712.                 i% = i% + 1
  1713.             ELSE
  1714.                 gIPRINT "Overflow"
  1715.             ENDIF
  1716.         ENDIF
  1717.     UNTIL stat% < 0
  1718.  
  1719.     Gholidx% = 1
  1720.  
  1721.     BUSY OFF
  1722.     RETURN stat%
  1723. ENDP
  1724.  
  1725. REM View the holidays as specified in file rulhand% for year%
  1726. PROC holview%:(rulhand%, year%, dorep%, repsho%, repend&)
  1727.     LOCAL   stat%
  1728.     LOCAL   currow%
  1729.     LOCAL   k%, i%, reptyp%
  1730.  
  1731.     BUSY "Press key to pause",3
  1732.     
  1733.     currow% = 0
  1734.     DO
  1735.         currow% = currow% + 1
  1736.         REM If we don't have to calculate and aren't asked to then don't
  1737.         IF (Gflags%(currow%) AND $2) = 0 AND NOT Gselcta(currow%)
  1738.             Gcalc = 0
  1739.             gIPRINT "Skipping. Press key to pause"
  1740.         ELSE
  1741.             gIPRINT ""
  1742.         ENDIF
  1743.         stat% = gethol%:(rulhand%)
  1744.         Gcalc = -1
  1745.         IF stat% >= 0
  1746.             i% = 1
  1747.             WHILE i% <= Gdattot%
  1748.                 IF Gselcta(currow%)
  1749.                     IF Grepmod% = 1                 REM If we never write repeating entries...
  1750.                         reptyp% = 1                     REM then force the type to not repeatable
  1751.                     ELSE
  1752.                         reptyp% = Greptyp%
  1753.                     ENDIF
  1754.                     IF reptyp%=1 OR dorep%<>0
  1755.                         gshowhol:(Gdaynum&(i%), Gyrsymb%, Gstyle%, Gdaytxt$(i%), reptyp%, Grepmon%, Grepday%, Grepnbr%, repsho%, Genttyp%)
  1756.                     ELSE
  1757.                         gIPRINT "Skipping. Press key to pause"
  1758.                     ENDIF
  1759.                 ENDIF
  1760.                 i% = i% + 1
  1761.             ENDWH
  1762.             k% = KEY
  1763.             IF k%<>0 AND k%<>27
  1764.                 BUSY "Press any key",3
  1765.                 LOCK ON
  1766.                 GET
  1767.                 LOCK OFF
  1768.                 BUSY "Press key to pause",3
  1769.             ENDIF
  1770.         ENDIF
  1771.     UNTIL stat% < 0 OR k%=27 OR currow%=Gholtot%
  1772.     
  1773.     BUSY OFF
  1774.     IF k%=27
  1775.         gIPRINT "Cancelled"
  1776.         RETURN -1000
  1777.     ENDIF
  1778.  
  1779.     RETURN stat%
  1780. ENDP
  1781.  
  1782. REM  Write the holidays as specified in file rulhand% for year%
  1783. REM  to agenda file Gagnfil$
  1784. PROC holwrit%:(rulhand%, year%, dorep%, repsho%, repend&)
  1785.     LOCAL   stat%, stat2%
  1786.     LOCAL   agnhand%        REM handle for agenda file
  1787.     LOCAL   offset&
  1788.     LOCAL   currow%
  1789.     LOCAL   k%, i%, reptyp%
  1790.  
  1791.     REM Open the agenda file and verify that it is an agenda file
  1792.     stat% = openagn%:(ADDR(agnhand%), Gagnfil$)
  1793.     IF stat% < 0
  1794.         RETURN stat%
  1795.     ENDIF
  1796.  
  1797.     stat% = agnchek%:(agnhand%)
  1798.     IF stat% < 0
  1799.         RETURN stat%
  1800.     ENDIF
  1801.  
  1802.     REM Position to the end of the file
  1803.     offset& = 0
  1804.     stat% = IOSEEK(agnhand%, 2, offset&)
  1805.     IF stat% < 0
  1806.         RETURN abort%:(stat%,"seeking end of agenda file")
  1807.     ENDIF
  1808.  
  1809.     BUSY "Press key to cancel",3
  1810.     
  1811.     currow% = 0
  1812.     DO
  1813.         currow% = currow% + 1
  1814.         IF (Gflags%(currow%) AND $2) = 0 AND NOT Gselcta(currow%)
  1815.             Gcalc = 0
  1816.             gIPRINT "Skipping. Press key to cancel"
  1817.         ELSE
  1818.             gIPRINT ""
  1819.         ENDIF
  1820.         stat% = gethol%:(rulhand%)
  1821.         Gcalc = -1
  1822.         IF stat% >= 0
  1823.             k% = key
  1824.             IF Gselcta(currow%) AND k% = 0
  1825.                 i% = 1
  1826.                 WHILE i% <= Gdattot% AND stat% >= 0
  1827.                     IF Grepmod% = 1                 REM If we never write repeating entries...
  1828.                         reptyp% = 1                     REM then force the type to not repeatable
  1829.                     ELSE
  1830.                         reptyp% = Greptyp%
  1831.                     ENDIF
  1832.                     IF reptyp%=1 OR dorep%<>0
  1833.                         REM can repeat and should repeat
  1834.                         gshowhol:(Gdaynum&(i%), Gyrsymb%, Gstyle%, Gdaytxt$(i%), reptyp%, Grepmon%, Grepday%, Grepnbr%, repsho%, Genttyp%)
  1835.                         stat% = agnwrit%:(agnhand%, Gdaynum&(i%), Gyrsymb%, Gstyle%, Gdaytxt$(i%), reptyp%, Grepday%, Grepnbr%, repsho%, repend&, Genttyp%)
  1836.                     ELSE
  1837.                         gIPRINT "Skipping. Press key to cancel"
  1838.                     ENDIF
  1839.                     i% = i% + 1
  1840.                 ENDWH
  1841.             ENDIF
  1842.         ENDIF
  1843.     UNTIL stat% < 0 OR k% <> 0 OR currow% = Gholtot%
  1844.  
  1845.     REM Close the agenda file
  1846.     stat2% = IOCLOSE(agnhand%)
  1847.     IF stat2% < 0
  1848.         RETURN abort%:(stat2%,"closing agenda file")
  1849.     ENDIF
  1850.  
  1851.     REM return a dummy error if a cancel key was pressed
  1852.     IF k%<>0
  1853.         gIPRINT "Cancelled"
  1854.         RETURN -1000
  1855.     ENDIF
  1856.     RETURN stat%
  1857. ENDP
  1858.  
  1859. REM Return true if the first character of txt$ is empty, a space or a tab
  1860. PROC isblank:(txt$)
  1861.     LOCAL   c%
  1862.  
  1863.     c% = ASC(txt$)
  1864.     IF c%=0 OR c%=32 OR c%=9
  1865.         RETURN -1
  1866.     ELSE
  1867.         RETURN 0
  1868.     ENDIF
  1869. ENDP
  1870.  
  1871. REM  Get absolute date(s) for islamic date iday&/imonth& in the islamic
  1872. REM  year that occurs during gregorian year year&
  1873. REM  Dates returned in Gabsdat,Gdattot
  1874. PROC islfix:(iday&, imonth&, year&)
  1875.     LOCAL   iglim$(8)   REM  islamic date for gregorian 1/1 in year&
  1876.     LOCAL   md$(4)              REM  imonth&/iday& as numeric string
  1877.     LOCAL   iyear&      REM  islamic year to use
  1878.     LOCAL       abs&
  1879.  
  1880.     iglim$ = islabs$:(absgreg&:(INT(1), INT(1), year&))
  1881.     md$ = RIGHT$("00"+NUM$(imonth&,2), 2) + RIGHT$("00"+NUM$(iday&,2), 2)
  1882.  
  1883.     IF md$ >= RIGHT$(iglim$, 4) 
  1884.     iyear& = VAL(LEFT$(iglim$, 4))
  1885.     ELSE
  1886.     iyear& = VAL(LEFT$(iglim$, 4)) + 1
  1887.     ENDIF
  1888.  
  1889.     Gabsdat&(1) = absisl&:(iday&, imonth&, iyear&)
  1890.  
  1891.     REM Is there a second occurance of this date during year%?
  1892.     REM Is it within current gregorian year?
  1893.     abs& = absisl&:(iday&, imonth&, INT(iyear&+1))
  1894.     IF abs& <= absgreg&:(INT(31), INT(12), year&)
  1895.         Gabsdat&(2) = abs&
  1896.         Gdattot% = 2
  1897.     ENDIF
  1898. ENDP
  1899.  
  1900. PROC nofile:
  1901.     LOCAL   oldfont%
  1902.     LOCAL   msg$(15)
  1903.  
  1904.     REM double height
  1905.     gSTYLE 8
  1906.     oldfont% = setfont%:(6)
  1907.  
  1908.     msg$ = "No file open"
  1909.  
  1910.     gAT Gscwidt%/2-gTWIDTH(msg$),Gscheit%/2+Gscrowh%/2
  1911.     gPRINT msg$
  1912.  
  1913.     gSTYLE 0
  1914.     setfont%:(oldfont%)
  1915. ENDP
  1916.  
  1917. REM  add next value * factor to Gabsdat&
  1918. PROC opadd%:(rulhand%, factor%)
  1919.     LOCAL   stat%
  1920.  
  1921.     Greptyp% = 1
  1922.     
  1923.     stat% = expect%:(rulhand%, "OPADD", "value", "")
  1924.     IF stat% < 0
  1925.         RETURN stat%
  1926.     ENDIF
  1927.  
  1928.     Gabsdat&(1) = Gabsdat&(1) + Gtoken& * factor%
  1929.     
  1930.     RETURN stat%
  1931. ENDP
  1932.  
  1933. REM Read [<string>] <token>
  1934. PROC opcall%:(rulhand%)
  1935.     LOCAL   module$(50)
  1936.     LOCAL   proc$(8)
  1937.     LOCAL   stat%
  1938.  
  1939.     stat% = gettok%:(rulhand%)
  1940.     IF stat% < 0
  1941.         RETURN stat%
  1942.     ENDIF
  1943.     IF Gtoktyp$="text"
  1944.         REM Got the optional module name
  1945.         module$ = Gtoken$
  1946.  
  1947.         IF Gusrmod$<>module$
  1948.             IF Gusrmod$<>""
  1949.                 UNLOADM Gusrmod$
  1950.             ENDIF
  1951.             LOADM module$
  1952.             Gusrmod$ = module$
  1953.         ENDIF
  1954.         
  1955.         stat% = gettok%:(rulhand%)
  1956.         IF stat% < 0
  1957.             RETURN stat%
  1958.         ENDIF
  1959.     ENDIF
  1960.  
  1961.     IF Gtoktyp$<>"symb"
  1962.         RETURN synterr%:("OPCALL: found unexpected token '"+Gtoken$+"'" )
  1963.     ENDIF
  1964.  
  1965.     proc$ = Gtoken$
  1966.  
  1967.     RETURN @%(proc$):(rulhand%)
  1968. ENDP
  1969.  
  1970. REM Read DONEIF (bool)
  1971. REM Expect <bool-expr> ';'
  1972. REM If bool is true, skip rest of directive up to ';'
  1973. PROC opdif%:(rulhand%)
  1974.     LOCAL   stat%
  1975.  
  1976.     Greptyp% = 1
  1977.     
  1978.     stat% = expect%:(rulhand%, "OPDIF", "char", "(")
  1979.     IF stat% < 0
  1980.         RETURN stat%
  1981.     ENDIF
  1982.  
  1983.     stat% = getbool%:(rulhand%)
  1984.     IF stat% < 0
  1985.         RETURN stat%
  1986.     ENDIF
  1987.  
  1988.     IF Gbool
  1989.         stat% = skipto%:(rulhand%, ";", -1)
  1990.     ENDIF
  1991.     
  1992.     RETURN stat%
  1993. ENDP
  1994.  
  1995. REM  Set date in Gabsdat& for year Gyear%
  1996. PROC opeastr%:(rulhand%)
  1997.     Greptyp% = 1
  1998.     Gdattot% = 1
  1999.     IF Gcalc
  2000.         Gabsdat&(1) = easter&:(INT(Gyear%))
  2001.     ENDIF
  2002.     RETURN 0
  2003. ENDP
  2004.  
  2005. REM  Read ELSEIF(bool) { ... }
  2006. REM  Expect '(' <bool> '{'
  2007. PROC opelif%:(rulhand%)
  2008.     LOCAL stat%
  2009.  
  2010.     Greptyp% = 1
  2011.  
  2012.     stat% = expect%:(rulhand%, "OPELIF", "char", "(")
  2013.     IF stat%<0
  2014.         RETURN stat%
  2015.     ENDIF
  2016.  
  2017.     REM Look at the condition if previous tests were false
  2018.     REM or we are not calculationg (only scanning)
  2019.     IF NOT Gbool OR NOT Gcalc
  2020.         stat% = getbool%:(rulhand%)
  2021.         IF stat%<0
  2022.             RETURN stat%
  2023.         ENDIF
  2024.        
  2025.         stat% = expect%:(rulhand%, "OPELIF", "char", "{")
  2026.         IF stat%<0
  2027.             RETURN stat%
  2028.         ENDIF
  2029.     
  2030.         IF (NOT Gbool AND Gcalc)
  2031.             stat% = skipto%:(rulhand%, "}", -1)
  2032.         ENDIF
  2033.     ELSE
  2034.         stat% = skipto%:(rulhand%, "}", -1)
  2035.     ENDIF
  2036.     RETURN stat%
  2037. ENDP
  2038.  
  2039. REM  Read ELSE { ... }
  2040. REM '{'
  2041. PROC opelse%:(rulhand%)
  2042.     LOCAL stat%
  2043.  
  2044.     Greptyp% = 1
  2045.        
  2046.     stat% = expect%:(rulhand%, "opelse", "char", "{")
  2047.     IF stat%<0
  2048.         RETURN stat%
  2049.     ENDIF
  2050.     
  2051.     IF (Gbool AND Gcalc)
  2052.         RETURN skipto%:(rulhand%, "}", -1)
  2053.     ENDIF
  2054.     RETURN stat%
  2055. ENDP
  2056.  
  2057.  
  2058. REM  Read FIX(m,d)
  2059. REM  Expect '(' <value> ',' <value> ')'
  2060. REM  Set date in Gabsdat& for year Gyear%
  2061. PROC opfix%:(rulhand%)
  2062.     LOCAL   stat%
  2063.     LOCAL   month%, day%            REM month in Gyear% and day in month%
  2064.  
  2065.     IF Greptyp%<>1
  2066.         Greptyp% = 2
  2067.     ENDIF
  2068.  
  2069.     stat% = expect%:(rulhand%, "OPFIX", "char", "(")
  2070.     IF stat% < 0
  2071.         RETURN stat%
  2072.     ENDIF
  2073.  
  2074.     stat% = expectr%:(rulhand%, "OPFIX", "value", 1, 12)
  2075.     IF stat% < 0
  2076.         RETURN stat%
  2077.     ENDIF
  2078.     month% = Gtoken&
  2079.     Grepmon% = month%
  2080.  
  2081.     stat% = expect%:(rulhand%, "OPFIX", "char", ",")
  2082.     IF stat% < 0
  2083.         RETURN stat%
  2084.     ENDIF
  2085.  
  2086.     stat% = expectr%:(rulhand%, "OPFIX", "value", 1, 31)
  2087.     IF stat% < 0
  2088.         RETURN stat%
  2089.     ENDIF
  2090.     day% = Gtoken&
  2091.     Grepday% = day%
  2092.  
  2093.     stat% = expect%:(rulhand%, "OPFIX", "char", ")")
  2094.     IF stat% < 0
  2095.         RETURN stat%
  2096.     ENDIF
  2097.  
  2098.     Gdattot% = 1
  2099.     IF Gcalc
  2100.         Gabsdat&(1) = absgreg&:(INT(day%), INT(month%), INT(Gyear%))
  2101.     ENDIF
  2102.  
  2103.     RETURN stat%
  2104. ENDP
  2105.  
  2106. REM  Read FLOAT(month, dname, n [, day])
  2107. REM  Holiday on nth dname (0=Sunday) in month [on or before day/month]
  2108. REM
  2109. REM  month      is month number
  2110. REM  dname      is weekday (0=Sunday, 1=Monday...)
  2111. REM  n          is nth occurance of dname; <>0. If < 0 then count from end of month
  2112. REM  day        if specified, means nth dname on or after/before the DAYth of MONTH
  2113. REM
  2114. REM  Expect '(' <value> ',' <value> ',' <negvalue> [ ',' <value>] ')'
  2115. REM  Set date in Gabsdat& for year Gyear%
  2116. PROC opfloat%:(rulhand%)
  2117.     LOCAL   stat%
  2118.     LOCAL   month&, dname&, n&, day&
  2119.  
  2120.     stat% = expect%:(rulhand%, "OPFLOAT", "char", "(")
  2121.     IF stat% < 0
  2122.         RETURN stat%
  2123.     ENDIF
  2124.  
  2125.     stat% = expectr%:(rulhand%, "OPFLOAT", "value", 1, 12)
  2126.     IF stat% < 0
  2127.         RETURN stat%
  2128.     ENDIF
  2129.     month& = Gtoken&
  2130.     Grepmon% = month&
  2131.  
  2132.     stat% = expect%:(rulhand%, "OPFLOAT", "char", ",")
  2133.     IF stat% < 0
  2134.         RETURN stat%
  2135.     ENDIF
  2136.  
  2137.     stat% = expectr%:(rulhand%, "OPFLOAT", "value", 0, 6)
  2138.     IF stat% < 0
  2139.         RETURN stat%
  2140.     ENDIF
  2141.     dname& = Gtoken&
  2142.     Grepday% = dname&
  2143.  
  2144.     stat% = expect%:(rulhand%, "OPFLOAT", "char", ",")
  2145.     IF stat% < 0
  2146.         RETURN stat%
  2147.     ENDIF
  2148.  
  2149.     stat% = expect%:(rulhand%, "OPFLOAT", "negvalue", "")
  2150.     IF stat% < 0
  2151.         RETURN stat%
  2152.     ENDIF
  2153.     n& = Gtoken&
  2154.     Grepnbr% = n&
  2155.  
  2156.     REM Get a ',' or ')'
  2157.     stat% = gettok%:(rulhand%)
  2158.     IF stat% < 0
  2159.         RETURN stat%
  2160.     ENDIF
  2161.     IF Gtoken$<>"," AND Gtoken$<>")"
  2162.         RETURN synterr%:("OPFLOAT: found '"+Gtoken$+"', expected ',' or ')'")
  2163.     ENDIF
  2164.  
  2165.     REM Get day if specified
  2166.     day& = 0
  2167.     IF Gtoken$=","
  2168.         stat% = expectr%:(rulhand%, "OPFLOAT", "value", 1, 31)
  2169.         IF stat% < 0
  2170.             RETURN stat%
  2171.         ENDIF
  2172.         day& = Gtoken&
  2173.         
  2174.         stat% = expect%:(rulhand%, "OPFLOAT", "char", ")")
  2175.         IF stat% < 0
  2176.             RETURN stat%
  2177.         ENDIF
  2178.         Greptyp% = 1
  2179.     ELSEIF Greptyp%<>1 AND n&>=-1 AND n&<=4 AND n&<>0
  2180.         Greptyp% = 3
  2181.     ELSE
  2182.         Greptyp% = 1
  2183.     ENDIF
  2184.  
  2185.     Gdattot% = 1
  2186.     IF Gcalc
  2187.         Gabsdat&(1) = nthabs&:(n&, dname&, month&, INT(Gyear%), day&)
  2188.     ENDIF
  2189.  
  2190.     RETURN stat%
  2191. ENDP
  2192.  
  2193. REM  Read HFIX(m,d)
  2194. REM  Expect '(' <value> ',' <value> ')'
  2195. REM  Set date in Gabsdat& for year Gyear%
  2196. PROC ophfix%:(rulhand%)
  2197.     LOCAL   stat%
  2198.     LOCAL   month%, day%            REM month in Gyear% and day in month%
  2199.  
  2200.     Greptyp% = 1
  2201.  
  2202.     stat% = expect%:(rulhand%, "OPHFIX", "char", "(")
  2203.     IF stat% < 0
  2204.         RETURN stat%
  2205.     ENDIF
  2206.  
  2207.     stat% = expectr%:(rulhand%, "OPHFIX", "value", 1, 13)
  2208.     IF stat% < 0
  2209.         RETURN stat%
  2210.     ENDIF
  2211.     IF Gtoken& = 13
  2212.         REM Last month of year (12 or 13 depending on leap year)
  2213.         REM (Adar is always in the spring, so this is safe)
  2214.         month% = hclmoy&:(INT(Gyear% + 3760))
  2215.     ELSE
  2216.         month% = Gtoken&
  2217.     ENDIF
  2218.  
  2219.     stat% = expect%:(rulhand%, "OPHFIX", "char", ",")
  2220.     IF stat% < 0
  2221.         RETURN stat%
  2222.     ENDIF
  2223.  
  2224.     stat% = expectr%:(rulhand%, "OPHFIX", "value", 1, 30)
  2225.     IF stat% < 0
  2226.         RETURN stat%
  2227.     ENDIF
  2228.     day% = Gtoken&
  2229.  
  2230.     stat% = expect%:(rulhand%, "OPHFIX", "char", ")")
  2231.     IF stat% < 0
  2232.         RETURN stat%
  2233.     ENDIF
  2234.  
  2235.     Gdattot% = 1
  2236.     IF Gcalc
  2237.         hebfix:(INT(day%), INT(month%), INT(Gyear%))
  2238.     ENDIF
  2239.  
  2240.     RETURN stat%
  2241. ENDP
  2242.  
  2243. REM  Read IF(bool) { ... }
  2244. REM  Expect '(' <bool> '{'
  2245. PROC opif%:(rulhand%)
  2246.     LOCAL stat%
  2247.  
  2248.     Greptyp% = 1
  2249.  
  2250.     stat% = expect%:(rulhand%, "OPIF", "char", "(")
  2251.     IF stat%<0
  2252.         RETURN stat%
  2253.     ENDIF
  2254.     
  2255.     stat% = getbool%:(rulhand%)
  2256.     IF stat%<0
  2257.         RETURN stat%
  2258.     ENDIF
  2259.        
  2260.     stat% = expect%:(rulhand%, "OPIF", "char", "{")
  2261.     IF stat%<0
  2262.         RETURN stat%
  2263.     ENDIF
  2264.     
  2265.     IF (NOT Gbool AND Gcalc)
  2266.         RETURN skipto%:(rulhand%, "}", -1)
  2267.     ENDIF
  2268.     RETURN stat%
  2269. ENDP
  2270.  
  2271. REM  Read IFIX(m,d)
  2272. REM  Expect '(' <value> ',' <value> ')'
  2273. REM  Set date in Gabsdat& for year Gyear%
  2274. PROC opifix%:(rulhand%)
  2275.     LOCAL   stat%
  2276.     LOCAL   month%, day%            REM month in Gyear% and day in month%
  2277.  
  2278.     Greptyp% = 1
  2279.  
  2280.     stat% = expect%:(rulhand%, "OPIFIX", "char", "(")
  2281.     IF stat% < 0
  2282.         RETURN stat%
  2283.     ENDIF
  2284.  
  2285.     stat% = expectr%:(rulhand%, "OPIFIX", "value", 1, 12)
  2286.     IF stat% < 0
  2287.         RETURN stat%
  2288.     ENDIF
  2289.     month% = Gtoken&
  2290.  
  2291.     stat% = expect%:(rulhand%, "OPIFIX", "char", ",")
  2292.     IF stat% < 0
  2293.         RETURN stat%
  2294.     ENDIF
  2295.  
  2296.     stat% = expectr%:(rulhand%, "OPIFIX", "value", 1, 30)
  2297.     IF stat% < 0
  2298.         RETURN stat%
  2299.     ENDIF
  2300.     day% = Gtoken&
  2301.  
  2302.     stat% = expect%:(rulhand%, "OPIFIX", "char", ")")
  2303.     IF stat% < 0
  2304.         RETURN stat%
  2305.     ENDIF
  2306.  
  2307.     Gdattot% = 1
  2308.     IF Gcalc
  2309.         islfix:(INT(day%), INT(month%), INT(Gyear%))
  2310.     ENDIF
  2311.  
  2312.     RETURN stat%
  2313. ENDP
  2314.  
  2315. REM Execute IGNORE
  2316. REM Skip rest of directive up to ';'
  2317. PROC opignor%:(rulhand%)
  2318.     LOCAL   stat%
  2319.  
  2320.     Greptyp% = 1
  2321.     Genttyp% = 0  REM don't write
  2322.     IF Gcalc
  2323.         stat% = skipto%:(rulhand%, ";", -1)
  2324.     ENDIF
  2325.     RETURN stat%
  2326. ENDP
  2327.  
  2328.  
  2329. REM  Read LAST(month)
  2330. REM  Expect '(' <value> ')'
  2331. REM  Set Gabsdat& to last day of month
  2332. PROC oplast%:(rulhand%)
  2333.     LOCAL   stat%
  2334.     LOCAL   month&
  2335.  
  2336.     Greptyp% = 1
  2337.  
  2338.     stat% = expect%:(rulhand%, "OPLAST", "char", "(")
  2339.     IF stat% < 0
  2340.         RETURN stat%
  2341.     ENDIF
  2342.  
  2343.     stat% = expectr%:(rulhand%, "OPLAST", "value", 1, 12)
  2344.     IF stat% < 0
  2345.         RETURN stat%
  2346.     ENDIF
  2347.     month& = Gtoken&
  2348.  
  2349.     stat% = expect%:(rulhand%, "OPLAST", "char", ")")
  2350.     IF stat% < 0
  2351.         RETURN stat%
  2352.     ENDIF
  2353.  
  2354.     Gdattot% = 1
  2355.     IF Gcalc
  2356.         Gabsdat&(1) = absgreg&:(ldom&:(month&, INT(Gyear%)), month&, INT(Gyear%))
  2357.     ENDIF
  2358.  
  2359.     RETURN stat%
  2360. ENDP
  2361.  
  2362. REM  Read NOT(bool)
  2363. REM  Expect '(' bool 
  2364. REM  Let getbool set Gbool
  2365. PROC opnot%:(rulhand%)
  2366.     LOCAL   stat%
  2367.  
  2368.     stat% = expect%:(rulhand%, "OPNOT", "char", "(")
  2369.     IF stat% < 0
  2370.         RETURN stat%
  2371.     ENDIF
  2372.  
  2373.     stat% = getbool%:(rulhand%)
  2374.     IF stat% < 0
  2375.         RETURN stat%
  2376.     ENDIF
  2377.  
  2378.     Gbool = NOT Gbool
  2379.     RETURN stat%
  2380. ENDP
  2381.  
  2382.  
  2383. REM  Read WEEKDAY(day)
  2384. REM  Expect '(' <value> ')'
  2385. REM  Set Gbool to true if Gabsdat is on weekday day (0 = sunday)
  2386. PROC opwd%:(rulhand%)
  2387.     LOCAL   stat%
  2388.     LOCAL   day%
  2389.  
  2390.     stat% = expect%:(rulhand%, "OPWD", "char", "(")
  2391.     IF stat% < 0
  2392.         RETURN stat%
  2393.     ENDIF
  2394.  
  2395.     stat% = expectr%:(rulhand%, "OPWD", "value", 0, 6)
  2396.     IF stat% < 0
  2397.         RETURN stat%
  2398.     ENDIF
  2399.     day% = Gtoken&
  2400.  
  2401.     stat% = expect%:(rulhand%, "OPWD", "char", ")")
  2402.     IF stat% < 0
  2403.         RETURN stat%
  2404.     ENDIF
  2405.  
  2406.     IF Gcalc
  2407.         Gbool = day% = dow%:(Gabsdat&(1))
  2408.     ENDIF
  2409.  
  2410.     RETURN stat%
  2411. ENDP
  2412.  
  2413.  
  2414. REM Open the agenda file as a binary file, positioned to the beginning
  2415. PROC openagn%:(pHand%, file$)
  2416.     LOCAL stat%
  2417.     LOCAL mode%
  2418.  
  2419.     REM     existing + binary + update + Random access
  2420.     mode% = $0000    + $0000  + $0100  + $0200
  2421.  
  2422.     stat% = IOOPEN(#pHand%, file$, mode%)
  2423.     IF stat% < 0
  2424.         RETURN abort%:(stat%, "opening "+file$)
  2425.     ENDIF
  2426.  
  2427.     RETURN stat%
  2428. ENDP
  2429.  
  2430. REM Open the rule file as a text file, positioned to the beginning
  2431. PROC openrul%:(pHand%, file$)
  2432.     LOCAL stat%
  2433.     LOCAL mode%
  2434.  
  2435.     REM     existing + text
  2436.     mode% = $0000    + $0020
  2437.  
  2438.     stat% = IOOPEN(#pHand%, file$, mode%)
  2439.     IF stat% < 0
  2440.         RETURN abort%:(stat%, "opening "+file$)
  2441.     ENDIF
  2442.     RETURN stat%
  2443. ENDP
  2444.  
  2445. REM Read the next row from handle% into global Grow$
  2446. PROC readrow%:(handle%)
  2447.  
  2448.     LOCAL   stat%
  2449.  
  2450.     stat% = IOREAD(handle%, UADD(ADDR(Grow$), 1), 255)
  2451.     IF (stat% < 0)
  2452.         IF (stat% <> -36)       REM end-of-file
  2453.             RETURN abort%:(stat%, "reading rule")
  2454.         ENDIF
  2455.         RETURN stat%
  2456.     ENDIF
  2457.  
  2458.     Growno% = Growno% + 1
  2459.     
  2460.     POKEB ADDR(Grow$),stat%
  2461.     RETURN stat%
  2462. ENDP
  2463.  
  2464. PROC rulclos%:
  2465.     LOCAL stat%, i%
  2466.  
  2467.     IF Grulhnd%<>0
  2468.         stat% = IOCLOSE(Grulhnd%)
  2469.         IF stat%<0
  2470.             abort%:(stat%, "closing")
  2471.         ENDIF
  2472.     ENDIF
  2473.  
  2474.     i% = 1
  2475.     WHILE i% <= Gholtot%
  2476.         FREEALLOC Gdaytxa%(i%)
  2477.         Gdaytxa%(i%) = 0
  2478.         i% = i% + 1
  2479.     ENDWH
  2480.     SETNAME "Hol"
  2481.     Grulhnd% = 0
  2482.     Gholidx% = 0
  2483.     Gholtot% = 0
  2484.     gCLS
  2485.     IF Gusrmod$<>""
  2486.         UNLOADM Gusrmod$
  2487.         Gusrmod$ = ""
  2488.     ENDIF
  2489.  
  2490.     RETURN 0
  2491. ENDP
  2492.  
  2493. PROC rulload%:(file$)
  2494.     LOCAL   stat%, winid%
  2495.  
  2496.     IF Gabshow
  2497.         REM show the about-window while we load the file
  2498.         winid% = abcre%:
  2499.     ENDIF
  2500.  
  2501.     Gcalc = 0
  2502.     rulclos%:
  2503.     
  2504.     Grulfil$ = file$
  2505.     stat% = openrul%:(ADDR(Grulhnd%), Grulfil$)
  2506.     IF stat%>=0
  2507.         REM read the file and put the holidays in the arrays
  2508.         REM Rewind
  2509.         stat% = rulrew:
  2510.         IF stat%>=0
  2511.             Gyear% = 1996
  2512.             stat% = holload%:(Grulhnd%, Gyear%)
  2513.  
  2514.             REM Update screen if we read anything
  2515.             IF Gholtot%>0
  2516.                 SETNAME Grulfil$
  2517.     
  2518.                 scupdate:(1, 1, Gscrows%, "gshowrow")
  2519.                 Gcurrow% = 1
  2520.                 sccursor:(1)
  2521.             ELSE
  2522.                 rulclos%:
  2523.                 nofile:
  2524.             ENDIF
  2525.         ENDIF
  2526.     ENDIF
  2527.     IF Gabshow
  2528.         REM once is enough
  2529.         Gabshow = 0
  2530.         gCLOSE winid%
  2531.     ENDIF
  2532.     Gcalc = -1
  2533.     
  2534.     RETURN stat%
  2535. ENDP
  2536.  
  2537. REM  Rewind the rule file and prepare globals
  2538. PROC rulrew:
  2539.     LOCAL   stat%
  2540.  
  2541.     Growno%  = 0
  2542.     Grow$    = ""
  2543.     Galinxt% = 1
  2544.     Gdyrsym% = 0
  2545.     Gdstyle% = 0
  2546.     Gdenttp% = 2
  2547.  
  2548.     REM "Rewind"
  2549.     stat% = IOCLOSE(Grulhnd%)
  2550.     IF stat% >= 0
  2551.         stat% = openrul%:(ADDR(Grulhnd%), Grulfil$)
  2552.     ENDIF
  2553.     IF stat% < 0
  2554.         RETURN abort%:(stat%, "rewinding "+Grulfil$)
  2555.     ENDIF
  2556.     RETURN stat%
  2557. ENDP
  2558.  
  2559. REM  Show or hide the cursor at the current text row
  2560. PROC sccursor:(onoff%)
  2561.     scgorow:(Gcurrow%)
  2562.     gSTYLE 0
  2563.     IF onoff%=1
  2564.         gPRINT CHR$($1c)
  2565.     ELSE
  2566.         gPRINTB " ",gTWIDTH(CHR$($1c))
  2567.     ENDIF
  2568. ENDP
  2569.  
  2570. REM  Set current screen position to text row rowno% (1-n)
  2571. PROC scgorow:(rowno%)
  2572.     gAT 0,Gscrowh%*rowno%
  2573. ENDP
  2574.  
  2575. REM  Move the current index in the arrays to pos idx%
  2576. REM  and update the screen accordingly.
  2577. PROC sccurmov:(idxp%)
  2578.     LOCAL   dist%           REM distance to move
  2579.     LOCAL   idx%
  2580.  
  2581.     idx% = idxp%
  2582.     
  2583.     REM anything on screen?
  2584.     IF Gholidx% = 0
  2585.         RETURN
  2586.     ENDIF
  2587.  
  2588.     REM Don't move outside the array
  2589.     IF idx% < 1
  2590.         idx% = 1
  2591.     ENDIF
  2592.     IF idx% > Gholtot%
  2593.         idx% = Gholtot%
  2594.     ENDIF
  2595.  
  2596.     sccursor:(0)
  2597.     dist% = idx% - Gholidx%
  2598.  
  2599.     REM Is the destination already on screen?
  2600.     IF ((Gcurrow% + dist%) >= 1) AND ((Gcurrow% + dist%) <= Gscrows%)
  2601.         REM yes, just move the cursor to that position
  2602.         Gcurrow% = Gcurrow% + dist%
  2603.         Gholidx% = idx%
  2604.  
  2605.     REM Can we scroll the screen into position?
  2606.     ELSEIF (dist%=1) AND (Gcurrow%=Gscrows%)
  2607.         gSCROLL 0,Gscrowh% * -dist%
  2608.         Gholidx% = Gholidx% + dist%
  2609.         scupdate:(Gcurrow%, Gholidx%, 1, "gshowrow")
  2610.  
  2611.     ELSEIF (dist%=-1) AND (Gcurrow%=1)
  2612.         gSCROLL 0,Gscrowh% * -dist%
  2613.         Gholidx% = Gholidx% + dist%
  2614.         scupdate:(Gcurrow%, Gholidx%, 1, "gshowrow")
  2615.  
  2616.     REM Erase and redraw screen
  2617.     ELSE
  2618.         gUPDATE OFF
  2619.         gCLS
  2620.         Gholidx% = idx%
  2621.         IF dist%<0              REM put new position at top of screen
  2622.             Gcurrow% = 1
  2623.             scupdate:(1, Gholidx%, Gscrows%, "gshowrow")
  2624.         ELSE                    REM put new position at end of screen
  2625.             Gcurrow% = Gscrows%
  2626.             scupdate:(1, Gholidx%-Gscrows%+1, Gscrows%, "gshowrow")
  2627.         ENDIF
  2628.         gUPDATE ON
  2629.     ENDIF
  2630.     sccursor:(1)
  2631.     
  2632. ENDP
  2633.  
  2634. REM  Redraw the list of holidays
  2635. PROC scredraw:(cls%)
  2636.     LOCAL   topidx%                 REM idx of line on top screen row
  2637.     topidx% = Gholidx% - Gcurrow% + 1
  2638.  
  2639.     IF cls%<>0
  2640.         gCLS
  2641.     ENDIF
  2642.     scupdate:(1, topidx%, Gscrows%, "gshowrow")
  2643.     sccursor:(1)
  2644. ENDP
  2645.  
  2646. REM  Print some of the lines on the screen
  2647. REM  scrow%             first text row to print on
  2648. REM  idx%               first array element to print
  2649. REM  count%             number of rows to print
  2650. REM  prtrow$    name of function to call to print one row
  2651. PROC scupdate:(scrow%, idx%, count%, prtrow$)
  2652.     LOCAL   idx1%           REM first element
  2653.     LOCAL   idx2%           REM last element
  2654.     LOCAL   i%
  2655.     LOCAL   r%
  2656.  
  2657.     REM Get first element to print
  2658.     idx1% = idx%
  2659.     IF idx% < 1
  2660.         idx1% = 1
  2661.     ENDIF
  2662.  
  2663.     REM Get last element to print
  2664.     idx2% = idx% + count% - 1
  2665.     IF idx2% > Gholtot%
  2666.         idx2% = Gholtot%
  2667.     ENDIF
  2668.  
  2669.     REM Don't print outside of window
  2670.     IF scrow%+count%-1 > Gscrows%
  2671.         idx2% = idx1% + (Gscrows% - scrow% + 1)
  2672.     ENDIF
  2673.  
  2674.     i% = idx1%
  2675.     r% = scrow%
  2676.     WHILE i% <= idx2%
  2677.         scgorow:(r%)
  2678.         @(prtrow$):(i%)
  2679.         i% = i% + 1
  2680.         r% = r% + 1
  2681.     ENDWH
  2682.  
  2683. ENDP
  2684.  
  2685. REM  set current font to number id%. Update globals
  2686. PROC setfont%:(id%)
  2687.     LOCAL   info%(32)
  2688.     LOCAL   oldfont%
  2689.  
  2690.     oldfont% = Gcurfnt%
  2691.     gFONT(id%)
  2692.     Gcurfnt% = id%
  2693.     
  2694.     gINFO info%()
  2695.     Gscrowh% = info%(3)+info%(4)    REM height + descent
  2696.     Gscrowd% = info%(4)
  2697.     Gscrows% = gHEIGHT / Gscrowh%
  2698.     Gscchrw% = info%(7)
  2699.     
  2700.     RETURN oldfont%
  2701. ENDP
  2702.  
  2703. PROC setstat:(type%)
  2704.     LOCAL   extent%(4)
  2705.     
  2706.     IF type% = 0
  2707.         STATUSWIN OFF
  2708.     ELSE
  2709.         STATUSWIN ON,type%
  2710.     ENDIF
  2711.     Gstatwn% = type%
  2712.     REM Adjust the main window size
  2713.     STATWININFO(-1, extent%())
  2714.     gSETWIN 0,0,extent%(1),Gscheit%
  2715.     SCREEN 20,15,1,1
  2716.     scredraw:(0)
  2717. ENDP
  2718.  
  2719. PROC gshowhol:(daynum&, yrsymb%, style%, daytxt$, reptyp%, repmon%, repday%, repnbr%, repsho%, enttyp%)
  2720.     LOCAL   yr%, mo%, dy%, hr%, mn%, sc%, yrday%, info%(32)
  2721.     LOCAL   mo$(2), dy$(2), nbr$(4), datwid%, agnfont%, oldfont%, y%
  2722.  
  2723.     REM Don't show ignored entries if Gshoign% is false
  2724.     IF enttyp% = 0 AND NOT Gshoign%
  2725.         RETURN 0
  2726.     ENDIF
  2727.     
  2728.     gNL:
  2729.     gSTYLE 0
  2730.  
  2731.     datwid% = gTWIDTH("9999"+Gdtsep$+"99"+Gdtsep$+"99")
  2732.  
  2733.     SECSTODATE daynum&*24*60*60, yr%, mo%, dy%, hr%, mn%, sc%, yrday%
  2734.     mo$ = RIGHT$("00"+NUM$(mo%,2), 2)
  2735.     dy$ = RIGHT$("00"+NUM$(dy%,2), 2)
  2736.     IF Gdtfmt% = 0        REM MDY
  2737.         gPRINT mo$;Gdtsep$;dy$;Gdtsep$;yr%
  2738.     ELSEIF Gdtfmt% = 1    REM DMY
  2739.         gPRINT dy$;Gdtsep$;mo$;Gdtsep$;yr%
  2740.     ELSE                REM YMD
  2741.         gPRINT yr%;Gdtsep$;mo$;Gdtsep$;dy$
  2742.     ENDIF
  2743.     IF reptyp%<>1
  2744.         gAT datwid%,gY
  2745.         gPRINT CHR$(175)
  2746.     ENDIF
  2747.  
  2748.     gAT datwid%+gTWIDTH("M"),gY
  2749.  
  2750.     IF yrsymb% >= 32
  2751.         gXPRINT CHR$(yrsymb%),1
  2752.     ENDIF
  2753.  
  2754.     IF Genttyp% = 3
  2755.         gAT gX+gTWIDTH("M"),gY
  2756.         REM TBD "should be loaded only once"
  2757.         agnfont% = gLOADFONT("ROM::AGN8SYM")
  2758.         oldfont% = setfont%:(agnfont%)
  2759.         gPRINT(CHR$(4)) REM candle
  2760.         setfont%:(oldfont%)
  2761.         gUNLOADFONT agnfont%
  2762.     ENDIF
  2763.     
  2764.     gAT datwid%+gTWIDTH("MMM"),gY
  2765.     gSTYLE style%
  2766.     gPRINT daytxt$
  2767.  
  2768.     REM Strikeout ignored entry
  2769.     IF enttyp% = 0
  2770.         gINFO info%()
  2771.         y% = gY
  2772.         gAT gX,gY-info%(3)/2
  2773.         gLINETO 0,gY
  2774.         gAT 0,y%
  2775.     ENDIF
  2776.  
  2777.     IF reptyp% <> 1
  2778.         gNL:
  2779.         gSTYLE 0
  2780.         IF Gscreen% = 1
  2781.             gAT datwid%+gTWIDTH("MMM"),gY
  2782.         ELSE
  2783.             gAT gTWIDTH("9"),gY
  2784.         ENDIF
  2785.         IF reptyp%=2
  2786.             IF Gscreen% = 1
  2787.                 gPRINT "repeating yearly on ";MONTH$(repmon%);" ";repday%
  2788.             ELSE
  2789.                 gPRINT "rpt yearly on ";MONTH$(repmon%);" ";repday%
  2790.             ENDIF
  2791.         ELSEIF reptyp%=3
  2792.             dy% = repday%
  2793.             IF dy% = 0
  2794.                 dy% = 7
  2795.             ENDIF
  2796.             IF repnbr% = 1
  2797.                 nbr$ = "1st"
  2798.             ELSEIF repnbr% = 2
  2799.                 nbr$ = "2nd"
  2800.             ELSEIF repnbr% = 3
  2801.                 nbr$ = "3rd"
  2802.             ELSEIF repnbr% = 4
  2803.                 nbr$ = "4th"
  2804.             ELSE
  2805.                 nbr$ = "last"
  2806.             ENDIF
  2807.             IF Gscreen% = 1
  2808.                 gPRINT "repeating yearly on ";nbr$;" ";DAYNAME$(dy%);" in ";MONTH$(repmon%);
  2809.             ELSE
  2810.                 gPRINT "rpt yearly on ";nbr$;" ";DAYNAME$(dy%);" in ";MONTH$(repmon%);
  2811.             ENDIF
  2812.         ENDIF
  2813.         IF repsho%=1
  2814.             IF Gscreen% = 1
  2815.                 gPRINT ", all entries shown"
  2816.             ELSE
  2817.                 gPRINT ", all shown"
  2818.             ENDIF
  2819.         ELSE
  2820.             IF Gscreen% = 1
  2821.                 gPRINT ", next entry shown"
  2822.             ELSE
  2823.                 gPRINT ", next shown"
  2824.             ENDIF
  2825.         ENDIF
  2826.     ENDIF
  2827. ENDP
  2828.  
  2829. PROC gshowrow:(idx%)
  2830.  
  2831.     gSTYLE 0
  2832.  
  2833.     REM Make room for the cursor
  2834.     gAT Gscchrw%,gY
  2835.  
  2836.     REM print selection marker
  2837.     IF Gselcta(idx%)
  2838.         gPRINT "*"
  2839.     ELSE
  2840.         gPRINTB " ",gTWIDTH("*")
  2841.     ENDIF
  2842.     gAT Gscchrw%*3,gY
  2843.     
  2844.     IF Gyrsyma%(idx%) >= 32
  2845.         gXPRINT CHR$(Gyrsyma%(idx%)),1
  2846.     ENDIF
  2847.  
  2848.     gAT 4.5*Gscchrw%,gY
  2849.     
  2850.     gSTYLE Gstylea%(idx%)
  2851.     gPRINT PEEK$(Gdaytxa%(idx%))
  2852.  
  2853.     gSTYLE 0
  2854. ENDP
  2855.  
  2856. PROC showhelp:(fname$)
  2857.     LOCAL gate%,buf%(65)
  2858.     LOCAL page&,base&
  2859.     
  2860.     page&=1 :base&=1
  2861.     
  2862.     REM Convert filename to "C" string:
  2863.     POKE$ ADDR(buf%(1)),"#"+fname$
  2864.     
  2865.     gate%=PEEKW($38)
  2866.     SEND(gate%,27,buf%(2)) :REM Set help filename
  2867.     SEND(gate%,26,#page&,#base&) :REM Do help
  2868. ENDP
  2869.  
  2870. REM Move past all blank/tabs in Grow, reading a new line if necessary
  2871. PROC skipspc%:(rulhand%)
  2872.  
  2873.     LOCAL   stat%
  2874.  
  2875.     stat% = 0
  2876.     WHILE isblank:(Grow$)
  2877.         IF LEN(Grow$) = 0               REM empty string
  2878.             stat% = readrow%:(rulhand%)
  2879.             IF stat% < 0
  2880.                 RETURN stat%
  2881.             ENDIF
  2882.         ELSE
  2883.             Grow$ = RIGHT$(Grow$, LEN(Grow$)-1)
  2884.         ENDIF
  2885.     ENDWH
  2886.  
  2887.     RETURN stat%
  2888. ENDP
  2889.  
  2890. PROC skipto%:(rulhand%, txt$, silent%)
  2891.     LOCAL   stat%
  2892.  
  2893.     IF NOT silent%
  2894.         gIPRINT "Skipping to "+txt$
  2895.     ENDIF
  2896.     DO
  2897.         stat% = gettok%:(rulhand%)
  2898.     UNTIL Gtoken$=txt$ OR stat%<0
  2899.     RETURN stat%
  2900. ENDP
  2901.  
  2902. PROC synterr%:(msg$)
  2903.     ALERT("Syntax error at line "+GEN$(Growno%, 6), msg$, "OK")
  2904.     RETURN -1
  2905. ENDP
  2906.  
  2907. PROC zoom:(step%):
  2908.     LOCAL   topidx%
  2909.  
  2910.     REM anything to zoom?
  2911.     IF Gholidx%<>0
  2912.         Gcurfnt% = Gcurfnt% + step%
  2913.         IF Gcurfnt% < 9
  2914.             Gcurfnt% = 12
  2915.         ENDIF
  2916.         IF Gcurfnt% > 12
  2917.             Gcurfnt% = 9
  2918.         ENDIF
  2919.     
  2920.         topidx% = Gholidx% - Gcurrow% + 1
  2921.         setfont%:(Gcurfnt%)
  2922.         IF Gcurrow% > Gscrows%
  2923.             topidx% = topidx% + (Gcurrow% - Gscrows%)
  2924.             Gcurrow% = Gscrows%
  2925.         ENDIF
  2926.         gCLS
  2927.         scupdate:(1, topidx%, Gscrows%, "gshowrow")
  2928.         sccursor:(1)
  2929.     ENDIF
  2930.     RETURN 0
  2931. ENDP
  2932.  
  2933. REM =======================================================================================
  2934. REM  Special date routines
  2935. REM =======================================================================================
  2936.  
  2937. PROC mod&:(a&,b&)
  2938.     LOCAL r&
  2939.     r& = a&-INT(a&/b&)*b&
  2940.     RETURN r&
  2941. ENDP
  2942.  
  2943. PROC mod%:(a%,b%)
  2944.     LOCAL r%
  2945.     r% = a%-INT(a%/b%)*b%
  2946.     RETURN r%
  2947. ENDP
  2948.  
  2949. REM ===========================================================================
  2950. REM
  2951. REM  The functions in this section are derived from functions in calendar.el in emacs.
  2952. REM  Calendar.el is Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994 Free Software
  2953. REM  Foundation, Inc.
  2954. REM  Author: Edward M. Reingold <reingold@cs.uiuc.edu>
  2955. REM
  2956. REM ===========================================================================
  2957.  
  2958. REM
  2959. REM (defun calendar-leap-year-p (year)
  2960. REM   "Returns t if YEAR is a Gregorian leap year."
  2961. REM   (or
  2962. REM     (and (=  (% year   4) 0)
  2963. REM          (/= (% year 100) 0))
  2964. REM     (= (% year 400) 0)))
  2965. REM 
  2966. PROC isleap:(y&)
  2967.     RETURN (mod&:(y&,INT(4))=0 AND mod&:(y&,INT(100))<>0) OR (mod&:(y&,INT(400))=0)
  2968. ENDP
  2969.  
  2970.  
  2971. REM ***************************************************************************
  2972. REM (defun calendar-last-day-of-month (month year)
  2973. REM   "The last day in MONTH during YEAR."
  2974. REM   (if (and (= month 2) (calendar-leap-year-p year))
  2975. REM      29
  2976. REM    (aref [31 28 31 30 31 30 31 31 30 31 30 31] (1- month))))
  2977. REM
  2978. PROC ldom&:(m&, y&)
  2979.     IF isleap:(y&) AND m&=2
  2980.         RETURN 29
  2981.     ELSEIF m&=2
  2982.         RETURN 28
  2983.     ELSEIF m&=1 OR m&=3 OR m&=5 OR m&=7 OR m&=8 OR m&=10 OR m&=12
  2984.         RETURN 31
  2985.     ELSE
  2986.         RETURN 30
  2987.     ENDIF
  2988. ENDP
  2989.  
  2990.  
  2991. REM ****************************************************************************
  2992. REM (defun calendar-day-number (date)
  2993. REM   "Return the day number within the year of the date DATE.
  2994. REM For example, (calendar-day-number '(1 1 1987)) returns the value 1,
  2995. REM while (calendar-day-number '(12 31 1980)) returns 366."
  2996. REM     (let* ((month (extract-calendar-month date))
  2997. REM            (day (extract-calendar-day date))
  2998. REM            (year (extract-calendar-year date))
  2999. REM          (day-of-year (+ day (* 31 (1- month)))))
  3000. REM       (if (> month 2)
  3001. REM           (progn
  3002. REM             (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10)))
  3003. REM             (if (calendar-leap-year-p year)
  3004. REM                 (setq day-of-year (1+ day-of-year)))))
  3005. REM       day-of-year))
  3006. REM 
  3007. PROC dnum&:(d&, m&, y&)
  3008.     LOCAL doy&  REM day-of-year
  3009.     doy& = d& + 31 * (m& - 1)
  3010.     IF m&>2
  3011.         doy& = doy& - (23 + 4*m&) / 10
  3012.         IF isleap:(y&)
  3013.             doy& = doy& + 1
  3014.         ENDIF
  3015.     ENDIF
  3016.     RETURN doy&
  3017. ENDP
  3018.  
  3019.  
  3020. REM ****************************************************************************
  3021. REM (defun calendar-absolute-from-gregorian (date)
  3022. REM   "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE.
  3023. REM The Gregorian date Sunday, December 31, 1 BC is imaginary."
  3024. REM   (let ((prior-years (1- (extract-calendar-year date))))
  3025. REM     (+ (calendar-day-number date);; Days this year
  3026. REM        (* 365 prior-years);;        + Days in prior years
  3027. REM        (/ prior-years 4);;          + Julian leap years
  3028. REM        (- (/ prior-years 100));;    - century years
  3029. REM        (/ prior-years 400))));;     + Gregorian leap years
  3030. REM 
  3031. PROC absgreg&:(d&, m&, y&)
  3032.     LOCAL   prioryr&        REM prior-years
  3033.     LOCAL   a&
  3034. REM print "absgreg"
  3035.     prioryr& = y& - 1
  3036.     a& = dnum&:(d&, m&, y&)         REM days this year
  3037.     a& = a& + 365*prioryr&          REM + days in prior years
  3038.     a& = a& + prioryr& / 4          REM + Julian leap years
  3039.     a& = a& - prioryr& / 100        REM - century years
  3040.     a& = a& + prioryr& / 400        REM + Gregorian leap years
  3041.     RETURN a&
  3042. ENDP
  3043.  
  3044.  
  3045. REM ****************************************************************************
  3046. REM (defun calendar-gregorian-from-absolute (date)
  3047. REM   "Compute the list (month day year) corresponding to the absolute DATE.
  3048. REM The absolute date is the number of days elapsed since the (imaginary)
  3049. REM Gregorian date Sunday, December 31, 1 BC."
  3050. REM ;; See the footnote on page 384 of ``Calendrical Calculations, Part II:
  3051. REM ;; Three Historical Calendars'' by E. M. Reingold,  N. Dershowitz, and S. M.
  3052. REM ;; Clamen, Software--Practice and Experience, Volume 23, Number 4
  3053. REM ;; (April, 1993), pages 383-404 for an explanation.
  3054. REM   (let* ((d0 (1- date))
  3055. REM          (n400 (/ d0 146097))
  3056. REM          (d1 (% d0 146097))
  3057. REM          (n100 (/ d1 36524))
  3058. REM          (d2 (% d1 36524))
  3059. REM          (n4 (/ d2 1461))
  3060. REM          (d3 (% d2 1461))
  3061. REM          (n1 (/ d3 365))
  3062. REM          (day (1+ (% d3 365)))
  3063. REM          (year (+ (* 400 n400) (* 100 n100) (* n4 4) n1)))
  3064. REM     (if (or (= n100 4) (= n1 4))
  3065. REM         (list 12 31 year)
  3066. REM       (let ((year (1+ year))
  3067. REM             (month 1))
  3068. REM         (while (let ((mdays (calendar-last-day-of-month month year)))
  3069. REM                  (and (< mdays day)
  3070. REM                       (setq day (- day mdays))))
  3071. REM           (setq month (1+ month)))
  3072. REM         (list month day year)))))
  3073. REM
  3074.  
  3075. REM  daynum-from-absolute
  3076. PROC dnumabs&:(date&)
  3077.     LOCAL d0&,n400&,d1&,n100&,d2&,n4&,d3&,n1&,day&,year&
  3078.     LOCAL month&, mdays&
  3079.     LOCAL y%,m%,d%
  3080.  
  3081. REM print "dnumabs:",date&
  3082.     d0&     = date& - 1
  3083.     n400&   = d0& / 146097
  3084.     d1&             = mod&:(d0&, INT(146097))
  3085.     n100&   = d1& / 36524
  3086.     d2&             = mod&:(d1&, INT(36524))
  3087.     n4&             = d2& / 1461
  3088.     d3&             = mod&:(d2&, INT(1461))
  3089.     n1&             = d3& / 365
  3090.     day&    = 1 + mod&:(d3&, INT(365))
  3091.     year&   = 400*n400& + 100*n100& + n4&*4 + n1&
  3092.  
  3093.     IF n100&=4 OR n1&=4
  3094.         day& = 31
  3095.         month& = 12
  3096.     ELSE
  3097.         year& = year& + 1
  3098.         month& = 1
  3099.         WHILE 1
  3100.             mdays& = ldom&:(month&, year&)  
  3101.             IF mdays&<day&
  3102.                 day& = day& - mdays&
  3103.                 IF day&<>0
  3104.                     month& = month&+1
  3105.                     CONTINUE
  3106.                 ENDIF
  3107.             ENDIF
  3108.             BREAK
  3109.         ENDWH
  3110.     ENDIF
  3111.  
  3112.     y% = year& : m% = month& : d% = day&
  3113. REM print "y%=";y%
  3114. REM print "m%=";m%
  3115. REM print "d%=";d%
  3116.     RETURN dt2dnm&:(y%, m%, d%)
  3117. ENDP
  3118.     
  3119. REM ****************************************************************************
  3120. REM (defun calendar-dayname-on-or-before (dayname date)
  3121. REM   "Returns the absolute date of the DAYNAME on or before absolute DATE.
  3122. REM DAYNAME=0 means Sunday, DAYNAME=1 means Monday, and so on.
  3123. REM 
  3124. REM Note: Applying this function to d+6 gives us the DAYNAME on or after an
  3125. REM absolute day d.  Similarly, applying it to d+3 gives the DAYNAME nearest to
  3126. REM absolute date d, applying it to d-1 gives the DAYNAME previous to absolute
  3127. REM date d, and applying it to d+7 gives the DAYNAME following absolute date d."
  3128. REM   (- date (% (- date dayname) 7)))
  3129. REM 
  3130. PROC doob&:(dayname&, date&)
  3131.     LOCAL r&
  3132. REM print "doob:",dayname&,date&
  3133.     r& = date& - mod&:(date&-dayname&, INT(7))
  3134.     RETURN r&
  3135. ENDP
  3136.  
  3137.  
  3138. REM ****************************************************************************
  3139. REM (defun calendar-day-of-week (date)
  3140. REM  "Returns the day-of-the-week index of DATE, 0 for Sunday, 1 for Monday, etc."
  3141. REM  (% (calendar-absolute-from-gregorian date) 7))
  3142. PROC dow%:(absdat&)
  3143.     RETURN mod&:(absdat&, INT(7))
  3144. ENDP
  3145.  
  3146.  
  3147. REM ****************************************************************************
  3148. REM (defun calendar-nth-named-absday (n dayname month year &optional day)
  3149. REM   "The absolute date of Nth DAYNAME in MONTH, YEAR before/after optional DAY.
  3150. REM A DAYNAME of 0 means Sunday, 1 means Monday, and so on.  If N<0,
  3151. REM return the Nth DAYNAME before MONTH DAY, YEAR (inclusive).
  3152. REM If N>0, return the Nth DAYNAME after MONTH DAY, YEAR (inclusive).
  3153. REM 
  3154. REM If DAY is omitted, it defaults to 1 if N>0, and MONTH's last day otherwise."
  3155. REM   (if (> n 0)
  3156. REM       (+ (* 7 (1- n))
  3157. REM          (calendar-dayname-on-or-before
  3158. REM                     dayname
  3159. REM                     (+ 6 (calendar-absolute-from-gregorian
  3160. REM                                     (list month (or day 1) year)))))
  3161. REM     (+ (* 7 (1+ n))
  3162. REM        (calendar-dayname-on-or-before
  3163. REM             dayname
  3164. REM             (calendar-absolute-from-gregorian
  3165. REM             (list month
  3166. REM            (or day (calendar-last-day-of-month month year))
  3167. REM            year))))))
  3168. REM 
  3169. PROC nthabs&:(n&, dayname&, month&, year&, d&)
  3170.     LOCAL day&
  3171.  
  3172.     day& = d&
  3173.     IF n&>0
  3174.         IF day& = 0 
  3175.             day& = 1
  3176.         ENDIF
  3177.         RETURN 7*(n&-1)+doob&:(dayname&, 6+absgreg&:(day&, month&, year&))
  3178.     ELSE
  3179.         IF day& = 0
  3180.             day& = ldom&:(month&, year&)
  3181.         ENDIF
  3182.         RETURN 7*(n&+1)+doob&:(dayname&, absgreg&:(day&, month&, year&))
  3183.     ENDIF
  3184. ENDP
  3185.  
  3186.  
  3187. REM ****************************************************************************
  3188. REM (defun calendar-nth-named-day (n dayname month year &optional day)
  3189. REM   "The date of Nth DAYNAME in MONTH, YEAR before/after optional DAY.
  3190. REM A DAYNAME of 0 means Sunday, 1 means Monday, and so on.  If N<0,
  3191. REM return the Nth DAYNAME before MONTH DAY, YEAR (inclusive).
  3192. REM If N>0, return the Nth DAYNAME after MONTH DAY, YEAR (inclusive).
  3193. REM 
  3194. REM If DAY is omitted, it defaults to 1 if N>0, and MONTH's last day otherwise."
  3195. REM   (calendar-gregorian-from-absolute
  3196. REM    (calendar-nth-named-absday n dayname month year day)))
  3197. REM 
  3198. PROC nthdnum&:(n&, dayname&, month&, year&, d&)
  3199. REM print "nthdnum(",n&, dayname&, month&, year&, d&,")"
  3200.     RETURN dnumabs&:(nthabs&:(n&, dayname&, month&, year&, d&))
  3201. ENDP
  3202.  
  3203. REM ****************************************************************************
  3204. REM (defun hebrew-calendar-leap-year-p (year)
  3205. REM  "t if YEAR is a Hebrew calendar leap year."
  3206. REM   (< (% (1+ (* 7 year)) 19) 7))
  3207.  
  3208. PROC hclyp:(year&)
  3209.     RETURN mod&:(INT((1 + (7*year&))), INT(19)) < 7
  3210. ENDP
  3211.  
  3212.  
  3213. REM ****************************************************************************
  3214. REM (defun hebrew-calendar-elapsed-days (year)
  3215. REM   "Days from Sun. prior to start of Hebrew calendar to mean conjunction of Tishri of Hebrew YEAR."
  3216. REM   (let* ((months-elapsed
  3217. REM           (+ (* 235 (/ (1- year) 19));; Months in complete cycles so far.
  3218. REM              (* 12 (% (1- year) 19))      ;; Regular months in this cycle
  3219. REM              (/ (1+ (* 7 (% (1- year) 19))) 19)));; Leap months this cycle
  3220. REM          (parts-elapsed (+ 204 (* 793 (% months-elapsed 1080))))
  3221. REM          (hours-elapsed (+ 5
  3222. REM                            (* 12 months-elapsed)
  3223. REM                            (* 793 (/ months-elapsed 1080))
  3224. REM                            (/ parts-elapsed 1080)))
  3225. REM          (parts                                  ;; Conjunction parts
  3226. REM           (+ (* 1080 (% hours-elapsed 24)) (% parts-elapsed 1080)))
  3227. REM          (day                                    ;; Conjunction day
  3228. REM           (+ 1 (* 29 months-elapsed) (/ hours-elapsed 24)))
  3229. REM          (alternative-day
  3230. REM           (if (or (>= parts 19440)    ;; If the new moon is at or after midday,
  3231. REM                   (and (= (% day 7) 2);; ...or is on a Tuesday...
  3232. REM                        (>= parts 9924)  ;;    at 9 hours, 204 parts or later...
  3233. REM                        (not (hebrew-calendar-leap-year-p year)));; of a
  3234. REM                                                                 ;; common year,
  3235. REM                   (and (= (% day 7) 1);; ...or is on a Monday...
  3236. REM                        (>= parts 16789) ;;   at 15 hours, 589 parts or later...
  3237. REM                        (hebrew-calendar-leap-year-p (1- year))));; at the end
  3238. REM                                                      ;; of a leap year
  3239. REM        ;; Then postpone Rosh HaShanah one day
  3240. REM              (1+ day)
  3241. REM        ;; Else
  3242. REM             day)))
  3243. REM    (if ;; If Rosh HaShanah would occur on Sunday, Wednesday, or Friday
  3244. REM        (memq (% alternative-day 7) (list 0 3 5))
  3245. REM   ;; Then postpone it one (more) day and return        
  3246. REM         (1+ alternative-day)
  3247. REM   ;; Else return        
  3248. REM       alternative-day)))
  3249.  
  3250. PROC hced&:(year&)
  3251.     LOCAL   monthse&, partse&, hourse&, parts&, day&, altday&, d&
  3252.  
  3253.     REM  Months in complete cycles so far.
  3254.     monthse& = 235 * INT((year& - 1) / 19)
  3255.     REM  Regular months in this cycle
  3256.     monthse& = monthse& + 12 * mod&:((year& - 1), INT(19))
  3257.     REM  Leap months this cycle
  3258.     monthse& = monthse& + ((1 + (7 * mod&:((year& - 1), INT(19)))) / 19)
  3259.  
  3260.     partse& = 204 + 793 * mod&:(monthse&, INT(1080))
  3261.  
  3262.     hourse& = 5 + 12*monthse& + 793*INT(monthse& / 1080) + INT(partse& / 1080)
  3263.  
  3264.     REM  Conjuction parts
  3265.     parts& = 1080 * mod&:(hourse&, INT(24)) + mod&:(partse&, INT(1080))
  3266.  
  3267.     REM  Conjuction day
  3268.     day& = 1 + 29*monthse& + hourse&/24
  3269.  
  3270.     IF parts& >= 19440                      REM  If the new moon is at or after midday,
  3271.         altday& = day& + 1              REM  Then postpone Rosh HaShanah one day
  3272.     ELSEIF mod&:(day&, INT(7))=2 AND parts&>=9924 AND NOT hclyp:(year&)  REM phew
  3273.         altday& = day& + 1              REM  Then postpone Rosh HaShanah one day        
  3274.     ELSEIF mod&:(day&, INT(7))=1 AND parts&>=16789 AND hclyp:(year& - 1)  REM phew
  3275.         altday& = day& + 1              REM  Then postpone Rosh HaShanah one day
  3276.     ELSE
  3277.         altday& = day&
  3278.     ENDIF
  3279.  
  3280.     REM  If Rosh HaShanah would occur on Sunday, Wednesday, or Friday
  3281.     d& = mod&:(altday&, INT(7))
  3282.     IF d&=0 OR d&=3 OR d&=5
  3283.         REM  Then postpone it one (more) day and return 
  3284.         RETURN altday& + 1
  3285.     ENDIF
  3286.     RETURN altday&
  3287.         
  3288. ENDP
  3289.  
  3290. REM ****************************************************************************
  3291. REM (defun hebrew-calendar-days-in-year (year)
  3292. REM   "Number of days in Hebrew YEAR."
  3293. REM   (- (hebrew-calendar-elapsed-days (1+ year))
  3294. REM      (hebrew-calendar-elapsed-days year)))
  3295. PROC hcdiy&:(year&)
  3296.     RETURN hced&:(year&+1) - hced&:(year&)
  3297. ENDP
  3298.  
  3299. REM ****************************************************************************
  3300. REM (defun hebrew-calendar-short-kislev-p (year)
  3301. REM   "t if Kislev is short in Hebrew YEAR."
  3302. REM   (= (% (hebrew-calendar-days-in-year year) 10) 3))
  3303. PROC hcskp:(year&)
  3304.     RETURN mod&:(hcdiy&:(year&), INT(10)) = 3
  3305. ENDP
  3306.  
  3307. REM ***************************************************************************
  3308. REM (defun hebrew-calendar-long-heshvan-p (year)
  3309. REM   "t if Heshvan is long in Hebrew YEAR."
  3310. REM   (= (% (hebrew-calendar-days-in-year year) 10) 5))
  3311. PROC hclhp:(year&)
  3312.     RETURN mod&:(hcdiy&:(year&), INT(10)) = 5
  3313. ENDP
  3314.  
  3315. REM ***************************************************************************
  3316. REM (defun hebrew-calendar-last-day-of-month (month year)
  3317. REM   "The last day of MONTH in YEAR."
  3318. REM   (if (or (memq month (list 2 4 6 10 13))
  3319. REM           (and (= month 12) (not (hebrew-calendar-leap-year-p year)))
  3320. REM           (and (= month 8) (not (hebrew-calendar-long-heshvan-p year)))
  3321. REM           (and (= month 9) (hebrew-calendar-short-kislev-p year)))
  3322. REM       29
  3323. REM     30))
  3324. PROC hcldom&:(month&, year&)
  3325.     IF month&=2 OR month&=4 OR month&=6 OR month&=10 OR month&=13
  3326.         RETURN 29
  3327.     ELSEIF month&=12 AND NOT hclyp:(year&)
  3328.         RETURN 29
  3329.     ELSEIF month&=8 AND NOT hclhp:(year&)
  3330.         RETURN 29
  3331.     ELSEIF month&=9 AND hcskp:(year&)
  3332.         RETURN 29
  3333.     ELSE
  3334.         RETURN 30
  3335.     ENDIF
  3336. ENDP
  3337.  
  3338. REM ***************************************************************************
  3339. REM (defun hebrew-calendar-last-month-of-year (year)
  3340. REM   "The last month of the Hebrew calendar YEAR."
  3341. REM   (if (hebrew-calendar-leap-year-p year)
  3342. REM       13
  3343. REM     12))
  3344. PROC hclmoy&:(year&)
  3345.     IF hclyp:(year&)
  3346.         RETURN 13
  3347.     ENDIF
  3348.     RETURN 12
  3349. ENDP
  3350.  
  3351. REM ***************************************************************************
  3352. REM (defmacro calendar-sum (index initial condition expression)
  3353. REM  "For INDEX = INITIAL et seq, as long as CONDITION holds, sum EXPRESSION."
  3354. REM  (` (let (( (, index) (, initial))
  3355. REM             (sum 0))
  3356. REM       (while (, condition)
  3357. REM         (setq sum (+ sum (, expression) ))
  3358. REM         (setq (, index) (1+ (, index))))
  3359. REM       sum)))
  3360. REM 
  3361. REM index = initial
  3362. REM sum = 0
  3363. REM WHILE condition
  3364. REM     sum = sum + expression
  3365. REM     index = index + 1
  3366. REM ENDW
  3367. REM RETURN sum
  3368.  
  3369.  
  3370.  
  3371. REM ****************************************************************************
  3372. REM (defun calendar-absolute-from-hebrew (date)
  3373. REM   "Absolute date of Hebrew DATE.
  3374. REM The absolute date is the number of days elapsed since the (imaginary)
  3375. REM Gregorian date Sunday, December 31, 1 BC."
  3376. REM   (let* ((month (extract-calendar-month date))
  3377. REM          (day (extract-calendar-day date))
  3378. REM          (year (extract-calendar-year date)))
  3379. REM     (+ day                            ;; Days so far this month.
  3380. REM        (if (< month 7);; before Tishri
  3381. REM      ;; Then add days in prior months this year before and after Nisan
  3382. REM            (+ (calendar-sum
  3383. REM                m 7 (<= m (hebrew-calendar-last-month-of-year year))
  3384. REM                (hebrew-calendar-last-day-of-month m year))
  3385. REM               (calendar-sum
  3386. REM                m 1 (< m month)
  3387. REM                (hebrew-calendar-last-day-of-month m year)))
  3388. REM      ;; Else add days in prior months this year
  3389. REM          (calendar-sum
  3390. REM           m 7 (< m month)
  3391. REM           (hebrew-calendar-last-day-of-month m year)))
  3392. REM     (hebrew-calendar-elapsed-days year);; Days in prior years.
  3393. REM     -1373429)))                        ;; Days elapsed before absolute date 1.
  3394. REM
  3395. PROC absheb&:(day&, month&, year&)
  3396.     LOCAL   a&, sum&, m&
  3397.  
  3398.     a& = day&               REM  Days so far this month.
  3399.     IF month&<7             REM  before Tishri
  3400.                     REM  Then add days in prior months this year before and after Nisan
  3401.         m& = 7
  3402.         sum& = 0
  3403.         WHILE m&<=hclmoy&:(year&)
  3404.             sum& = sum& + hcldom&:(m&, year&)
  3405.             m& = m& + 1
  3406.         ENDWH
  3407.         a& = a& + sum&
  3408.  
  3409.         m& = 1
  3410.         sum& = 0
  3411.         WHILE m&<month&
  3412.             sum& = sum& + hcldom&:(m&, year&)
  3413.             m& = m& + 1
  3414.         ENDWH
  3415.         a& = a& + sum&
  3416.     ELSE                    REM  Else add days in prior months this year
  3417.         m& = 7
  3418.         sum& = 0
  3419.         WHILE m&<month&
  3420.             sum& = sum& + hcldom&:(m&, year&)
  3421.             m& = m& + 1
  3422.         ENDWH
  3423.         a& = a& + sum&
  3424.     ENDIF
  3425.  
  3426.     a& = a& + hced&:(year&) REM  Days in prior years.
  3427.     a& = a& - 1373429               REM  Days elapsed before absolute date 1.
  3428.     RETURN a&
  3429. ENDP
  3430.  
  3431.  
  3432. REM ***************************************************************************
  3433. REM (defun calendar-hebrew-from-absolute (date)
  3434. REM   "Compute the Hebrew date (month day year) corresponding to absolute DATE.
  3435. REM The absolute date is the number of days elapsed since the (imaginary)
  3436. REM Gregorian date Sunday, December 31, 1 BC."
  3437. REM   (let* ((greg-date (calendar-gregorian-from-absolute date))
  3438. REM          (month (aref [9 10 11 12 1 2 3 4 7 7 7 8]
  3439. REM                  (1- (extract-calendar-month greg-date))))
  3440. REM          (day)
  3441. REM          (year (+ 3760 (extract-calendar-year greg-date))))
  3442. REM     (while (>= date (calendar-absolute-from-hebrew (list 7 1 (1+ year))))
  3443. REM         (setq year (1+ year)))
  3444. REM     (let ((length (hebrew-calendar-last-month-of-year year)))
  3445. REM       (while (> date
  3446. REM                 (calendar-absolute-from-hebrew
  3447. REM                  (list month
  3448. REM                        (hebrew-calendar-last-day-of-month month year)
  3449. REM                        year)))
  3450. REM         (setq month (1+ (% month length)))))
  3451. REM     (setq day (1+
  3452. REM                (- date (calendar-absolute-from-hebrew (list month 1 year)))))
  3453. REM     (list month day year)))
  3454. PROC hebabs$:(date&)
  3455.     LOCAL   month&, day&, year&, length&
  3456.     LOCAL   yr%, mo%, dy%, hr%, mn%, sc%, yrday%
  3457.     LOCAL   mo$(2), dy$(2), heb$(8)
  3458.  
  3459.     REM Get month and year from the absolute date
  3460.     SECSTODATE dnumabs&:(date&)*24*60*60, yr%, mo%, dy%, hr%, mn%, sc%, yrday%
  3461.     IF mo% = 1
  3462.         month& = 9
  3463.     ELSEIF mo% = 2
  3464.         month& = 10
  3465.     ELSEIF mo% = 3
  3466.         month& = 11
  3467.     ELSEIF mo% = 4
  3468.         month& = 12
  3469.     ELSEIF mo% = 5
  3470.         month& = 1
  3471.     ELSEIF mo% = 6
  3472.         month& = 2
  3473.     ELSEIF mo% = 7
  3474.         month& = 3
  3475.     ELSEIF mo% = 8
  3476.         month& = 4
  3477.     ELSEIF mo% = 9 OR mo% = 10 OR mo% = 11
  3478.         month& = 7
  3479.     ELSE
  3480.         month& = 8
  3481.     ENDIF
  3482.  
  3483.     year& = yr% + 3760
  3484.     while date& >= absheb&:(INT(1), INT(7), INT(year&+1))
  3485.         year& = year& + 1
  3486.     ENDWH
  3487.     length& = hclmoy&:(year&)
  3488.     WHILE date& > absheb&:(hcldom&:(month&, year&), month&, year&)
  3489.         month& = mod&:(month&, length&) + 1
  3490.     ENDWH
  3491.     day& = 1 + date& - absheb&:(INT(1), month&, year&)
  3492.     heb$ = NUM$(year&, 4) + RIGHT$("00"+NUM$(month&, 2), 2)
  3493.     heb$ = heb$ + RIGHT$("00"+NUM$(day&, 2), 2)
  3494.  
  3495.     RETURN heb$
  3496. ENDP
  3497. REM ***************************************************************************
  3498. REM (defun islamic-calendar-day-number (date)
  3499. REM   "Return the day number within the year of the Islamic date DATE."
  3500. REM     (let* ((month (extract-calendar-month date))
  3501. REM            (day (extract-calendar-day date)))
  3502. REM       (+ (* 30 (/ month 2))
  3503. REM          (* 29 (/ (1- month) 2))
  3504. REM          day)))
  3505.  
  3506. PROC icdn&:(day&, month&)
  3507.     RETURN 30*INT(month&/2) + 29*INT((month&-1)/2) + day&
  3508. ENDP
  3509.  
  3510. REM ***************************************************************************
  3511. REM (defun calendar-absolute-from-islamic (date)
  3512. REM   "Absolute date of Islamic DATE.
  3513. REM The absolute date is the number of days elapsed since the (imaginary)
  3514. REM Gregorian date Sunday, December 31, 1 BC."
  3515. REM   (let* ((month (extract-calendar-month date))
  3516. REM          (day (extract-calendar-day date))
  3517. REM          (year (extract-calendar-year date))
  3518. REM          (y (% year 30))
  3519. REM          (leap-years-in-cycle
  3520. REM           (cond
  3521. REM            ((< y 3) 0)  ((< y 6) 1)  ((< y 8) 2)  ((< y 11) 3) ((< y 14) 4)
  3522. REM            ((< y 17) 5) ((< y 19) 6) ((< y 22) 7) ((< y 25) 8) ((< y 27) 9)
  3523. REM            (t 10))))
  3524. REM     (+ (islamic-calendar-day-number date);; days so far this year
  3525. REM        (* (1- year) 354)                 ;; days in all non-leap years
  3526. REM        (* 11 (/ year 30))                ;; leap days in complete cycles
  3527. REM        leap-years-in-cycle               ;; leap days this cycle
  3528. REM        227014)))                         ;; days before start of calendar
  3529. PROC absisl&:(day&, month&, year&)
  3530.     LOCAL   y&
  3531.     LOCAL   lyic&   REM leap-years-in-cycle
  3532.     LOCAL   a&
  3533.  
  3534.     y& = mod&:(year&, INT(30))
  3535.     IF y& < 3
  3536.         lyic& = 0
  3537.     ELSEIF y& < 6
  3538.         lyic& = 1
  3539.     ELSEIF y& < 8
  3540.         lyic& = 2
  3541.     ELSEIF y& < 11
  3542.         lyic& = 3
  3543.     ELSEIF y& < 14
  3544.         lyic& = 4
  3545.     ELSEIF y& < 17
  3546.         lyic& = 5
  3547.     ELSEIF y& < 19
  3548.         lyic& = 6
  3549.     ELSEIF y& < 22
  3550.         lyic& = 7
  3551.     ELSEIF y& < 25
  3552.         lyic& = 8
  3553.     ELSEIF y& < 27
  3554.         lyic& = 9
  3555.     ELSE
  3556.         lyic& = 10
  3557.     ENDIF
  3558.  
  3559.     a& = icdn&:(day&, month&)
  3560.     a& = a& + (year& - 1) * 354
  3561.     a& = a& + INT(year& / 30) * 11
  3562.     a& = a& + lyic& + 227014
  3563.     RETURN a&
  3564. ENDP
  3565.  
  3566.  
  3567. REM ***************************************************************************
  3568. REM (defun islamic-calendar-leap-year-p (year)
  3569. REM   "Returns t if YEAR is a leap year on the Islamic calendar."
  3570. REM   (memq (% year 30)
  3571. REM         (list 2 5 7 10 13 16 18 21 24 26 29)))
  3572. PROC iclyp:(year&)
  3573.     LOCAL x&
  3574.     x& = mod&:(year&, INT(30))
  3575.     RETURN x&=2 OR x&=5 OR x&=7 OR x&=10 OR x&=13 OR x&=16 OR x&=18 OR x&=21 OR x&=24 OR x&=26 OR x&=29
  3576.    
  3577. ENDP
  3578.  
  3579. REM ***************************************************************************
  3580. REM (defun islamic-calendar-last-day-of-month (month year)
  3581. REM   "The last day in MONTH during YEAR on the Islamic calendar."
  3582. REM   (cond
  3583. REM    ((memq month (list 1 3 5 7 9 11)) 30)
  3584. REM    ((memq month (list 2 4 6 8 10)) 29)
  3585. REM    (t (if (islamic-calendar-leap-year-p year) 30 29))))
  3586. PROC icldom&:(month&, year&)
  3587.     IF month&=1 OR month&=3 OR month&=5 OR month&=7 OR month&=9 OR month&=11
  3588.     RETURN 30
  3589.     ENDIF
  3590.     IF month&=2 OR month&=4 OR month&=6 OR month&=8 OR month&=10
  3591.     RETURN 29
  3592.     ENDIF
  3593.     IF iclyp:(year&)
  3594.     RETURN 30
  3595.     ELSE
  3596.     RETURN 29
  3597.     ENDIF
  3598. ENDP
  3599.  
  3600. REM ***************************************************************************
  3601. REM 
  3602. REM (defun calendar-islamic-from-absolute (date)
  3603. REM   "Compute the Islamic date (month day year) corresponding to absolute DATE.
  3604. REM The absolute date is the number of days elapsed since the (imaginary)
  3605. REM Gregorian date Sunday, December 31, 1 BC."
  3606. REM   (if (< date 227015)
  3607. REM       (list 0 0 0);; pre-Islamic date
  3608. REM     (let* ((approx (/ (- date 227014) 355));; Approximation from below.
  3609. REM            (year           ;; Search forward from the approximation.
  3610. REM             (+ approx
  3611. REM                (calendar-sum y approx
  3612. REM                              (>= date (calendar-absolute-from-islamic
  3613. REM                                        (list 1 1 (1+ y))))
  3614. REM                              1)))
  3615. REM            (month          ;; Search forward from Muharram.
  3616. REM             (1+ (calendar-sum m 1
  3617. REM                               (> date
  3618. REM                                  (calendar-absolute-from-islamic
  3619. REM                                   (list m
  3620. REM                                         (islamic-calendar-last-day-of-month
  3621. REM                                          m year)
  3622. REM                                         year)))
  3623. REM                               1)))
  3624. REM            (day            ;; Calculate the day by subtraction.
  3625. REM             (- date
  3626. REM                (1- (calendar-absolute-from-islamic (list month 1 year))))))
  3627. REM       (list month day year))))
  3628. PROC islabs$:(date&)
  3629.     LOCAL   approx&, year&, month&, day&, sum&, y&, m&
  3630.     LOCAL   isl$(8)     REM yyyymmdd
  3631.  
  3632.     approx& = (date& - 227014) / 355
  3633.  
  3634.     y& = approx&
  3635.     sum& = 0
  3636.     WHILE date& >= absisl&:(INT(1), INT(1), INT(y& + 1))
  3637.     sum& = sum& + 1
  3638.     y& = y& + 1
  3639.     ENDWH
  3640.     year& = approx& + sum&
  3641.  
  3642.     m& = 1
  3643.     sum& = 0
  3644.     WHILE date& > absisl&:(icldom&:(m&, year&), m&, year&)
  3645.     sum& = sum& + 1
  3646.     m& = m& + 1
  3647.     ENDWH
  3648.     month& = 1 + sum&
  3649.  
  3650.     day& = date& - (absisl&:(INT(1), month&, year&) - 1)
  3651.  
  3652.     isl$ = NUM$(year&, 4) + RIGHT$("00"+NUM$(month&, 2), 2)
  3653.     isl$ = isl$ + RIGHT$("00"+NUM$(day&, 2), 2)
  3654.  
  3655.     RETURN isl$
  3656. ENDP
  3657.  
  3658. REM ===========================================================================
  3659. REM
  3660. REM End of functions derived from calendar.el
  3661. REM
  3662. REM ===========================================================================
  3663.  
  3664. REM ===========================================================================
  3665. REM
  3666. REM  The functions in this section are derived from functions in holidays.el in emacs.
  3667. REM  Holidays.el is Copyright (C) 1989, 1990, 1992, 1993, 1994 Free Software Foundation, Inc.
  3668. REM  Author: Edward M. Reingold <reingold@cs.uiuc.edu>
  3669. REM
  3670. REM ===========================================================================
  3671.  
  3672. REM   (let* ((century (1+ (/ displayed-year 100)))
  3673. REM          (shifted-epact        ;; Age of moon for April 5...
  3674. REM           (% (+ 14 (* 11 (% displayed-year 19));;     ...by Nicaean rule
  3675. REM                 (-           ;; ...corrected for the Gregorian century rule
  3676. REM                  (/ (* 3 century) 4))
  3677. REM                 (/    ;; ...corrected for Metonic cycle inaccuracy.
  3678. REM                  (+ 5 (* 8 century)) 25)
  3679. REM                 (* 30 century));;              Keeps value positive.
  3680. REM              30))
  3681. REM          (adjusted-epact       ;;  Adjust for 29.5 day month.
  3682. REM           (if (or (= shifted-epact 0)
  3683. REM                   (and (= shifted-epact 1) (< 10 (% displayed-year 19))))
  3684. REM               (1+ shifted-epact)
  3685. REM             shifted-epact))
  3686. REM          (paschal-moon       ;; Day after the full moon on or after March 21.
  3687. REM           (- (calendar-absolute-from-gregorian (list 4 19 displayed-year))
  3688. REM              adjusted-epact))
  3689. REM          (abs-easter (calendar-dayname-on-or-before 0 (+ paschal-moon 7)))
  3690. REM          (mandatory
  3691. REM           (list
  3692. REM            (list (calendar-gregorian-from-absolute abs-easter)
  3693. REM                  "Easter Sunday")
  3694.  
  3695. REM  Calculate the absolute date for easter sunday
  3696. PROC easter&:(year&)
  3697.     LOCAL   century&
  3698.     LOCAL   shepact&        REM shifted-epact; age of moon for April 5
  3699.     LOCAL   adepact&        REM adjusted-epact; adjustfor 29.5 day month
  3700.     LOCAL   pasmoon&        REM pascal-moon; day of full moon on or after March 21
  3701.     LOCAL   abseast&
  3702.  
  3703.     century& = year& / 100 + 1
  3704.  
  3705.     REM  Age of moon for April 5...
  3706.     REM  ...by Nicaean rule
  3707.     shepact& = 14 + (11 * mod&:(year&, INT(19)))
  3708.     REM  ...corrected for the Gregorian century rule
  3709.     shepact& = shepact& - 3*century&/4
  3710.     REM  ...corrected for Metonic cycle inaccuracy.
  3711.     shepact& = shepact& + (5 + 8*century&)/25
  3712.     REM  Keeps value positive.
  3713.     shepact& = shepact& + 30*century&
  3714.     shepact& = mod&:(shepact&, INT(30))
  3715.  
  3716.     REM  Adjust for 29.5 day month.
  3717.     IF (shepact&=0) OR ((shepact&=1) AND (mod&:(year&,INT(19)) < 10))
  3718.         adepact& = shepact& + 1
  3719.     ELSE
  3720.         adepact& = shepact&
  3721.     ENDIF
  3722.  
  3723.     REM  Day after the full moon on or after March 21.
  3724.     pasmoon& = absgreg&:(INT(19), INT(4), year&) - adepact&
  3725.  
  3726.     abseast& = doob&:(INT(0), INT(pasmoon&+7))
  3727.  
  3728.     RETURN abseast&
  3729.  
  3730. ENDP
  3731.  
  3732. REM =======================================================================================
  3733. REM
  3734. REM End of functions derived from holidays.el
  3735. REM
  3736. REM =======================================================================================
  3737.  
  3738.