A Beginners Guide To Euphoria

Euphoria And DOS, Part One

The operating system is the bridge between the program and the actual hardware of your computer. If a program can directly access operating system features, it would be able to handle tasks that would normally be beyond the scope of the programming language that it is made of. Euphoria has a set of library routines that allow access to the date and time, run DOS programs and commands, accept values from outside the program as parameters, and much more!

If you are writing programs for other people to use, the one feature people like to see most often is the date and time on the screen as they work at their computer. Euphoria has a library routine that returns the date and time to your program.

 rs = date()

date() returns a sequence value composed of eight atom elements, which is stored in receiving variable rs The sequence is the date and time on the computer, formatted in the following manner:

 {number of years since 1900, month number (where January is 1),
day of month (starting at one), hour (between 0 and 23),
minute (between 0 and 59), second (between 0 and 59),
day of the week (where Sunday is 1),
number of days since the start of the year}

If the first element of this sequence is greater than or equal to 100, then you are dealing with dates in the 21st century. For example, 101 is actually the year 2001. A demo program is available to show the system date and time in human readable form after date() is used to get them.

Demo Program 68
integer curr_year, curr_day, curr_day_of_year,
curr_hour, curr_minute, curr_second
sequence system_date, word_week, word_month, notation,
curr_day_of_week, curr_month

word_week = {"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"}

word_month = {"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"}


system_date = date()

curr_year = system_date[1]
curr_month = word_month[system_date[2]]
curr_day = system_date[3]
curr_hour = system_date[4]
curr_minute = system_date[5]
curr_second = system_date[6]
curr_day_of_week = word_week[system_date[7]]
curr_day_of_year = system_date[8]

if curr_hour >= 12 then
notation = "p.m."
else
notation = "a.m."
end if

if curr_hour > 12 then
curr_hour = curr_hour - 12
end if

if curr_hour = 0 then
curr_hour = 12
end if

puts(1, "\nHello!\n\n")
printf(1, "Today is %s, %s %d, 19%d.\n", {curr_day_of_week,
curr_month,
curr_day, curr_year})
printf(1, "The time is %.2d:%.2d:%.2d %s\n", {curr_hour, curr_minute,
curr_second, notation})
printf(1, "It is %3d days into the current year.\n", {curr_day_of_year})


As a programmer, you may be interested in time of a differenct kind, as in elapsed time taken to complete a process. This is particularly the case if you are interested in learning how long a section of code in your program takes to complete, or even to delay program execution for an interval of time. Here's the library routine that can help you do this:

   ra = time()

time returns an atom value, representing the number of seconds elapsed since a fixed point in time which can be stored in receiving variable ra. The fixed point in time time() measures against is the moment the Euphoria program started running, so executing time() at the very start of your program would return a value very close to, if not equal to, 0. To measure an interval of time, or to delay program execution for a set number of seconds, you execute time() at two different points of the program run, storing each returned value in separate atom variables, by subtracting the first returned value from the second one, you obtain a measurement in seconds between these two points.

When you look at a clock, such as your wristwatch or a wall-mounted one, you notice that it advance one second at a time. This means its measurement of time has a resolution of one second. MSDOS on the other hand has a resolution of 0.055 seconds, so its resolution is more precise. This means the smallest amount of time you can measure using time(), by default, is 0.055 seconds [Note: On Win32\Linux\FreeBSD it's about 0.01 seconds]. A demo program is available to show how to use time() as a timer.

Demo Program 69
include graphics.e
atom minutes, seconds, elapsed_seconds, halt_program
clear_screen()
position(25,1)
text_color(9)
puts(1,"Press Q To Quit Demo Or Let Run For 5 Minutes")
text_color(2)
halt_program = ' '
while halt_program != 'q' do
     halt_program = get_key()
     elapsed_seconds = time()
     minutes = elapsed_seconds / 60
     seconds = remainder(elapsed_seconds, 60)

     position(10, 30)
     printf(1, "Program run time: %.2d:%.2d", {minutes, seconds})
     if minutes >= 4 then
        if seconds >= 50 then
             if seconds >= 55 then
                  text_color(12 + 16)
             else
                  text_color(14)
             end if
        end if
     end if

     if minutes >= 5 then
          clear_screen()
          halt_program = 'q'
     end if
end while

0.055 seconds resolution is a very small fraction of time, too small to perceive, and is fine enough to serve your programming purposes. However, for programming that requires a very fine resolution of time, 0.055 seconds may be too large. If you want to measure time in even smaller steps, you have to force the operating system to fetch the time more often per second. By default, the time is checked 18.2 times per second, here is a library routine that forces more checking per second and thus giving you a finer resolution of time:

   include machine.e
   tick_rate(i)  [Note: DOS32 only]

i represents the number of interrupts per second. An interrupt is a pause where the operating system stops to go get something, such as a keystroke, or, in this case, the time. Because computers run very fast, the pause is unnoticeable. The higher i is in value, the more often the time is checked, and the finer the time resolution will become. To figure out how to get the resolution you want, simply divide one by the desired time resolution. For example, if you want to have time advance by 0.025 seconds, you divide one by 0.025, giving 40. This means the operating system must check the time 40 times a second to get a resolution of 0.025 seconds, so you issue tick_rate(40).

The time resolution always resets back to 0.055 when a Euphoria program stops running (normally or by any error). You can issue tick_rate(0) to set it back to 0.055 if you want the time resolution back to default without ending the program run. While you can set the time resolution as fine as you want, you cannot change the time resolution to something more than 0.055 seconds. If the program encounters a "causeway" error, or if your system locks up, you should reboot your computer immediately to set the time resolution back to normal, or it will run too quickly. A demo program is available to show how to properly use tick_rate()

Demo Program 70
include machine.e
atom precision_rate, ticker, seconds
precision_rate = .01
ticker = 1/precision_rate
tick_rate(ticker)
clear_screen()
position(22,1)
puts(1,"Press Any Key To End This Program")
seconds = 0
while get_key() = -1 do
     position(12,24)
     seconds = time()
     printf(1,"Program Running For %03.2f Seconds",seconds)
end while
clear_screen()

The system time isn't the only thing you can get from the operating system. You can also get directory information from the hard drive and floppy drives. Here is the library routine that can do this for you:

   include file.e
   ro = dir(s)

dir() contains information on either a single file or a directory containing files (s). The information returned is a sequence value that is stored in receiving variable ro. dir() works exactly like the DOS command DIR. If s is a directory name, a shortcut path like .. (parent directory) or . (current directory) or a wildcard filename like *.COM, the sequence returned is made up of sequence elements, where each element represents information on a file or subdirectory. If s is a filename, like DAVID.GIF, or any wildcard filename that matches only one file, the sequence returned is made up of a single sequence element, representing information on that one file. Here is the structure of the returned sequence:

   {{s1, s2, i3, i4, i5, i6, i7, i8, i9},
    {s1, s2, i3, i4, i5, i6, i7, i8, i9},...}

Each sequence element is in turn made up of 2 sequences (s1 and s2) and seven integer elements (i3 to i9). Each is explained below:

   s1 - File or directory name            s2 - Attribute(s)
   i3 - Size in bytes                     i4 - Year modified
   i5 - Month modified                    i6 - Day modified
   i7 - Hour modified                     i8 - Minute modified
   i9 - Second modified

The attributes element (s2) contains a list of single characters that describes the file or directory (s1). Each of the characters are described below:

   d - directory                          r - read only file
   h - hidden file                        s - system file
   v - volume id                          a - archive file

It's possible to have the attributes as a null sequence or {}, meaning a nonsystem file that is erasable, visible, and unchanged.

While you can pass a Windows 95 long file or directory name to dir(), the file and directory names returned are in the DOS 8.3 format. If the file or directory name passed into dir() does not exist or is invalid, the library routine will return a value of -1. A demo program is available to demonstrate the use of dir() to list directory files.

Demo program 71
include file.e
include graphics.e

sequence entry_type, format_string

integer keystroke, update, display_from, length_window, current_entry
object cur_dir_info

clear_screen()

position(21,1)
text_color(7)
puts(1,"Page Up And Page Down Keys : Scroll Data ")
position(22,1)
text_color(7)
puts(1,"Up And Down Arrow Keys: Move Highlight Line ")
position(23,1)
text_color(7)
puts(1,"ENTER Key: Get File Or Directory Attributes ")
position(24,1)
text_color(7)
puts(1,"Press Q Key To Quit Program")

position(2,1)
puts(1,
"   Object         Object     Object       Date        Time  \n")

puts(1,
"    Name           Type       Size      Modified    Modified\n")

puts(1,
"____________   ___________   _______   __________   ________\n")
format_string = "%-12s   %11s   %7d   %04d\\%02d\\%02d   %02d:%02d:%02d\n"
display_from = 1
length_window = 10
cur_dir_info = dir(".")
current_entry = 1
update = 'y'
keystroke = 0
while keystroke != 'q' do
     keystroke = get_key()

     if keystroke = 13 then
          position(18,1)
          puts(1,repeat(' ',78))
          if length(cur_dir_info[current_entry][2]) > 0 then
               position(18,1)
               text_color(7)
               puts(1,"Entry Attributes: ")
               for attribz = 1 to length(cur_dir_info[current_entry][2]) do
                    puts(1,cur_dir_info[current_entry][2][attribz])
                    puts(1, " ")
               end for
               text_color(8)
          end if
     end if

     if keystroke = 337 then
          if (display_from + length_window - 1) <
              length(cur_dir_info) then
               display_from = display_from + 1
               current_entry = display_from
               update = 'y'
          end if
     end if

     if keystroke = 329 then
          if display_from > 1 then
               display_from = display_from - 1
               current_entry = display_from
               update = 'y'
          end if
     end if

     if keystroke = 336 then
          if current_entry < length(cur_dir_info) and
               current_entry < (display_from + length_window - 1) then
               update = 'y'
               current_entry = current_entry + 1
          end if
     end if

     if keystroke = 328 then
          if current_entry > 1 and current_entry > display_from then
               update = 'y'
               current_entry = current_entry - 1
          end if
     end if

     if update = 'y' then

          update = 'n'
          position(6,1)
          for line = display_from to (display_from + (length_window-1)) do
               if line <= length(cur_dir_info) then
                    if find('d',cur_dir_info[line][2]) then
                         entry_type = "<DIRECTORY>"
                    else
                         entry_type = "  -FILE-   "
                    end if
                    if current_entry = line then
                         text_color(15)
                    else
                         text_color(8)
                    end if
                    printf(1,format_string,{cur_dir_info[line][1],
                                            entry_type,
                                            cur_dir_info[line][3],
                                            cur_dir_info[line][4],
                                            cur_dir_info[line][5],
                                            cur_dir_info[line][6],
                                            cur_dir_info[line][7],
                                            cur_dir_info[line][8],
                                            cur_dir_info[line][9]})
               else
                    puts(1,repeat(' ',78) & "\n")
               end if
          end for
     end if
end while

clear_screen()

If you want to know what directory you are currently in, you can use the following library routine:

   include file.e
   rs = current_dir()

current_dir() returns a sequence value representing the current working directory. If you were running a Euphoria program in directory "C:\STUFF", and you issue current_dir(), the value returned to receiving variable rs would be "C:\STUFF". You could then pass this sequence value to dir() to obtain directory information. A short demo is available to show how current_dir() works.

Demo program
include file.e
sequence where_am_i
where_am_i = current_dir()
puts(1,"Hello!\n")
printf(1,"This demo runs from directory %s\n",{where_am_i})


Having the ability to access all DOS commands and programs from a Euphoria program means having access to nearly all features of your computer. This gives the program incredible scope beyond the limits of the language. This library routine allows such access:

   system(s,i)

system() will pass a string, s, representing a command for DOS to execute for you. It can be DOS command like cd, dir, rename, or deltree. The string can also be the name of a program, either written in Euphoria or in another programming language. i handles the kind of return to the Euphoria program once the command is finished running:

   0 - clear the screen by restoring graphics mode of Euphoria program
   1 - beep, wait for key press, and then restore graphics mode
   2 - do not restore graphics mode

Be careful with option 2 as the choice of return. It should only be used when the graphics mode is not going to be changed by the DOS command. system() can be used to design Euphoria programs that install software, or to handle handle the programs on your computer using a flexible menu. A demo program uses system() to access the DOS command TYPE, in order to display your AUTOEXEC.BAT file.

Demo program
system("type C:\\autoexec.bat | more ",2)

We will conclude our discussion of Euphoria and DOS in the next chapter by showing how to use DOS to control the execution of a Euphoria program, and also how to end the program in more ways than one.

ToC

Euphoria And DOS, Part Two

The previous chapter showed how a Euphoria program accessed the operating system for needed resources. But it is also possible to have DOS influence the way a Euphoria program runs, such as passing values to the program upon startup, like the way you pass the drive letter to FORMAT to indicate which drive to format. You can also control the way a Euphoria program terminates, even when the termination is a result of a program error.

Just as library routines can accept parameters to process, a program can accept parameters from the user in order to operate in a certain way based on the received values. For example, when you use XCOPY to copy files from one part of your hard drive to another (or to a floppy disk), you can state if you want subdirectories to be copied as well. Even though it looks like a different kind of program is running when program parameters are used, it's really the same program running in a slightly different way. To allow your Euphoria programs to accept parameters from the MS-DOS prompt, or from the Run window in Windows, you use this library routine:

   rs = command_line()

command_line() returns the string used to start your Euphoria program, along with any parameters following the program name. This line is stored as a sequence value in receiving variable rs. The sequence value returned by command_line() is made up of sequence elements, each element representing a word in the string used to start the Euphoria program. command_line() works whether you run your Euphoria program through the interpreter EX.EXE, or as a stand-alone file .EXE created by BIND.BAT. But the returned sequence value will differ based on the method used. If the Euphoria program is run by EX.EXE, the sequence value is:

  {the EX.EXE file name (including the directory where it is stored),
   the name of your Euphoria program being run,
   the first parameter the program accepts,...}

If the Euphoria program is an .EXE file, the sequence value is:

  {the Euphoria program name (including the directory where it is stored),
   the Euphoria program name (including the directory where it is stored),
   the first parameter the program accepts,...}

When command_line() is used in a program that is stand-alone created by BIND.BAT, the first and second elements are the same value. This ensures the the parameters following the program name are in the same element positions, no matter how the Euphoria program is started. Depending on the number of parameters following the program name, the sequence value returned by command_line() can be any length in terms of elements. If no parameters are entered after the program, the smallest the sequence value can be is two elements long. It's the elements from the third position onward that you should focus attention on, as these are where the parameters are located. You can condition groups of statements to execute only when certain parameters are received by you program.

A demo program is available to show how to use command_line() in a Euphoria program, but one important note: you may view both the batch file source and the source of the Euphoria program the batch file runs and sends parameters to, but do NOT run the Euphoria program itself!

Demo program 72
sequence command_line_data

atom number_of_parameters

clear_screen()

command_line_data = command_line()

number_of_parameters = length(command_line_data) - 2

if number_of_parameters = 0 then
     puts(1, "\nPlease run the demo BATCH file to execute this program\n")
else
     printf(1, "\n%d parameter(s) were passed to this program\n\n",
            {number_of_parameters})
     for ix = 3 to length(command_line_data) do
          printf(1, "%s is parameter %d\n", {command_line_data[ix],
                                         ix-2})
     end for
end if

Here's the associated batch file:
   ex d2305a.ex cats dogs budgies mice

Another way of passing parameters to your program is by accepting the values of environment variables in DOS. You are probably familiar with the PATH variable (where the operating system searches for a program if it is not found in the current directory you are in).

   ro = getenv(s)    [Note: DOS32 only]

The value assigned to the environment variable, shown here as s, is stored as a sequence in receiving variable ro. If the environment variable has no assigned value, then a value of -1 is returned instead. Run a demo program now that gets information on the PATH variable.

Demo program 73
object path_settings
clear_screen()
path_settings = getenv("PATH")
if sequence(path_settings) then
     puts(1,
     "\nThe following directories are in your DOS PATH variable:\n\n")
     for ix = 1 to length(path_settings) do
          if path_settings[ix] = ';' then
               puts(1, "\n")
          else
               puts(1, path_settings[ix])
          end if
     end for
     puts(1, "\n\nScan completed. Have a nice day!\n")
else
     puts(1, "\nNo variable PATH found. You really should set the path\n")
     puts(1, "variable in DOS. It will allow you to run programs in\n")
     puts(1, "different directories without typing the full path name!\n")
end if


Contolling the execution of a Euphoria program includes how it terminates. So far, the programs you have seen ended after the last statement was executed. Sometimes it is necessary to halt the program when something it requires is missing (such as a file) rather than continue onward. To do this, you would either need to segment your program as conditioned groups of code, each dependant on the outcome of processing from previous groups, or you can use this very simple library routine:

   abort(i)

When executed, the Euphoria program immediately stops, and the value i is returned to the operating system. A batch file could use the returned value to proceed based on how the Euphoria program ended. While it is entirely up to you to how to assign meanings to the values returned by abort(), most programmers define 0 to mean the program ended normally. The beauty of abort() is that the program ends quickly and cleanly immediately after it is executed, no matter how deep in the program's execution you are in. It also allows the program to communicate why it ended so you know what is wrong, not to mention forcing you to design programs to be able to handle all possible conditions. A demo program shows how abort() is used in conjuction with a batch file.

Demo program 74
sequence parms, workarea

integer no_of_parms, bad_first, bad_second

parms = command_line()

no_of_parms = length(parms) - 2

if no_of_parms < 2 then
     abort(1)
else
     workarea = parms[3]
     bad_first = 0
     bad_second = 0
     for ix = 1 to length(workarea) do
          if workarea[ix] < '0' or workarea[ix] > '9' then
               bad_first = 1
               exit
          end if
     end for

     workarea = parms[4]
     for ix = 1 to length(workarea) do
          if workarea[ix] < '0' or workarea[ix] > '9' then
               bad_second = 1
               exit
          end if
     end for

     if bad_first then
          abort(2)
     end if

     if bad_second then
          abort(3)
     end if

     puts(1, parms[3] & parms[4] & "\n\n")

     abort(0)
end if

Here's the associated batch file:

@echo off

ex d2307a.ex %1 %2

if errorlevel 3 goto err3
if errorlevel 2 goto err2
if errorlevel 1 goto err1
if errorlevel 0 goto err0

:err3
echo Second parameter is non-numeric
goto finished

:err2
echo First parameter is non-numeric
goto finished

:err1
echo Two numbers required to join
goto finished

:err0
echo Program completed normally
:finished

Despite all the best planning and all the possible contingencies you have imagined, no program is perfect. There will come a time when you program will encounter an error and it will stop abruptly because of it. Normally when this happens, a file called EX.ERR is generated containing details about the problem, Also, there previous graphics mode you were in before starting the program is not restored, making characters on the screen unreadable. While a Euphoria programmmer can just look inside the EX.ERR file and find out what went wrong, suppose this program was run by a person you wrote it for? or worse, sold the program to!!! So with the person running your program staring at the screen that either has cryptic programming diagnostics, or is totally unreadable, you can believe he or she is going to be worried about what to do next. A more appropriate way is to have the program send a screen message explaining what to do next, and who to contact for assistance. The message should also be in easy-to-understand terms. Euphoria has a library routine that sets a screen message to be displayed in case of program failure:

   include machine.e
   crash_message(s)

crash_message() does not display a message, s, on the screen when it is executed. Rather, it simply tells Euphoria what to display in case a syntax error (such as an undeclared variable name) occurs, and also for errors that occur during program execution, like trying to divide by zero or using invalid element numbers. These are called run-time errors. You can format the message using special characters like "\n" or "\t", to give it a certain appearance. When an error occurs, the graphics mode is set to text mode before your message is displayed. It will appear at the top of the screen. You can issue crash_message() as many times as needed, but the message of the most recent crash_message() will be the one that appears if an error occurs. Still, you may want to issue crash_message() every time a section of your program begins running if each section requires different handling instructions.

Euphoria always generates an EX.ERR file whether or not you use crash_message(). your crash_message() should include a note to send the EX.ERR file to you for analysis. A demo program is available to show how crash_message() in a divide by zero situation.

Demo program 75
include machine.e

atom result

crash_message("**************************************\n"&
              "* An error has been encountered that *\n"&
              "* is so serious the software must    *\n"&
              "* stop running now.                  *\n"&
              "*                                    *\n"&
              "* Please Email the file ex.err to    *\n"&
              "* moggie@interlog.com. Thank you!    *\n"&
              "**************************************\n")



for ix = 100 to 0 by -1 do
    result = 100 / ix
    printf(1,"%d divided by %d gives %f\n",{100,ix,result})
end for


A final way to make DOS control the way a Euphoria program runs is by use of DOS' redirection symbols. Earlier in the tutorial, we stated that the number 0 was by default defined for keyboard input, and 1 and 2 were by default assigned for screen output. By using the following DOS redirection symbols below, the source of input (0) and destination for output (1 and 2) can be changed:

   euphoria program < input file or device

   euphoria program > output file or device

The DOS redirection symbol < means keyboard input library routines will read data from an input file or device other than the keyboard. The DOS redirection symbol > means (text) screen output library routines will send data to an output file or device other than the computer screen.

The use of the open() library routine in conjuction with input and output library routines is more effective than using the DOS redirect symbols. However, DOS redirect symbols serve as a handy "ad-hoc" way to force your program to handle input and output that do not involve the keyboard and screen. Your operating system manual contains more details on how to use redirection symbols and other DOS features to change data flow to and from your written programs.

Go to the next chapter now to learn about the binary number system and how it can be used with the Euphora programming language!

ToC