home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / TP5WIO.ZIP / TP5WIO.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1989-07-07  |  38.9 KB  |  1,159 lines

  1. unit tp5wio;
  2. { !!!! NOTE: THE FILE TP5MISC.TPU MUST BE COMPILED FIRST !!!!
  3.    -- Global I/O procedures to include in programs generally
  4.   Much credit is due Bill Meacham who wrote the original file IO22.INC
  5.   and released it to the public domain.  Using that work this unit was
  6.   created and added to by Gerald Rohr of Homogenized Software.  As
  7.   with Bill's work, this program is released to the Public Domain for
  8.   all to use and modify.
  9.                        REVISION  HISTORY
  10.   ---------------------------------------------------------------------
  11.   Ver 2.22 Converted to a Turbo pascal V4 units.          30 Dec 87 gbr
  12.   Ver 2.30 Converted dates to longint types               19 Jan 88 gbr
  13.   Ver 2.42 Added global inv_flag for all write routines   08 Apr 88 gbr
  14.   Ver 2.43 Added long integer read and write routines     01 May 88 gbr
  15.   Ver 2.43 Added month and month/day routines             10 May 88 gbr
  16.   Ver 3.00 Replaced Window procedures/Reformated file     15 Jul 88 gbr
  17.   Ver 3.10 Moved Window error routines here               26 Aug 88 gbr
  18.   Ver 3.20 Added code and globals for color hi lights     27 Aug 88 gbr
  19.   Ver 3.21 Fixed leading decimal point in read_real       02 Sep 88 gbr
  20.   Ver 3.25 Added longint to/from packed string[4]         02 Sep 88 gbr
  21.   Ver 3.26 Added sys_time global variable                 07 Sep 88 gbr
  22.   Ver 3.30 Recompiled with Turbo Pascal Version 5.0       29 Sep 88 gbr
  23.   Ver 3.40 Added Month Name (string)                      05 Oct 88 gbr
  24.   Ver 3.50 Changed to use actual scan codes               30 Oct 88 gbr
  25.   Ver 3.60 Added RW word, byte                            18 Nov 88 gbr
  26.   Ver 3.70 Moved many routines to tp5misc.tpu             10 Dec 88 gbr
  27.   Ver 3.80 Added Vtp5wio function                         24 Mar 89 gbr
  28.   Ver 3.90 Added mk_dt_sts() date without century         28 Mar 89 gbr
  29.   Ver 4.00 Added color definitions to windows             06 Jul 89 gbr
  30.   Ver 4.10 Added openwind to open window with default col.07 Jul 89 gbr
  31.   --------------------------------------------------------------------- }
  32.  
  33. interface
  34.  
  35. uses
  36.    crt,dos,tp5misc;
  37.  
  38. const
  39.    fdslen     = 29 ;  { length of fulldatestring }
  40.  
  41. type
  42.    datestring = string[10] ;  { 'MM/DD/YYYY' }
  43.  
  44.    fulldatestring = string[fdslen] ;
  45.  
  46.    juldate = record
  47.       yr  : integer ; { 0 .. 9999 }
  48.       day : integer ; { 1 .. 366 }
  49.    end ;
  50.  
  51.    juldatestring = string[8] ; { 'YYYY/DDD' }
  52.  
  53.    montharray = array [1 .. 13] of integer ;
  54.  
  55.    intst     = string[2];        { packed string of an integer }
  56.    lintst    = string[4];        { packed string of an longint }
  57.  
  58. var
  59.    sys_date      :longint;
  60.    null_date     :longint;
  61.    null_date_str : datestring;
  62.    sys_time      :string[8];  { storage for the system time }
  63.  
  64.    fld, scrn     : integer ; { For field & screen cursor control }
  65.    macro         :array[1..10] of string; { Function key macro storage }
  66.    inv_flag      :boolean;  { if true all write routines inverse the screen,
  67.                               set to false by initialization. User uses
  68.                               this flag to control the screen attributes.}
  69.    col_inv_flag  :boolean;  { true if color monitor, false if monochrome,
  70.                               set by initialization routine,  User may change. }
  71.    inv_color     :byte;     { color to use for inverse data if col_inv_flag
  72.                               is true. Defaults to green, but user may change. }
  73.    in_window     :boolean;  { if true then we are in a window, used by the
  74.                               screen writing routines to high light screen
  75.                               data.  NOTE high lighting can only be done when
  76.                               in_window flag is true. }
  77.    reserv_wind   :integer;  { number of windows to reserve (not close) with
  78.                               endwindows procedure.  Initialized to 0, use
  79.                               with multiple program files. }
  80.    text_fg,                 { Text foreground color }
  81.    text_bg,                 { Text background color }
  82.    framefgnd,              { window border color }
  83.    framebkgnd,             { window background color }
  84.    title_color,             { window title color }
  85.    err_fg,                  { error message foreground }
  86.    err_bg,                  { error message background }
  87.    msg_fg,                  { message foreground }
  88.    msg_bg                   { message background }
  89.                 :byte;
  90.  
  91. PROCEDURE CLRLINE (col,row : integer);
  92. PROCEDURE BEEP ;
  93. PROCEDURE DO_FLD_CTL (key : integer);
  94.          { Adjusts global FLD based on value of key, the ordinal value
  95.            of last key pressed }
  96. PROCEDURE DO_SCRN_CTL ;
  97.          { Checks value of FLD and adjusts value of SCRN accordingly }
  98. PROCEDURE WRITE_STR (st:string ; col,row:integer);
  99. PROCEDURE WRITE_TEMP(var ln:string;tmp:string;x,y:integer);
  100.          { writes a string using a template.  the string (ln) is printed
  101.            left justified in the template using the filler locations.
  102.            quits when the template is complete on the screen.  Fills unused
  103.            template filler locations with space. }
  104. PROCEDURE WRITE_INT (i:integer ; width,col,row:integer);
  105. PROCEDURE WRITE_WORD(i:word; width,col,row:integer);
  106. PROCEDURE WRITE_BYTE(i:byte;width,col,row:integer);
  107. PROCEDURE WRITE_LINT(lint:longint;width,col,row:integer);
  108. PROCEDURE SET_BOOL (var bool : boolean);
  109.          { Sets boolean to be undefined, neither true nor false.
  110.            Boolean is stored as one byte:
  111.                $80 = undefined
  112.                $01 = true
  113.                $00 = false.
  114.            Note : Turbo interprets $80 as true because it is greater than zero! }
  115. FUNCTION DEFINED (bool : boolean) : boolean ;
  116.          { Determines whether the boolean is defined or not }
  117. PROCEDURE WRITE_BOOL (bool:boolean ; col, row:integer);
  118. PROCEDURE WRITE_REAL (r:real ; width,frac,col,row:integer);
  119. FUNCTION BUILD_STR (ch : char ; n : integer) : string ;
  120.          { returns a string of length n of the character ch }
  121. PROCEDURE READ_STR (var st:string ; maxlen, col, row:integer);
  122.          { Read String.  This procedure gets input from the keyboard one
  123.            character at a time and edits on the fly, rejecting invalid
  124.            characters.  COL and ROW tell where to begin the data input
  125.            field, and MAXLEN is the maximum length of the string to be
  126.            returned.
  127.            Only use the Function keys for string input data, for other
  128.            types of input will beep. }
  129. PROCEDURE READ_TEMP(var st:string;tmp:string;col, row:integer);
  130.            { Read string with a template.  This procedure gets input from
  131.            the keyboard one character at a time and edits on the fly,
  132.            rejecting invalid characters.  tmp is a template which is filled
  133.            in where filler characters exist, any other characters are displayed
  134.            on the screen.  Returned string does NOT have the template imbeded in
  135.            it.  COL and ROW tell where to begin the data input
  136.            field, Max length of the string is the max length of the template.
  137.            }
  138. PROCEDURE READ_INT (var int:integer ; maxlen, col, row:integer);
  139.          { Read Integer.  This procedure gets input from the keyboard
  140.            one character at a time and edits on the fly, rejecting
  141.            invalid characters.  COL and ROW tell where to begin the data
  142.            input field, and MAXLEN is the maximum length of the integer
  143.            to be returned. }
  144. PROCEDURE READ_LINT (var lint:longint ; maxlen, col, row:integer);
  145.          { Read LongInt.  This procedure gets input from the keyboard
  146.            one character at a time and edits on the fly, rejecting
  147.            invalid characters.  COL and ROW tell where to begin the data
  148.            input field, and MAXLEN is the maximum length of the integer
  149.            to be returned. }
  150. PROCEDURE READ_WORD(var wd:word; maxlen,col,row:integer);
  151.          { Read Word.  This procedure gets input from the keyboard
  152.            one character at a time and edits on the fly, rejecting
  153.            invalid characters.  COL and ROW tell where to begin the data
  154.            input field, and MAXLEN is the maximum length of the word
  155.            to be returned. }
  156. PROCEDURE READ_BYTE(var bt:byte; maxlen,col,row:integer);
  157.          { Read byte.  This procedure gets input from the keyboard
  158.            one character at a time and edits on the fly, rejecting
  159.            invalid characters.  COL and ROW tell where to begin the data
  160.            input field, and MAXLEN is the maximum length of the byte
  161.            to be returned. }
  162.  
  163. FUNCTION EQUAL (r1,r2 : real) : boolean ;
  164.          { tests functional equality of two real numbers -- 4/30/85 }
  165. FUNCTION GREATER (r1,r2 : real) : boolean ;
  166.          { tests functional inequality of two real numbers -- 5/1/85 }
  167. PROCEDURE READ_REAL (var r:real ; maxlen,frac,col,row:integer);
  168.          { Read Real.  This procedure gets input from the keyboard
  169.            one character at a time and edits on the fly, rejecting
  170.            invalid characters.  COL and ROW tell where to begin the data
  171.            input field; MAXLEN is the maximum length of the string
  172.            representation of the real number, including sign and decimal
  173.            point; FRAC is the fractional part, the number of digits to
  174.            right of the decimal point.
  175.  
  176.            Note -- In Turbo the maximum number of significant digits in
  177.            decimal (not scientific) representation is 11.  In TurboBCD,
  178.            the maximum number of significant digits is 18.  It is the
  179.            programmer's responsibility to limit input and computed output
  180.            to the maximum significant digits. }
  181. PROCEDURE READ_YN (var bool:boolean; col,row:integer);
  182.          { Inputs "Y" OR "N" to boolean at column and row specified,
  183.            prints "YES" or "NO."
  184.            Note -- use this when the screen control will not return
  185.            to the question and the boolean IS NOT defined before the
  186.            user answers the question.  Does not affect global FLD. }
  187. PROCEDURE READ_BOOL (var bool:boolean; col,row:integer);
  188.          { Displays boolean at column and row specified, inputs "Y"
  189.            or "N" to set new value of boolean, prints "YES" or "NO."
  190.            Boolean is "forced;" user cannot cursor forward past undefined
  191.            boolean.  Pressing "Y" or "N" terminates entry.
  192.            Boolean is stored as one byte:
  193.                $80 = undefined
  194.                $01 = true
  195.                $00 = false.
  196.            Note : Turbo interprets $80 as true because it is greater
  197.            than zero! }
  198. PROCEDURE PAUSE ;
  199.          {Prints message on bottom line, waits for user response.
  200.           Changed from line 24 to line 23 for windows  gbr}
  201. PROCEDURE HARD_PAUSE ;
  202.          { Like Pause, but only accepts space bar or Escape and only
  203.            goes forward. Changed from line 24 to line 23 for windows.  gbr }
  204. PROCEDURE SHOW_MSG (msg : string);
  205.          { Beeps, displays message centered on line 22, pauses }
  206.          { changed from line 23 to line 22 for windows. gbr }
  207. FUNCTION MK_DT_ST (dt :longint) : datestring ;
  208.          { Makes a string out of a date -- used for printing dates,
  209.            includes century (ie MM/DD/YYYY) }
  210. FUNCTION MK_DT_STS(dt :longint) : datestring ;
  211.          { Makes a string out of a date -- used for printing dates,
  212.            does not include century (ie MM/DD/YY) }
  213. PROCEDURE WRITE_DATE (dt: longint ; col, row: integer);
  214.          { Writes date at column and row specified }
  215. FUNCTION MK_JUL_DT_ST (jdt : juldate) : juldatestring ;
  216.          { makes a string out of a julian date }
  217. PROCEDURE READ_DATE (var dt: longint ; col, row: integer);
  218.          { Read date at column and row specified.  If the user enters
  219.            only two digits for the year, the procedure plugs the
  220.            century as 1900 or 2000, but the user can enter all four
  221.            digits to override the plug. }
  222. FUNCTION GREATER_DATE (dt1, dt2 : longint) : integer ;
  223.          { Compares two dates, returns 0 if both equal, 1 if first is
  224.            greater, 2 if second is greater. }
  225. PROCEDURE GREG_TO_JUL (dt : longint ; var jdt : juldate);
  226.          { converts a gregorian date to a julian date }
  227. PROCEDURE JUL_TO_GREG (jdt : juldate ; var dt : longint);
  228.          { converts a julian date to a gregorian date }
  229. PROCEDURE NEXT_DAY (var dt : longint);
  230.          { Adds one day to the date }
  231. PROCEDURE PREV_DAY (var dt : longint);
  232.          { Subtracts one day from the date }
  233. FUNCTION DATE_DIFF (dt1, dt2 : longint) : longint ;
  234.          { computes the number of days between two dates }
  235. FUNCTION MONTH_DIFF (dt1, dt2 : longint ) : integer ;
  236.          { Computes number of months between two dates, rounded.
  237.            30.4167 = 356/12, average number of days in a month. }
  238. FUNCTION EQUAL_DATE (dt1, dt2 : longint) : boolean ;
  239.          { Tests whether two dates are equal }
  240. FUNCTION BUILD_FULL_DATE_STR (dt : longint) : fulldatestring ;
  241.          { Build printable string of current date -- from ROS 3.4
  242.            source code. }
  243. FUNCTION MONTH(dt:longint):integer;
  244.          { returns the month portion of a date.}
  245. FUNCTION DAY(dt:longint):integer;
  246.          { returns the day from the date }
  247. FUNCTION YEAR(dt:longint;centry:boolean):integer;
  248.          { returns the year of a date.  if the centry flag is true
  249.            returns 4 digit year otherwise returns two digit year. }
  250. FUNCTION MONTH_NAME(mon:integer):string;
  251.          { returns the month name given the month number (1-12) }
  252.  
  253. { ---- window procedures Derived from article in Computer Language
  254.   Magazine June 1988 by James Kerr ---- }
  255. PROCEDURE OPENWIND(wtitle:string;x1,y1,x2,y2:byte);
  256.          { Works just like openwindow except uses the default colors
  257.            for text foreground and background.  Actually just calls
  258.            openwindow with text_fg and text_bg
  259.          }
  260. PROCEDURE OPENWINDOW(wtitle:string;x1,y1,x2,y2:byte;
  261.                      fgnd,bkgnd: byte);
  262.          { wtitle is centered on the top border line of the window, x
  263.          and y are the window coordinates, fgnd and bkgnd are the
  264.          colors of the inside of the window (note the border is always
  265.          white, if a window can not be opened, a message as to why will
  266.          be displayed and the program exits
  267.          }
  268. PROCEDURE CLOSEWINDOW;
  269.          { closes the current open window, does nothing if no
  270.            window to close. }
  271. PROCEDURE ENDWINDOWS;
  272.          { close any open windows when exiting the windows system.  Use
  273.            as the last statment in program to insure return to
  274.            enviroment you came from.  The global reserv_wind is normally
  275.            set to 0 allowing all windows to be closed, if using a
  276.            multi file window program, reserv_wind can be set to the
  277.            number of windows to be left open when a particular program
  278.            terminates.  Always set reserv_wind to 0 before the final
  279.            program call to endwindows.
  280.          }
  281.  
  282. FUNCTION VTP5WIO:string;
  283.         { Return a string which contains the version of this set of
  284.           routines }
  285. { ---------------------------------------------------------------- }
  286.  
  287. implementation
  288. {$I TP5WIO.INC}
  289.  
  290. procedure read_yn(var bool:boolean; col,row:integer);
  291. { Inputs "Y" OR "N" to boolean at column and row specified,
  292.   prints "YES" or "NO."
  293.  
  294.   Note -- use this when the screen control will not return
  295.   to the question and the boolean IS NOT defined before the
  296.   user answers the question.  Does not affect global FLD. }
  297.  
  298. var ch:char ;
  299. begin
  300.    gotoxy (col,row);
  301.    write ('   ');
  302.    gotoxy (col,row);
  303.    repeat
  304.       keyin (ch)
  305.    until (ch in ['Y', 'y', 'N', 'n']);
  306.    if (ch = 'Y') or (ch = 'y') then
  307.       begin
  308.       write_str('YES',col,row);
  309.       bool := true
  310.    end
  311.    else
  312.       begin
  313.       write_str('NO ',col,row);
  314.       bool := false
  315.    end
  316. end ; { proc read_yn }
  317.  
  318. { ------------------------------------------------------------ }
  319.  
  320. procedure read_bool(var bool:boolean; col,row:integer);
  321. { Displays boolean at column and row specified, inputs "Y"
  322.   or "N" to set new value of boolean, prints "YES" or "NO."
  323.   Boolean is "forced;" user cannot cursor forward past undefined
  324.   boolean.  Pressing "Y" or "N" terminates entry.
  325.  
  326.   Boolean is stored as one byte:
  327.       $80 = undefined
  328.       $01 = true
  329.       $00 = false.
  330.   Note : Turbo interprets $80 as true because it is greater than zero! }
  331.  
  332. var
  333.    ch  : char ;
  334.    key : integer ;
  335.  
  336. begin
  337.    write_bool (bool, col, row);
  338.    gotoxy (col, row);
  339.    repeat
  340.       keyin (ch);
  341.       key := ord(ch);
  342.       if key in [$59,$79] then          { 'Y','y' }
  343.          begin
  344.          bool := true ;
  345.          key  := next_fld ;
  346.          do_fld_ctl(key)
  347.       end
  348.       else if key in [$4E, $6E] then    { 'N','n' }
  349.          begin
  350.          bool := false ;
  351.          key  := next_fld ;
  352.          do_fld_ctl(key)
  353.       end
  354.       else if key in terminating then
  355.          begin
  356.          if(not defined(bool)) and
  357.            (key in [carr_rtn, next_fld, next_page]) then
  358.             key := $00
  359.          else
  360.             do_fld_ctl (key)
  361.       end
  362.    until key in terminating ;
  363.    write_bool (bool, col, row)
  364. end ; {--- of read_bool ---}
  365.  
  366. { -------------------------------------------------------------------------- }
  367.  
  368. procedure pause ;
  369. {Prints message on bottom line, waits for user response.
  370.  Moved message into window in lower left corner gbr}
  371. var
  372.    ch   : char ;
  373.    key : integer ;
  374. begin
  375.    save_colors;
  376.    openwindow('',2,23,60,25,msg_fg,msg_bg);
  377.    write_str ('SPACE BAR = CONTINUE, UP-ARROW = GO BACK, ESC = QUIT',2,1);
  378.    repeat
  379.       keyin (ch);
  380.       key := ord(ch);
  381.       case key of
  382.          $20      : fld := succ(fld);
  383.          prev_fld : fld := pred(fld);
  384.          prev_page : fld := -999 ;
  385.          escape   : fld := maxint ;
  386.       end ;
  387.    until key in [$20, prev_fld, prev_page, escape] ;
  388.    closewindow;
  389.    restore_colors;
  390. end ; { proc pause }
  391.  
  392. { ------------------------------------------------------------ }
  393.  
  394. procedure hard_pause ;
  395. { Like Pause, but only accepts space bar or Escape and only goes forward }
  396. { puts the message in a window at bottom of screen }
  397. var
  398.    ch   : char ;
  399.    key : integer ;
  400. begin
  401.    save_colors;
  402.    openwindow('',1,23,25,25,msg_fg,msg_bg);
  403.    write_str('SPACE BAR TO CONTINUE',2,1);
  404.    repeat
  405.       keyin (ch);
  406.       key := ord(ch);
  407.       case key of
  408.          $20      : fld := succ(fld);
  409.          escape   : fld := maxint ;
  410.       end ;
  411.    until key in [$20, escape] ;
  412.    closewindow;
  413.    restore_colors;
  414. end ; { proc hard_pause }
  415.  
  416. { ------------------------------------------------------------ }
  417.  
  418. procedure show_msg(msg : string);
  419. { Beeps, displays message centered on line 22, pauses }
  420. { changed to put message in window in lower right corner. gbr }
  421.  
  422. var
  423.    savefld : integer ;
  424.  
  425. begin
  426.    save_colors;
  427.    savefld := fld ;
  428.    beep ;
  429.    openwindow('ERROR MESSAGE',26,23,79,25,err_fg,err_bg);
  430.    if length(msg) > 76 then msg := copy(msg,1,52);
  431.    write_str(msg,((52-length(msg)) div 2),1);
  432.    hard_pause ;
  433.    closewindow;
  434.    fld := savefld ;
  435.    restore_colors;
  436. end ; { proc show_msg }
  437.  
  438. { ---------------------------------------------------------------- }
  439.  
  440. { -- End of Standard screen routines - Beginning of Date routines -- }
  441.  
  442. function mk_dt_st(dt:longint):datestring;
  443. { returns a string of the dates to print, returns long date. ie MM/DD/YYYY }
  444.  
  445. var
  446.    yr,mo,dy,i   :integer;
  447.    result       :longint;
  448.    stmo,stdy    :string[2];
  449.    styr         :string[4];
  450.  
  451. begin
  452.    if dt = 0 then mk_dt_st := null_date_str
  453.    else
  454.       begin
  455.       dy := (dt mod 100);
  456.       result := (dt - dy); { subtract the number of days }
  457.       result := result div 100;  { move to right }
  458.       mo := (result mod 100);  { get the month }
  459.       yr := (result div 100); { get year }
  460.       str(yr:1,styr);
  461.       str(mo:1,stmo);
  462.       if length(stmo) = 1 then stmo := concat('0',stmo);
  463.       str(dy:1,stdy);
  464.       if length(stdy) = 1 then stdy := concat('0',stdy);
  465.       mk_dt_st := concat(stmo,'/',stdy,'/',styr);
  466.    end;
  467. end; {function mk_dt_st}
  468.  
  469. { ------------------------------------------------------------ }
  470.  
  471. function mk_dt_sts(dt:longint):datestring;
  472. { returns a string of the dates to print, returns short date,
  473.   ie MM/DD/YY }
  474.  
  475. var
  476.    yr,mo,dy,i   :integer;
  477.    result       :longint;
  478.    stmo,stdy    :string[2];
  479.    styr         :string[4];
  480.  
  481. begin
  482.    if dt = 0 then mk_dt_sts := copy(null_date_str,1,8)
  483.    else
  484.       begin
  485.       dy := (dt mod 100);
  486.       result := (dt - dy); { subtract the number of days }
  487.       result := result div 100;  { move to right }
  488.       mo := (result mod 100);  { get the month }
  489.       yr := (result div 100); { get year }
  490.       str(yr:1,styr);
  491.       if(length(styr) < 2) then styr := concat('??',styr);
  492.       styr := copy(styr,length(styr)-1,2);
  493.       str(mo:1,stmo);
  494.       if length(stmo) = 1 then stmo := concat('0',stmo);
  495.       str(dy:1,stdy);
  496.       if length(stdy) = 1 then stdy := concat('0',stdy);
  497.       mk_dt_sts := concat(stmo,'/',stdy,'/',styr);
  498.    end;
  499. end; {function mk_dt_sts}
  500.  
  501. { ------------------------------------------------------------ }
  502.  
  503. procedure write_date(dt: longint ; col, row: integer);
  504. { Writes date at column and row specified }
  505. var
  506.     ds : datestring ;
  507. begin
  508.     ds := mk_dt_st (dt);
  509.     write_str(ds,col,row)
  510. end ; { --- proc write_date --- }
  511.  
  512. { ------------------------------------------------------------ }
  513.  
  514. function mk_jul_dt_st(jdt : juldate) : juldatestring;
  515. { makes a string out of a julian date }
  516. var
  517.    yr_st  : string[4] ;
  518.    day_st : string[3] ;
  519.    jdt_st : juldatestring ;
  520. begin
  521.    with jdt do
  522.       if (yr=0) and (day = 0) then
  523.          jdt_st := 'YYYY/DDD'
  524.       else
  525.          begin
  526.          str(yr:4,yr_st);
  527.          str(day:3,day_st);
  528.          jdt_st := concat (yr_st,'/',day_st)
  529.       end ;
  530.    mk_jul_dt_st := jdt_st
  531. end ;  { function mk_jul_dt_st }
  532.  
  533. { ------------------------------------------------------------ }
  534.  
  535. function leapyear (yr : integer) : boolean ;
  536. { Whether the year is a leap year or not.
  537.   The year is year and century, e.g. year '1984' is 1984, not 84 }
  538. begin
  539.    leapyear := ((yr mod 4 = 0) and (not(yr mod 100 = 0)))
  540.              or ( yr mod 400 = 0 )
  541. end ;
  542.  
  543. { ------------------------------------------------------------ }
  544.  
  545. procedure get_dt_val(tpdate:longint;var yr,mo,dy:integer);
  546. { breaks the tpdate into the global integer values }
  547.  
  548. var
  549.    result       :longint;
  550.  
  551. begin
  552.    dy := (tpdate mod 100);
  553.    result := (tpdate - dy); { subtract the number of days }
  554.    result := result div 100;  { move to right }
  555.    mo := (result mod 100);  { get the month }
  556.    yr := (result div 100); { get year }
  557. end;  {function get_dt_val}
  558.  
  559. { ------------------------------------------------------------ }
  560.  
  561. function valid_date (dt:longint) : boolean ;
  562. { Test whether date is valid }
  563. var
  564.     bad_fld  :integer ;
  565.     yr,mo,dy :integer;
  566.  
  567. begin
  568.    get_dt_val(dt,yr,mo,dy);   { puts the date in local variables }
  569.    bad_fld := 0 ;
  570.    if (mo = 0) and (dy = 0) and (yr = 0) then
  571.       bad_fld := 0
  572.    else if not (mo in [1 .. 12]) then
  573.       bad_fld := 1
  574.    else
  575.       if (dy > 31) or (dy < 1) or ((mo in [4,6,9,11]) and (dy > 30)) then
  576.          bad_fld := 2
  577.    else
  578.       if mo = 2 then
  579.          begin
  580.          if (leapyear(yr) and (dy > 29)) or
  581.             ((not leapyear(yr)) and (dy > 28)) then
  582.             bad_fld := 2
  583.       end
  584.    else
  585.       if yr = 0 then
  586.          bad_fld := 3;
  587.    valid_date := (bad_fld = 0)
  588. end ; { function valid_date }
  589.  
  590. { ------------------------------------------------------------ }
  591.  
  592. procedure read_date(var dt: longint ; col, row: integer);
  593.  
  594. { Read date at column and row specified.  If the user enters only
  595.   two digits for the year, the procedure plugs the century as 1900 or
  596.   2000, but the user can enter all four digits to override the plug. }
  597.  
  598. var
  599.    ch       : char ;
  600.    savefld,
  601.    bad_fld,
  602.    key,
  603.    p        : integer ;
  604.    yr,mo,dy :integer;
  605.    s,
  606.    template : datestring ;
  607.  
  608.    { ==================== }
  609.  
  610.    procedure add_to_str ;
  611.    var
  612.       l : integer ;
  613.    begin
  614.       l := length(s);
  615.       if l = 10 then
  616.          beep
  617.       else if (l=1) or (l=4) then
  618.          begin
  619.          s := concat(s,ch,'/');
  620.          write (ch,'/')
  621.       end
  622.       else
  623.          begin
  624.          s := concat(s,ch);
  625.          write (ch)
  626.       end
  627.    end ; { proc add_to_str }
  628.  
  629.    { ==================== }
  630.  
  631.    procedure adjust_dt_str ;
  632.    var
  633.       l : integer ;
  634.    begin
  635.       case key of
  636.          del_fld :begin
  637.                      s := '' ;
  638.                      gotoxy(col,row);
  639.                      write(template);
  640.                      gotoxy (col,row)
  641.                   end ;
  642.          del_left,
  643.         prev_char,
  644.         BS       :begin   { prev_char is destructive backspace! }
  645.                      l := length(s);
  646.                      if l = 0 then
  647.                         beep
  648.                      else
  649.                         if (l=3) or (l=6) then
  650.                            begin
  651.                            write (^H,^H,chr(filler),^H);
  652.                            delete (s,l-1,2)
  653.                         end
  654.                      else
  655.                         begin
  656.                         write (^H,chr(filler),^H);
  657.                         delete (s,l,1)
  658.                      end
  659.                   end
  660.       end { case }
  661.    end ; { proc adjust_dt_str }
  662.  
  663.    { ==================== }
  664.  
  665.    procedure convert_date ;
  666.    { convert the string to a date -- longint }
  667.    var
  668.       code     :integer ;
  669.       result   :longint;
  670.       i        :byte;
  671.  
  672.    begin
  673.       for i := 1 to 8 do  { fill to 2 digits of year }
  674.          begin
  675.          if length(s) < i then s := concat(s,'0');
  676.          if s[i] = ' ' then s[i] := '0'; { fill any spaces with 0 }
  677.       end;
  678.       val (copy(s,1,2),mo,code);
  679.       if code <> 0 then
  680.          begin
  681.          write ('** MONTH CONVERSION ERROR ',code);
  682.          halt
  683.       end ;
  684.       val (copy(s,4,2),dy,code);
  685.       if code <> 0 then
  686.          begin
  687.          write ('** DAY CONVERSION ERROR ',code);
  688.          halt
  689.       end ;
  690.       val (copy(s,7,4),yr,code);
  691.       if code <> 0 then
  692.          begin
  693.          write ('** YEAR CONVERSION ERROR ',code);
  694.          halt
  695.       end ;
  696.       if ((yr = 0) and (mo = 0) and (dy = 0)) then
  697.          begin                      { default to nodate }
  698.          dt := 0;
  699.       end
  700.       else
  701.          begin                       { plug century }
  702.          if yr < 80 then
  703.             yr := 2000 + yr
  704.          else if yr < 100 then
  705.             yr := 1900 + yr;
  706.          result := yr;
  707.          result := (result * 100) + mo;
  708.          result := (result * 100) + dy;
  709.          dt := result;
  710.       end;
  711.       result := yr;
  712.       result := (result * 100) + mo;
  713.       result := (result * 100) + dy;
  714.       dt := result;
  715.    end ; { proc convert_date}
  716.  
  717.    { ==================== }
  718.  
  719.    procedure edit_date ;                  { Edit for valid date }
  720.    begin
  721.       bad_fld := 0 ;
  722.       if (yr = 0) and (mo = 0) and (dy = 0) then
  723.          bad_fld := 0
  724.       else if not (mo in [1 .. 12]) then
  725.            bad_fld := 1
  726.       else if (dy > 31) or (dy < 1) or ((mo in [4,6,9,11]) and (dy > 30)) then
  727.            bad_fld := 2
  728.       else if mo = 2 then
  729.          begin
  730.          if (leapyear(yr) and (dy > 29)) or ((not leapyear(yr)) and
  731.             (dy > 28)) then
  732.             bad_fld := 2
  733.       end
  734.       else
  735.          if yr = 0 then
  736.             bad_fld := 3
  737.    end ; { proc edit_date }
  738.  
  739.    { ==================== }
  740.  
  741. begin { proc read_date }
  742.    savefld := fld ;
  743.    ch := chr(filler);
  744.    template := concat(ch,ch,'/',ch,ch,'/',ch,ch,ch,ch);
  745.    if (dt = 0) then
  746.       begin
  747.       write_str (template,col,row);
  748.       s := '' ;
  749.       gotoxy (col,row)
  750.    end
  751.    else
  752.       begin
  753.       s := mk_dt_st(dt);
  754.       p := pos(' ',s);
  755.       while p <> 0 do
  756.          begin
  757.          s[p] := '0' ;
  758.          p := pos(' ',s)
  759.       end ;
  760.       write_str (s,col,row)
  761.    end ;
  762.    repeat
  763.       keyin(ch);
  764.       key := ord(ch);
  765.       if ch in ['0'..'9'] then
  766.          add_to_str
  767.       else if key in adjusting then
  768.          adjust_dt_str
  769.       else if key in terminating then
  770.          begin
  771.          convert_date ;  { uses local yr, mo, and dy }
  772.          edit_date ;
  773.          do_fld_ctl (key);
  774.          if (fld < maxint) and (fld > savefld) then
  775.             begin                          { edit only going forward }
  776.             if bad_fld <> 0 then
  777.                begin
  778.                case bad_fld of
  779.                   1 : show_msg ('INVALID MONTH');
  780.                   2 : show_msg ('INVALID DAY');
  781.                   3 : show_msg ('INVALID YEAR')
  782.                end ; { case }
  783.                fld := savefld
  784.             end
  785.          end
  786.       end
  787. (*      else
  788.           beep  *)
  789.    until key in terminating ;
  790.    write_date (dt,col,row)
  791. end ; { proc read_date }
  792.  
  793. { ------------------------------------------------------------ }
  794.  
  795. function greater_date(dt1, dt2 : longint) : integer;
  796. { Compares two dates, returns 0 if both equal, 1 if first is
  797.   greater, 2 if second is greater.
  798. }
  799.  
  800. begin
  801.    if dt1 > dt2 then
  802.       greater_date := 1
  803.    else if dt2 > dt1 then
  804.       greater_date := 2
  805.    else { both equal }
  806.       greater_date := 0
  807. end ; { --- of greater_date --- }
  808.  
  809. { ------------------------------------------------------------ }
  810.  
  811. procedure greg_to_jul(dt : longint ; var jdt : juldate);
  812. { converts a gregorian date to a julian date }
  813. var
  814.    yr,mo,dy :integer;
  815. begin
  816.    get_dt_val(dt,yr,mo,dy);   { get the global dates }
  817.    jdt.yr := yr ;
  818.    if (yr = 0) and (mo = 0) and (dy = 0) then
  819.       jdt.day := 0
  820.    else
  821.       begin
  822.       if (leapyear(yr)) and (mo > 2) then
  823.          jdt.day := 1
  824.       else
  825.          jdt.day := 0 ;
  826.       jdt.day := jdt.day + monthtotal[mo] + dy
  827.    end
  828. end ;  { --- procedure greg_to_jul --- }
  829.  
  830. { ------------------------------------------------------------ }
  831.  
  832. procedure jul_to_greg(jdt : juldate ; var dt : longint);
  833. { converts a julian date to a gregorian date }
  834. var
  835.    i, workday :integer ;
  836.    yr,mo,dy   :integer;
  837. begin
  838.    yr := jdt.yr ;
  839.    if (jdt.yr = 0) and (jdt.day = 0) then
  840.       begin
  841.       mo := 0 ; dy := 0
  842.    end
  843.    else
  844.       begin
  845.       workday := jdt.day ;
  846.       if (leapyear(jdt.yr)) and (workday > 59) then
  847.          workday := workday - 1 ;   { make it look like a non-leap year }
  848.       i := 1 ;
  849.       repeat
  850.          i := i + 1
  851.       until not (workday > monthtotal[i]);
  852.       i := i - 1 ;
  853.       mo := i ;
  854.       dy := workday - monthtotal[i] ;
  855.       if leapyear(jdt.yr) and (jdt.day = 60) then
  856.          dy := dy + 1
  857.       end;
  858.    { need to convert the globals back to longint }
  859.    dt := yr;
  860.    dt := (dt * 100) + mo;
  861.    dt := (dt * 100) + dy;
  862. end ;  { --- procedure jul_to_greg --- }
  863.  
  864. { ------------------------------------------------------------ }
  865.  
  866. procedure next_day(var dt : longint);
  867. { Adds one day to the date }
  868. var
  869.    jdt  : juldate ;
  870.    leap : boolean ;
  871.    yr,mo,dy :integer;
  872.  
  873. begin
  874.    get_dt_val(dt,yr,mo,dy);
  875.    greg_to_jul (dt,jdt);
  876.    jdt.day := jdt.day + 1 ;
  877.    leap := leapyear (yr);
  878.    if (leap and (jdt.day = 367)) or (not leap and (jdt.day = 366)) then
  879.       begin
  880.       jdt.yr := jdt.yr + 1 ;
  881.       jdt.day := 1
  882.    end ;
  883.    jul_to_greg (jdt,dt)
  884. end ;  { --- procedure next_day --- }
  885.  
  886. { ------------------------------------------------------------ }
  887.  
  888. procedure prev_day(var dt : longint);
  889. { Subtracts one day from the date }
  890. var
  891.    jdt : juldate ;
  892. begin
  893.    greg_to_jul (dt,jdt);
  894.    jdt.day := jdt.day - 1 ;
  895.    if jdt.day < 1 then
  896.       begin
  897.       jdt.yr := jdt.yr - 1 ;
  898.       if leapyear (jdt.yr) then
  899.          jdt.day := 366
  900.       else
  901.          jdt.day := 365
  902.    end ;
  903.    jul_to_greg (jdt,dt)
  904. end ;  { --- procedure prev_day --- }
  905.  
  906. { ------------------------------------------------------------ }
  907.  
  908. function date_diff(dt1, dt2 : longint) : longint;
  909. { computes the number of days between two dates }
  910.  
  911. var
  912.    jdt1, jdt2 : juldate ;
  913.    i, num_leap_yrs,
  914.    yr1,mo1,dy1,
  915.    yr2,mo2,dy2  : integer ;
  916.  
  917. begin
  918.    greg_to_jul (dt1, jdt1);
  919.    greg_to_jul (dt2, jdt2);
  920.    get_dt_val(dt1,yr1,mo1,dy1);
  921.    get_dt_val(dt2,yr2,mo2,dy2);
  922.    num_leap_yrs := 0 ;         { adjust for leap years }
  923.    if yr2 > yr1 then
  924.       begin
  925.       for i := yr1 to yr2 - 1 do
  926.          if leapyear(i) then
  927.             num_leap_yrs := num_leap_yrs + 1
  928.    end
  929.    else
  930.       if yr1 > yr2 then
  931.          begin
  932.          for i := yr2 to yr1 - 1 do
  933.             if leapyear(i) then
  934.                num_leap_yrs := num_leap_yrs - 1
  935.    end ;
  936.  
  937.    date_diff := jdt2.day - jdt1.day +
  938.                 ((jdt2.yr - jdt1.yr) * 365) + num_leap_yrs;
  939. end ;
  940.  
  941. { ------------------------------------------------------------ }
  942.  
  943. function month_diff(dt1, dt2 : longint ) : integer;
  944. { Computes number of months between two dates, rounded.
  945.   30.4167 = 356/12, average number of days in a month. }
  946. begin
  947.    month_diff := round((date_diff(dt1, dt2) + 1) / 30.4167)
  948. end ;
  949.  
  950. { ------------------------------------------------------------ }
  951.  
  952. function equal_date(dt1, dt2 : longint) : boolean;
  953. { Tests whether two dates are equal }
  954. begin
  955.    if (dt1 = dt2) then
  956.       equal_date := true
  957.    else
  958.       equal_date := false;
  959. end ;
  960.  
  961. { ------------------------------------------------------------ }
  962.  
  963. function zeller (dt : longint) : integer ;
  964. { Compute the day of the week using Zeller's Congruence.
  965.   From ROS 3.4 source code }
  966. var
  967.    century: integer ;
  968.    yr,mo,dy :integer;
  969.  
  970. begin
  971.    get_dt_val(dt,yr,mo,dy);
  972.    if mo > 2
  973.       then mo := mo - 2
  974.    else
  975.       begin
  976.       mo := mo + 10 ;
  977.       yr := pred(yr)
  978.    end ;
  979.    century := yr div 100 ;
  980.    yr := yr mod 100 ;
  981.    zeller := (dy - 1 + ((13 * mo - 1) div 5) + (5 * yr div 4) +
  982.               century div 4 - 2 * century + 1) mod 7
  983. end ;  { function zeller }
  984.  
  985. { ------------------------------------------------------------ }
  986.  
  987. function build_full_date_str(dt : longint) : fulldatestring;
  988. { Build printable string of current date -- from ROS 3.4 source code. }
  989. const
  990.    day: array [0..6] of string[6] =
  991.               ('Sun','Mon','Tues','Wednes','Thurs','Fri','Satur');
  992.    month: array [1..12] of string[9] =
  993.               ('January','February','March','April','May','June','July',
  994.                'August','September','October','November','December');
  995. var
  996.    i: integer ;
  997.    s: fulldatestring ;
  998.    yr,mo,dy :integer;
  999.  
  1000.    function intstr(n, w: integer): string ;
  1001.    { Return a string value of width w for the input integer n }
  1002.    var
  1003.       st: string ;
  1004.    begin
  1005.       str(n:w, st);
  1006.       st := purgech (st,' ');
  1007.       intstr := st
  1008.    end ;
  1009.  
  1010. begin { build_full_date_str }
  1011.    get_dt_val(dt,yr,mo,dy);
  1012.    if  (mo = 0) and (dy = 0) and (yr = 0) then
  1013.       s := 'No Date'
  1014.    else
  1015.       s := day[zeller(dt)] + 'day, ' +
  1016.             month[mo] + ' ' + intstr(dy, 2) + ', ' + intstr(yr, 4);
  1017.    if length (s) < fdslen then
  1018.       s := pad (s,' ',fdslen);
  1019.    build_full_date_str := s
  1020. end ; { function build_full_date_str }
  1021.  
  1022. { ---------------------------------------------------------------- }
  1023.  
  1024. procedure get_date;
  1025. { puts the system date in the sys_date date variable. }
  1026. var
  1027.    year,month,day,dow :word;
  1028. begin
  1029.    getdate(year,month,day,dow);
  1030.    sys_date := year;
  1031.    sys_date := (sys_date * 100) + month;
  1032.    sys_date := (sys_date * 100) + day;
  1033. end; { procedure get_date }
  1034.  
  1035. { ---------------------------------------------------------------- }
  1036.  
  1037. procedure get_time;  { gets the system time }
  1038. var
  1039.   i              : integer;
  1040.   hour, minute,
  1041.   second, sec100 : word;
  1042.   timest         :string;
  1043.   hr,mn,sc       :string[2];
  1044. begin
  1045.   gettime(hour,minute,second,sec100);
  1046.   str(hour:2,hr);
  1047.   str(minute:2,mn);
  1048.   str(second:2,sc);
  1049.   timest := hr + ':' + mn + ':' + sc;
  1050.   for i := 1 to length(timest) do if timest[i] = ' ' then timest[i] := '0';
  1051.   sys_time := timest;
  1052. end;    { function get_time }
  1053.  
  1054. { ---------------------------------------------------------------- }
  1055.  
  1056. function month(dt:longint):integer;
  1057. { returns the month portion of a date.}
  1058. var
  1059.    lo_date :integer;
  1060. begin
  1061.    lo_date := dt mod 10000;
  1062.    month := (lo_date div 100);
  1063. end; {function month }
  1064.  
  1065. { ---------------------------------------------------------------- }
  1066.  
  1067. function day(dt:longint):integer;
  1068. { returns the day from the date }
  1069. var
  1070.    lo_date :integer;
  1071. begin
  1072.    lo_date := dt mod 10000;
  1073.    day := lo_date mod 100;
  1074. end;  { function day }
  1075.  
  1076. { ---------------------------------------------------------------- }
  1077.  
  1078. function year(dt:longint;centry:boolean):integer;
  1079. { returns the year of a date.  if the centry flag is true
  1080.   returns 4 digit year otherwise returns two digit year. }
  1081. var
  1082.    hi_date,
  1083.    result  :integer;
  1084. begin
  1085.    hi_date := dt div 10000;
  1086.    if(centry) then year := hi_date
  1087.    else year := hi_date mod 100;
  1088. end; {function year}
  1089.  
  1090. { ---------------------------------------------------------------- }
  1091.  
  1092. function month_name(mon:integer):string;
  1093. { returns the month name from the month number }
  1094. const
  1095.    month: array [1..12] of string[9] =
  1096.               ('January','February','March','April','May','June','July',
  1097.                'August','September','October','November','December');
  1098. begin
  1099.    if(mon < 1) or (mon > 12) then
  1100.       month_name := 'Unknown'
  1101.    else
  1102.       month_name := month[mon];
  1103. end; {function month_name}
  1104.  
  1105. { ----- End of Date routines ------}
  1106.  
  1107. function vtp5wio:string;
  1108. { returns a string containing the version number of this package }
  1109. begin
  1110.    vtp5wio := vno;
  1111. end; {function vtp5wio}
  1112.  
  1113. { ----- Start of initialization ----- }
  1114.  
  1115. begin  {unit initialization}
  1116.    null_date := 0;
  1117.    null_date_str := 'MM/DD/YYYY' ;
  1118.    get_date;   { put todays date in sys_date }
  1119.    get_time;   { put the time in sys_time }
  1120.    { use the vidstart here before it is set to the proper value }
  1121.    for vidstart := 1 to 10 do macro[vidstart] := '';  { blank the macro strings }
  1122.    inv_flag := false;  { default to normal screen writes }
  1123.    inv_color := green; { default color for high lighted items if color monitor}
  1124.    numwindows := 0;
  1125.    reserv_wind := 0;
  1126.    regs.ah := 15;  { prepare for dos interrupt }
  1127.    intr($10,regs); { determine current video mode }
  1128.    case regs.al of
  1129.       0..3 :begin
  1130.                vidstart := $B800;  { start of color video memory }
  1131.                col_inv_flag := true;
  1132.                text_fg     := lightgray;
  1133.                text_bg     := black;
  1134.                framefgnd   := black;   {yellow;}
  1135.                framebkgnd  := black;
  1136.                title_color := black;
  1137.                err_fg      := red;
  1138.                err_bg      := green;
  1139.                msg_fg      := blue;
  1140.                msg_bg      := lightgray;
  1141.             end;
  1142.          7 :begin
  1143.                vidstart := $B000;  { start of mono video memory }
  1144.                col_inv_flag := false;
  1145.                text_fg     := lightgray;
  1146.                text_bg     := black;
  1147.                framefgnd   := lightgray;
  1148.                framebkgnd  := black;
  1149.                title_color := white;
  1150.                err_fg      := lightgray;
  1151.                err_bg      := black;
  1152.                msg_fg      := lightgray;
  1153.                msg_bg      := black;
  1154.             end;
  1155.       else vidstart := $B000;      { unknown try mono video ?? }
  1156.    end; {case}
  1157.    in_window := false; { default to not in windows }
  1158. end.  { tp5wio unit }
  1159.