program sleep; (* * sleep -- suspend execution for a specified time * Copyright (C) 2000 Trane Francks * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * Author and Maintainer: Trane Francks * * To reach me by snail-mail, contact me at: * * Sento Hills Tsurukawa #305 * 1856-4 Kanai-cho, Machida City * Tokyo 195-0071 * JAPAN * * Comments, suggestions, and bug reports should be sent to me, preferably * by e-mail. I'd like to hear from you if you find this program useful. * Constructive criticism is nice; flames will go quietly into /dev/null. *) (* * NAME * sleep -- suspend execution for a specified time * * SYNOPSIS * sleep [--help] [--version] seconds * * DESCRIPTION * The sleep command continues running until the specified number of * seconds has elapsed. sleep can delay execution of a program or * produce periodic execution in conjunction with shell commands. * * The seconds argument can be either a number of seconds or a more * general time description of the form nhnmns, with nh, nm, and s being * optional. The maximum sleep duration is 2,147,483,647 seconds, which * works out to 596,523 hours, 14 minutes and 7 seconds. That, I think, * should be just about long enough for anybody. ;-) * * OPTIONS * sleep accepts the following options: * * --help * /? * displays help and exits successfully. When this option is * used, sleep does not suspend execution. This option cannot * be used with other options. * Note: sleep checks for the * switchar defined in DOS. If you have set a different * switchar in DOS, sleep will honour your preferred setting. * * --version * /v * displays version and exits successfully. When this option * is used, sleep does not suspend execution. This option * cannot be used with other options. * Note: sleep checks * for the switchar defined in DOS. If you have set a * different switchar in DOS, sleep will honour your preferred * setting. * * EXAMPLES * sleep 360 sleeps for 360 seconds * sleep 5m sleeps for 5 minutes * sleep 1h15m30s sleeps for 1 hour, 5 minutes, 30 seconds * * DIAGNOSTICS * Possible exit status values are: * * 0 Successful completion. * * 2 Failure because seconds value was not specified or is invalid. *) (* --------------------------------------------------------------------- *) (* Simple routines to slice the CPU in Win, OS/2, Dos, DV, and DoubleDos *) (* Written by Pete Rocca (support@mbcc.com), Freeware source code *) (* *) (* You may use this code in your programs, however please make a small *) (* mention in your documentation, or let me know that you're using it. *) (* *) (* Fido: 1:2401/305, BBS: 519-660-8981, Email: support@mbcc.com *) (* --------------------------------------------------------------------- *) uses Crt, Dos; const version = '1.0'; { program version number } DOS4 = 0; { Dos 4 and under - no slicing } DOS5 = 1; { Dos 5 and up } DV = 2; { DV 2.x and up } WINDOWS = 3; { Windows 2.x and up } DOUBLEDOS = 4; { DoubleDos 1.x and up } OS1 = 5; { OS/2 1.x and under - no slicing } OS2 = 6; { OS/2 2.x and up } var code : integer; { errorcode returned from val procedure } seconds : longint; { number of seconds for program to sleep } userInput : string; { time to sleep as entered on command line } SystemEnvironment : byte; { stores ID of operating system } switch : string; { holds uppercased userInput } counter : byte; { counter used with for loop } switchar : char; { the DOS command-switch character } function getswitchar: char; (* * gets and returns the DOS switch character *) var regs : registers; { holds packed registers for switchar } begin regs.ax := $37 shl 8; with regs do msdos(regs); getswitchar := chr(regs.dx and $00ff) end; (* BEGIN MODIFIED CODE BY PETE ROCCA *) procedure DetectEnvironment; var R : Registers; begin SystemEnvironment := DOS4; R.AX := $3000; Intr($21,R); if R.AL = 10 then SystemEnvironment := OS1 else if R.AL > 19 then SystemEnvironment := OS2 else if R.AL > 4 then SystemEnvironment := DOS5; R.AX := $E400; Intr($21,R); if (R.AL = $01) or (R.AL = $02) then SystemEnvironment := DOUBLEDOS; R.AX := $4680; Intr($2F,R); if R.AX = $0000 then SystemEnvironment := WINDOWS; R.AX := $1600; Intr($2F,R); if (R.AL <> $00) and (R.AL <> $80) then SystemEnvironment := WINDOWS; R.AX := $2B01; R.CX := $4445; R.DX := $5351; Intr($21,R); if R.AL <> $FF then SystemEnvironment := DV; end; procedure SliceTimer; begin case SystemEnvironment of DV : asm MOV AX,$1000 INT $15 end; DOS5, WINDOWS, OS2 : asm MOV AX,$1680 INT $2F end; DOUBLEDOS : asm MOV AX,$EE01 INT $21 end; OS1, DOS4: { no slicing }; end; end; (* END MODIFIED CODE BY PETE ROCCA *) function convert2seconds(userInput : string): longint; (* * Parses userInput and returns number of seconds from string. If string * contains invalid formatting or characters or the parsed value is out of * valid longint range, returns -1. *) const h = 0; m = 1; s = 2; var timeValue : longint; { current time value } seconds : longint; { total accumulated seconds } currentDigit : byte; { current digit in parsed string } numberString : string; { holds numeric string for later conversion } code : integer; { errorcode returned by val } counter : byte; { string-position counter } tempValue : longint; { temp holder for range checking } timeUnit : byte; { hours, minutes or seconds defined in const } begin { convert2seconds } timeValue := 0; seconds := 0; currentDigit := 0; numberString := ''; for counter := 1 to length(userInput) do begin { parse userInput } val(userInput[counter], currentDigit, code); if (code = 0) then { it's a number } numberString := concat(numberString, userInput[counter]) else { it's a letter } begin { determine timeUnit and calculate time } if (userInput[counter] in ['h', 'H']) then timeUnit := h else if (userInput[counter] in ['m', 'M']) then timeUnit := m else if (userInput[counter] in ['s', 'S']) then timeUnit := s else begin { invalid userInput } convert2seconds := -1; exit; end; { invalid userInput } val(numberString, timeValue, code); if (code <> 0) then { oops, we somehow got a bad value } begin { bail } convert2seconds := -1; exit; end; { bail } begin { calculation and variable reset } case timeUnit of h : begin { convert hours to seconds } tempValue := (timeValue * 60 * 60); end; { convert hours to seconds } m : begin { convert minutes to seconds } tempValue := (timeValue * 60); end; { convert minutes to seconds } s : begin { add seconds to total seconds } tempValue := timeValue; end; { add seconds to total seconds } end; { case timeUnit of } if (tempValue <= (maxLongInt - seconds)) then seconds := seconds + tempValue else begin { out of range } convert2seconds := -1; exit; end; { out of range } numberString := ''; timeValue := 0; currentDigit := 0; end; { calculation and variable reset } end; { determine timeUnit and calculate time } end; { parse userInput } if (numberString <> '') then { userInput has trailing digits } begin { convert trailing digits to seconds } val(numberString, timeValue, code); if (code <> 0) then { we got a bad value } begin { bail out } convert2seconds := -1; exit; end { bail out } else begin { check range } if (timeValue <= (maxLongInt - seconds)) then seconds := seconds + timeValue else begin { out of range } convert2seconds := -1; exit; end; { out of range } end; { check range } end; { convert trailing digits to seconds } convert2seconds := seconds; end; { convert2seconds } procedure showhelp; begin { procedure showhelp } writeln('sleep ', version); writeln('Usage: sleep [#h#m]#[s]'); writeln; writeln('Example: sleep 1h15m'); writeln(' sleep 30'); end; { procedure showhelp } procedure displayversion; (* * display version information *) begin { procedure display version } writeln('sleep ', version); writeln('Copyright (C) 2000 Trane Francks'); writeln('sleep comes with NO WARRANTY'); writeln('to the extent permitted by law.'); writeln('You may redistribute copies of sleep'); writeln('under the terms of the GNU General Public License.'); writeln('For more information about these matters,'); writeln('see the file LICENSE.TXT.'); writeln; writeln('Report bugs to "Trane Francks" '); writeln('Snail-mail address: Trane Francks'); writeln(' Sento Hills Tsurukawa #305'); writeln(' 1856-4 Kanai-cho, Machida City'); writeln(' Tokyo 195-0071'); writeln(' JAPAN'); end; { procedure display version } procedure snooze(seconds : longint); (* * This is where the sleeping takes place. Gets the time and watches to see * when the seconds change. Counter ticks for each change until number of * seconds has elapsed. Gives up time slices to OS to be friendly. *) var counter : longint; { track number of seconds already slept } hrs : word; { hours segment of current time } min : word; { minutes segment of current time } sec : word; { seconds segment of current time } hun : word; { hundreds segment of current time } oldsec : word; { store old sec segment for comparison } slice : byte; { counter for number of slices to give up } key : char; { value of key pressed } begin { snooze } gettime(hrs, min, sec, hun); counter := 0; repeat oldsec := sec; repeat gettime(hrs, min, sec, hun); for slice := 1 to 32 do SliceTimer; if keypressed then begin { process keystroke } key := readkey; if (key = #3) then { user pressed Ctrl-C } halt(130); end; { process keystroke } until (oldsec <> sec); if (oldsec = 59) then counter := counter + (sec + 1) else counter := counter + (sec - oldsec); until (counter >= seconds); end; { snooze } begin { program sleep } directVideo := false; { use BIOS screen writes } seconds := 0; detectenvironment; { determine OS so we can time-slice } switchar := getswitchar; { get command-line switch char from DOS } if (paramcount = 0) or (paramcount > 1) then begin { incorrect number of parameters } writeln('sleep: incorrect number of parameters'); showhelp; halt(2); end; { incorrect number of parameters } userInput := paramstr(1); val(userInput, seconds, code); if (code = 0) then { user entered seconds only } begin { check validity } if (seconds = 0) then halt(0) else if (seconds > 0) then snooze(seconds) else begin { invalid value } writeln('sleep: ', paramstr(1), ': invalid value'); showhelp; halt(2); end; { invalid value } end { check validity } else begin { parse userInput } { first, check to see if we're working with option switches } switch := userInput; for counter := 1 to length(switch) do switch[counter] := upcase(switch[counter]); if (switch = '--HELP') or (switch = switchar + '?') then begin { show help and exit gracefully } showhelp; halt(0); end { show help and exit gracefully } else if (switch = '--VERSION') or (switch = switchar + 'V') then begin { display version and exit gracefully } displayversion; halt(0); end; { display version and exit gracefully } seconds := convert2seconds(userInput); if (seconds = -1) then { the input string was invalid } begin { bail out finally } writeln('sleep: ', userInput, ': invalid value'); showhelp; halt(2); end { bail out finally } else if (seconds > 0) then snooze(seconds); end; { parse userInput } halt(0); end. { program sleep }