]> 4ch.mooo.com Git - 16.git/blobdiff - doc/CGUIDE_3.TXT
clear
[16.git] / doc / CGUIDE_3.TXT
diff --git a/doc/CGUIDE_3.TXT b/doc/CGUIDE_3.TXT
deleted file mode 100755 (executable)
index 23f6a6b..0000000
+++ /dev/null
@@ -1,14499 +0,0 @@
-\r
-                                    \r
-                   THE IBM PC PROGRAMMER'S GUIDE TO C\r
-                                    \r
-                                    \r
-                                    \r
-                               3rd Edition\r
-                                    \r
-                                    \r
-                                    \r
-                             Matthew Probert\r
-\r
-\r
-                                    \r
-                            COPYRIGHT NOTICE\r
-\r
-\r
-This publication remains the property of Matthew Probert. License is\r
-hereby given for this work to be freely distibuted in whole under the\r
-proviso that credit is given to the author. Sections of this work may be\r
-used and distributed without payment under the proviso that credit is\r
-given to both this work and the author. Source code occuring in this work\r
-may be used within commercial and non-commercial applications without\r
-charge and without reference to the author.\r
-\r
- BIOGRAPHICAL NOTES\r
-\r
-\r
-Matthew Probert is a software consultant working for his own firm,\r
-Servile Software. He has been involved with micro-computer software\r
-design and programming since the age of eighteen and has been involved\r
-with the C programming language for the past ten years.\r
-\r
-His educational background lies in the non-too distinguished honour of\r
-having a nervous break down during his last year of secondary school\r
-which resulted in a lack of formal qualifications. However, Matthew has\r
-made up for it by achieving a complete recovery and has been studying\r
-Psychology with particular attention to behaviourism and conversation\r
-ever since.\r
-\r
-His chequered career spans back twelve years during which time he has\r
-trained people in the design and use of database management applications,\r
-been called upon to design and implement structured methodologies and has\r
-been a "good old fashioned" analyst programmer.\r
-\r
-Matthew Probert is currently researching Artificial Intelligence with\r
-particular reference to the application of natural language processing,\r
-whereby a computer software package may decode written human language and\r
-respond to it in an intelligent manner. He is also monitoring the\r
-progress of facilitated communication amongst autistic and children with\r
-severe learning and challenging behaviour and hopes one day to be able to\r
-develope a computer based mechanism for true and reliable communication\r
-between autistic people and the rest of society.\r
-\r
-\r
-Matthew Probert can be contacted via\r
-     Servile Software\r
-     5 Longcroft Close\r
-     Basingstoke\r
-     Hampshire\r
-     RG21 8XG\r
-     England\r
-\r
-     Telephone 01256 478576\r
-\r
-                                    \r
-                                 PREFACE\r
-\r
-In 1992, an English software house, Servile Software published a paper\r
-entitled "HOW TO C", which sought to introduce computer programmers to\r
-the C programming language. That paper was written by Matthew Probert. A\r
-follow up effort was "HOW TO CMORE", a document that was also published\r
-by Servile Software. Now those two documents have been amalgamated and\r
-thoroughly revamped to create this book. I have included loads of new\r
-source code that can be lifted directly out of the text.\r
-\r
-All the program listings have been typed in to the Turbo C compiler,\r
-compiled and executed successfully before being imported into this\r
-document.\r
-\r
-I hope you enjoy my work, and more I hope that you learn to program in C.\r
-It really is a great language, there can be no other language that gives\r
-the computer the opportunity to live up to the old saying;\r
-\r
-\r
-"To err is human, to make a complete balls up requires a computer!"\r
-\r
-\r
-\r
-Warning!\r
-\r
-This document is the result of over ten years experience as a software\r
-engineer. This document contains professional source code that is not\r
-intended for beginers.\r
-\r
-\r
-                                    \r
-                              INTRODUCTION\r
-\r
-\r
-The major distinguishing features of the C programming language are;\r
-\r
- ú    block-structured flow-control constructs (typical of most high-level\r
-      languages);\r
- ú    freedom to manipulate basic machine objects (eg: bytes) and to refer\r
-      to them using any particular object view desired (typical of assembly-\r
-      languages);\r
- ú    both high-level operations (eg: floating-point arithmetic) and low-\r
-      level operations (which map closely onto machine-language instructions,\r
-      thereby offering the means to code in an optimal, yet portable, manner).\r
-\r
-This book sets out to describe the C programming language, as commonly\r
-found with compilers for the IBM PC, to enable a computer programmer with\r
-no previous knowledge of the C programming language to program in C using\r
-the IBM PC including the ROM facilities provided by the PC and facilities\r
-provided DOS.\r
-\r
-It is assumed that the reader has access to a C compiler, and to the\r
-documentation that accompanies it regarding library functions.\r
-\r
-The example programs were written with Borland's Turbo C, most of the non-\r
-standard facilities provided by Turbo C should be found in later releases\r
-of Microsoft C.\r
-\r
-\r
-\r
-Differences Between the Various Versions of C\r
-\r
-The original C (prior to the definitive book by K&R) defined the\r
-combination assignment operators (eg: +=, *=, etc.) backwards (ie: they\r
-were written =+, =*, etc.).  This caused terrible confusion when a\r
-statement such as\r
-\r
- x=-y;\r
-was compiled - it could have meant\r
-\r
- x = x - y;\r
-or\r
-\r
- x = (-y);\r
-Ritchie soon spotted this ambiguity and changed the language to have\r
-these operators written in the now-familiar manner (+=, *=, etc.).\r
-\r
-The major variations, however, are between K&R C and ANSI C.  These can\r
-be summarized as follows:\r
-\r
- ú    introduction of function prototypes in declarations; change of\r
-      function definition preamble to match the style of prototypes;\r
- ú    introduction of the ellipsis ("...") to show variable-length\r
-      function argument lists;\r
- ú    introduction of the keyword `void' (for functions not returning a\r
-      value) and the type `void *' for generic pointer variables;\r
- ú    addition of string-merging, token-pasting and stringizing functions\r
-      in the preprocessor;\r
- ú    addition of trigraph translation in the preprocessor;\r
- ú    addition of the `#pragma' directive and formalization of the\r
-      `declared()' pseudofunction in the preprocessor;\r
- ú    introduction of multi-byte strings and characters to support non-\r
-      English languages;\r
- ú    introduction of the `signed' keyword (to complement the `unsigned'\r
-      keyword when used in integer declarations) and the unary plus (`+')\r
-      operator.\r
-\r
-\r
-\r
-C is a medium level language\r
-\r
-The powerful facilities offered by C to allow manipulation of direct\r
-memory addresses and data, even down to the bit level, along with C's\r
-structured approach to programming cause C to be classified as a "medium\r
-level" programming language. It possesses fewer ready made facilities\r
-than a high level language, such as BASIC, but a higher level of\r
-structure than low level Assembler.\r
-\r
-\r
-Key words\r
-\r
-The original C language as described in; "The C programming language", by\r
-Kernighan and Ritchie, provided 27 key words. To those 27 the ANSI\r
-standards committee on C have added five more. This confusingly results\r
-in two standards for the C language. However, the ANSI standard is\r
-quickly taking over from the old K & R standard.\r
-\r
-\r
-The 32 C key words are;\r
-\r
-auto              double             int                struct\r
-break             else               long               switch\r
-case              enum               register           typedef\r
-char              extern             return             union\r
-const             float              short              unsigned\r
-continue          for                signed             void\r
-default           goto               sizeof             volatile\r
-do                if                 static             while\r
-\r
-Some C compilers offer additional key words specific to the hardware\r
-environment that they operate on. You should be aware of your own C\r
-compilers additional key words. Most notably on the PC these are;\r
-\r
-\r
-near      far        huge\r
-\r
-\r
-\r
-Structure\r
-\r
-C programs are written in a structured manner. A collection of code\r
-blocks are created that call each other to comprise the complete program.\r
-As a structured language C provides various looping and testing commands\r
-such as;\r
-\r
-\r
-           do-while,  for, while, if\r
-\r
-and the use of jumps, while provided for, are rarely used.\r
-\r
-A C code block is contained within a pair of curly braces "{ }", and may\r
-be a complete procedure, in C terminology called a "function", or a\r
-subset of code within a function. For example the following is a code\r
-block. The statements within the curly braces are only executed upon\r
-satisfaction of the condition that "x < 10";\r
-\r
-\r
-if (x < 10)\r
-{\r
-     a = 1;\r
-     b = 0;\r
-}\r
-\r
-while this, is a complete function code block containing a sub code block\r
-as a do-while loop;\r
-\r
-int GET_X()\r
-{\r
-     int x;\r
-\r
-     do\r
-     {\r
-          printf("\nEnter a number between 0 and 10 ");\r
-          scanf("%d",&x);\r
-     }\r
-     while(x < 0 || x > 10);\r
-     return(x);\r
-}\r
-\r
-Notice how every statement line is terminated in a semicolon, unless that\r
-statement marks the start of a code block, in which case it is followed\r
-by a curly brace. C is a case sensitive but free flow language, spaces\r
-between commands are ignored, and therefore the semicolon delimiter is\r
-required to mark the end of the command line.\r
-\r
-Having a freeflow structure the following commands are recognised as the\r
-same by the C compiler;\r
-\r
-\r
-     x = 0;\r
-     x      =0;\r
-     x=0;\r
-\r
-\r
-The general form of a C program is as follows;\r
-\r
-     compiler preprocessor statements\r
-     global data declarations\r
-     \r
-     \r
-     \r
-     \r
-     return-type main(parameter list)\r
-     {\r
-          statements\r
-     }\r
-     \r
-     return-type f1(parameter list)\r
-     {\r
-          statements\r
-     }\r
-     \r
-     return-type f2(parameter list)\r
-     {\r
-          statements\r
-     }\r
-     .\r
-     .\r
-     .\r
-     return-type fn(parameter list)\r
-     {\r
-          statements\r
-     }\r
-     \r
-\r
-\r
-Comments\r
-\r
-C allows comments to be included in the program. A comment line is\r
-defined by being enclosed within "/*" and "*/". Thus the following is a\r
-comment;\r
-\r
-\r
-/* This is a legitimate C comment line */\r
-\r
-\r
-Libraries\r
-\r
-C programs are compiled and combined with library functions provided with\r
-the C compiler. These libraries are of generally standard functions, the\r
-functionality of which are defined in the ANSI standard of the C\r
-language, but are provided by the individual C compiler manufacturers to\r
-be machine dependant. Thus, the standard library function "printf()"\r
-provides the same facilities on a DEC VAX as on an IBM PC, although the\r
-actual machine language code in the library is quite different for each.\r
-The C programmer however, does not need to know about the internals of\r
-the libraries, only that each library function will behave in the same\r
-way on any computer.\r
-\r
-\r
-                                    \r
-                               DATA TYPES\r
-\r
-\r
-There are four basic types of data in the C language; character, integer,\r
-floating point, and valueless that are referred to by the C key words;\r
-\r
-"char", "int", "float" and "void" respectively.\r
-\r
-To the basic data types may be added the type modifiers; signed,\r
-unsigned, long and short to produce further data types. By default data\r
-types are assumed signed, and the signed modifier is rarely used, unless\r
-to overide a compiler switch defaulting a data type to unsigned.\r
-\r
-The size of each data type varies from one hardware platform to another,\r
-but the least range of values that can be held is described in the ANSI\r
-standard as follows;\r
-\r
-\r
-Type                     Size                     Range\r
-                                                  \r
-char                     8                        -127 to 127\r
-unsigned char            8                        0 to 255\r
-int                      16                       -32767 to 32767\r
-unsigned int             16                       0 to 65535\r
-long int                 32                       -2147483647 to\r
-                                                  2147483647\r
-unsigned long int        32                       0 to 4294967295\r
-float                    32                       Six digit precision\r
-double                   64                       Ten digit precision\r
-long double              80                       Ten digit precision\r
-\r
-\r
-In practice, this means that the data type `char' is particularly\r
-suitable for storing flag type variables, such as status codes, which\r
-have a limited range of values. The `int' data type can be used, but if\r
-the range of values does not exceed 127 (or 255 for an unsigned char),\r
-then each declared variable would be wasting storage space.\r
-\r
-Which real number data type to use: `float', `double' or `long double' is\r
-another tricky question. When numeric accuracy is required, for example\r
-in an accounting application, the instinct would be to use the `long\r
-double', but this requires at least 10 bytes of storage space for each\r
-variable. And real numbers are not as precise as integers anyway, so\r
-perhaps one should use integer data types instead and work around the\r
-problem. The data type `float' is worse than useless since its six digit\r
-precision is too inaccurate to be relied upon. Generally, then, you\r
-should use integer data types where ever possible, but if real numbers\r
-are required use at least a `double'.\r
-\r
-\r
-Declaring a variable\r
-\r
-All variables in a C program must be declared before they can be used.\r
-The general form of a variable definition is;\r
-\r
-\r
-     type name;\r
-\r
-So, for example to declare a variable "x", of data type "int" so that it\r
-may store a value in the range -32767 to 32767, you use the statement;\r
-\r
-\r
-     int x;\r
-\r
-Character strings may be declared, which are in reality arrays of\r
-characters. They are declared as follows;\r
-\r
-\r
-     char name[number_of_elements];\r
-\r
-So, to declare a string thirty characters long, and called `name' you\r
-would use the declaration;\r
-\r
-\r
-     char name[30];\r
-\r
-\r
-Arrays of other data types also may be declared in one, two or more\r
-dimensions in the same way. For example to declare a two dimensional\r
-array of integers;\r
-\r
-\r
-     int x[10][10];\r
-\r
-The elements of this array are then accessed as;\r
-\r
-     x[0][0]\r
-     x[0][1]\r
-     x[n][n]\r
-\r
-There are three levels of access to variable; local, module and global. A\r
-variable declared within a code block is only known to the statements\r
-within that code block. A variable declared outside any function code\r
-blocks but prefixed with the storage modifier "static" is known only to\r
-the statements within that source module. A variable declared outside any\r
-functions and not prefixed with the static storage type modifier may be\r
-accessed by any statement within any source module of the program.\r
-\r
-\r
-For example;\r
-\r
-     int error;\r
-     static int a;\r
-     \r
-     main()\r
-     {\r
-          int x;\r
-          int y;\r
-     \r
-     }\r
-     \r
-     funca()\r
-     {\r
-          /* Test variable 'a' for equality with 0 */\r
-          if (a == 0)\r
-          {\r
-               int b;\r
-               for(b = 0; b < 20; b++)\r
-                    printf("\nHello World");\r
-          }\r
-     \r
-     }\r
-     \r
-\r
-In this example the variable `error' is accessible by all source code\r
-modules compiled together to form the finished program. The variable `a'\r
-is accessible by statements in both functions `main()' and `funca()', but\r
-is invisible to any other source module. Variables `x' and `y' are only\r
-accessible by statements within function `main()'. The variable `b' is\r
-only accessible by statements within the code block following the `if'\r
-statement.\r
-\r
-If a second source module wished to access the variable `error' it would\r
-need to declare `error' as an `extern' global variable thus;\r
-\r
-\r
-     extern int error;\r
-     \r
-     funcb()\r
-     {\r
-     }\r
-     \r
-C will quite happily allow you, the programmer, to assign different data\r
-types to each other. For example, you may declare a variable to be of\r
-type `char' in which case a single byte of data will be allocated to\r
-store the variable. To this variable you can attempt to allocate larger\r
-values, for example;\r
-\r
-\r
-     main()\r
-     {\r
-     \r
-          x = 5000;\r
-     \r
-     }\r
-     \r
-In this example the variable `x' can only store a value between -127 and\r
-128, so the figure 5000 will NOT be assigned to the variable `x'. Rather\r
-the value 136 will be assigned!\r
-\r
-\r
-Often you may wish to assign different data types to each other, and to\r
-prevent the compiler from warning you of a possible error you can use a\r
-cast to tell the compiler that you know what you're doing. A cast\r
-statement is a data type in parenthesis preceding a variable or\r
-expression;\r
-\r
-\r
-     main()\r
-     {\r
-          float x;\r
-          int y;\r
-     \r
-          x = 100 / 25;\r
-     \r
-          y = (int)x;\r
-     }\r
-\r
-In this example the (int) cast tells the compiler to convert the value of\r
-the floating point variable x to an integer before assigning it to the\r
-variable y.\r
-\r
-\r
-\r
-Formal parameters\r
-\r
-A C function may receive parameters from a calling function. These\r
-parameters are declared as variables within the parentheses of the\r
-function name, thus;\r
-\r
-\r
-     int MULT(int x, int y)\r
-     {\r
-          /* Return parameter x multiplied by parameter y */\r
-          return(x * y);\r
-     }\r
-     \r
-     main()\r
-     {\r
-          int a;\r
-          int b;\r
-          int c;\r
-     \r
-          a = 5;\r
-          b = 7;\r
-          c = MULT(a,b);\r
-     \r
-          printf("%d multiplied by %d equals %d\n",a,b,c);\r
-     }\r
-     \r
-\r
-Access modifiers\r
-\r
-There are two access modifiers; `const' and `volatile'. A variable\r
-declared to be `const' may not be changed by the program, whereas a\r
-variable declared as type  as type `volatile' may be changed by the\r
-program. In addition, declaring a variable to be volatile prevents the C\r
-compiler from allocating the variable to a register, and reduces the\r
-optimization carried out on the variable.\r
-\r
-\r
-\r
-Storage class types\r
-C provides four storage types; `extern', `static', `auto' and `register'.\r
-\r
-The extern storage type is used to allow a source module within a C\r
-program to access a variable declared in another source module.\r
-\r
-Static variables are only accessible within the code block that declared\r
-them, and additionally if the variable is local, rather than global, they\r
-retain their old value between subsequent calls to the code block.\r
-\r
-Register variables are stored within CPU registers where ever possible,\r
-providing the fastest possible access to their values.\r
-\r
-The auto type variable is only used with local variables, and declares\r
-the variable to retain its value locally only. Since this is the default\r
-for local variables the auto storage type is very rarely used.\r
-                                    \r
-                                    \r
-                                    \r
-                                OPERATORS\r
-\r
-Operators are tokens that cause a computation to occur when applied to\r
-variables. C provides the following operators;\r
-\r
-\r
-&                  Address\r
-*                  Indirection\r
-+                  Unary plus\r
--                  Unary minus\r
-~                  Bitwise compliment\r
-!                  Logical negation\r
-++                 As a prefix;\r
-                   preincrement\r
-                   As a suffix;\r
-                   postincrement\r
---                 As a prefix;\r
-                   predecrement\r
-                   As a suffix;\r
-                   postdecrement\r
-+                  Addition\r
--                  Subtraction\r
-*                  Multiply\r
-/                  Divide\r
-%                  Remainder\r
-<<                 Shift left\r
->>                 Shift right\r
-&                  Bitwise AND\r
-|                  Bitwise OR\r
-^                  Bitwise XOR\r
-&&                 Logical AND\r
-||                 Logical OR\r
-=                  Assignment\r
-*=                 Assign product\r
-/=                 Assign quotient\r
-%=                 Assign remainder\r
-                   (modulus)\r
-+=                 Assign sum\r
--=                 Assign difference\r
-<<=                Assign left shift\r
->>=                Assign right shift\r
-&=                 Assign bitwise AND\r
-|=                 Assign bitwise OR\r
-^=                 Assign bitwise XOR\r
-<                  Less than\r
->                  Greater than\r
-<=                 Less than or equal\r
-                   to\r
->=                 Greater than or\r
-                   equal to\r
-==                 Equal to\r
-!=                 Not equal to\r
-.                  Direct component\r
-                   selector\r
-->                 Indirect component\r
-                   selector\r
-a ? x:y            "If a is true then\r
-                   x else y"\r
-[]                 Define arrays\r
-()                 Parenthesis\r
-                   isolate conditions\r
-                   and expressions\r
-...                Ellipsis are used\r
-                   in formal\r
-                   parameter lists of\r
-                   function\r
-                   prototypes to show\r
-                   a variable number\r
-                   of\r
-                   parameters or\r
-                   parameters of\r
-                   varying types.\r
-\r
-To illustrate some more commonly used operators consider the following\r
-short program;\r
-\r
-\r
-     main()\r
-     {\r
-          int a;\r
-          int b;\r
-          int c;\r
-          a = 5;           /* Assign a value of 5 to variable 'a' */\r
-          b = a / 2;       /* Assign the value of 'a' divided by two to\r
-     variable 'b' */\r
-          c = b * 2;       /* Assign the value of 'b' multiplied by two\r
-     to variable 'c' */\r
-     \r
-          if (a == c)     /* Test if 'a' holds the same value as 'c' */\r
-     \r
-               puts("Variable 'a' is an even number");\r
-          else\r
-               puts("Variable 'a' is an odd number");\r
-     }\r
-     \r
-     \r
-Normally when incrementing the value of a variable you would write\r
-something like;\r
-\r
-          x = x + 1\r
-\r
-C provides the incremental operator '++' as well so that you can write;\r
-\r
-          x++\r
-\r
-Similarly you can decrement the value of a variable using '--' as;\r
-\r
-          x--\r
-\r
-All the other mathematical operators may be used the same, so in a C\r
-program you can write in shorthand;\r
-\r
-\r
-NORMAL                               C\r
-                                     \r
-x = x + 1                            x++\r
-x = x - 1                            x--\r
-x = x * 2                            x *= 2\r
-x = x / y                            x /= y\r
-x = x % 5                            x %= 5\r
-\r
-and so on.\r
-                                    \r
-                                    \r
-                                    \r
-                                FUNCTIONS\r
-\r
-Functions are the source code procedures that comprise a C program. They\r
-follow the general form;\r
-\r
-     return_type function_name(parameter_list)\r
-     {\r
-          statements\r
-     }\r
-\r
-\r
-The return_type specifies the data type that will be returned by the\r
-function; char, int, double, void &c.\r
-\r
-The code within a C function is invisible to any other C function, and\r
-jumps may not be made from one function into the middle of another,\r
-although functions may call other functions. Also, functions cannot be\r
-defined within functions, only within source modules.\r
-\r
-Parameters may be passed to a function either by value, or by reference.\r
-If a parameter is passed by value, then only a copy of the current value\r
-of the parameter is passed to the function. A parameter passed by\r
-reference however, is a pointer to the actual parameter that may then be\r
-changed by the function.\r
-\r
-\r
-The following example passes two parameters by value to a function,\r
-funca(), which attempts to change the value of the variables passed to\r
-it. And then passes the same two parameters by reference to funcb() which\r
-also attempts to modify their values.\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     int funca(int x, int y)\r
-     {\r
-          /* This function receives two parameters by value, x and y */\r
-     \r
-          x = x * 2;\r
-          y = y * 2;\r
-     \r
-          printf("\nValue of x in funca() %d value of y in funca()\r
-     %d",x,y);\r
-     \r
-          return(x);\r
-     }\r
-     \r
-     \r
-     int funcb(int *x, int *y)\r
-     {\r
-          /* This function receives two parameters by reference, x and y\r
-     */\r
-     \r
-          *x = *x * 2;\r
-          *y = *y * 2;\r
-     \r
-          printf("\nValue of x in funcb() %d value of y in funcb()\r
-     %d",*x,*y);\r
-     \r
-          return(*x);\r
-     }\r
-     \r
-     main()\r
-     {\r
-          int x;\r
-          int y;\r
-          int z;\r
-     \r
-          x = 5;\r
-          y = 7;\r
-     \r
-          z = funca(x,y);\r
-          z = funcb(&x,&y);\r
-     \r
-          printf("\nValue of x %d value of y %d value of z %d",x,y,z);\r
-     }\r
-     \r
-\r
-Actually funcb() does not change the values of the parameters it\r
-receives.  Rather it changes the contents of the memory addresses pointed\r
-to by the received parameters. While funca() receives the values of\r
-variables `x' and `y' from function main(), funcb() receives the memory\r
-addresses of the variables `x' and `y' from function main().\r
-\r
-\r
-\r
-Passing an array to a function\r
-\r
-The following program passes an array to a function, funca(), which\r
-initialises the array elements;\r
-\r
-     #include <stdio.h>\r
-     \r
-     void funca(int x[])\r
-     {\r
-          int n;\r
-     \r
-          for(n = 0; n < 100; n++)\r
-          x[n] = n;\r
-     }\r
-     \r
-     main()\r
-     {\r
-          int array[100];\r
-          int counter;\r
-     \r
-          funca(array);\r
-     \r
-          for(counter = 0; counter < 100; counter++)\r
-               printf("\nValue of element %d is\r
-     %d",counter,array[counter]);\r
-     }\r
-\r
-The parameter of funca() `int x[]' is declared to be an array of any\r
-length.  This works because the compiler passes the address of the start\r
-of the array parameter to the function, rather than the value of the\r
-individual elements.  This does of course mean that the function can\r
-change the value of the array elements. To prevent a function from\r
-changing the values you can specify the parameter as type `const';\r
-\r
-\r
-     funca(const int x[])\r
-     {\r
-     }\r
-\r
-This will then generate a compiler error at the line that attempts to\r
-write a value to the array. However, specifying a parameter to be const\r
-does not protect the parameter from indirect assignment as the following\r
-program illustrates;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     int funca(const int x[])\r
-     {\r
-          int *ptr;\r
-          int n;\r
-     \r
-          /* This line gives a 'suspicious pointer conversion warning' */\r
-          /* because x is a const pointer, and ptr is not */\r
-          ptr = x;\r
-     \r
-          for(n = 0; n < 100; n++)\r
-          {\r
-               *ptr = n;\r
-               ptr++;\r
-          }\r
-     }\r
-     \r
-     main()\r
-     {\r
-          int array[100];\r
-          int counter;\r
-     \r
-          funca(array);\r
-     \r
-          for(counter = 0; counter < 100; counter++)\r
-               printf("\nValue of element %d is\r
-     %d",counter,array[counter]);\r
-     }\r
-     \r
-\r
-\r
-Passing parameters to main()\r
-\r
-C allows parameters to be passed from the operating system to the program\r
-when it starts executing through two parameters; argc and argv[], as\r
-follows;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     main(int argc, char *argv[])\r
-     {\r
-          int n;\r
-     \r
-          for(n = 0; n < argc; n++)\r
-          printf("\nParameter %d equals %s",n,argv[n]);\r
-     }\r
-     \r
-\r
-Parameter argc holds the number of parameters passed to the program, and\r
-the array argv[] holds the addresses of each parameter passed. argv[0] is\r
-always the program name. This feature may be put to good use in\r
-applications that need to access system files. Consider the following\r
-scenario:\r
-\r
-A simple database application stores its data in a single file called\r
-"data.dat". The application needs to be created so that it may be stored\r
-in any directory on either a floppy diskette or a hard disk, and executed\r
-both from within the host directory and through a DOS search path. To\r
-work correctly the application must always know where to find the data\r
-file; "data.dat". This is solved by assuming that the data file will be\r
-in the same directory as the executable module, a not unreasonable\r
-restriction to place upon the operator. The following code fragment then\r
-illustrates how an application may apply this algorithm into practice to\r
-be always able to locate a desired system file:\r
-\r
-\r
-     #include <string.h>\r
-     \r
-     char system_file_name[160];\r
-     \r
-     void main(int argc,char *argv[])\r
-     {\r
-          char *data_file = "DATA.DAT";\r
-          char *p;\r
-     \r
-          strcpy(system_file_name,argv[0]);\r
-          p = strstr(system_file_name,".EXE");\r
-          if (p == NULL)\r
-          {\r
-               /* The executable is a .COM file */\r
-             p = strstr(system_file_name,".COM");\r
-          }\r
-     \r
-          /* Now back track to the last '\' character in the file name */\r
-          while(*(p - 1) != '\\')\r
-               p--;\r
-     \r
-          strcpy(p,data_file);\r
-     }\r
-\r
-In practice this code creates a string in system_file_name that is\r
-comprised of path\data.dat, so if for example the executable file is\r
-called "test.exe" and resides in the directory \borlandc, then\r
-system_file_name will be assigned with: \borlandc\data.dat\r
-\r
-\r
-\r
-Returning from a function\r
-\r
-The command `return' is used to return immediately from a function. If\r
-the function was declared with a return data type, then return should be\r
-used with a parameter of the same data type.\r
-\r
-\r
-\r
-Function prototypes\r
-\r
-Prototypes for functions allow the C compiler to check that the type of\r
-data being passed to and from functions is correct. This is very\r
-important to prevent data overflowing its allocated storage space into\r
-other variables areas.\r
-\r
-A function prototype is placed at the beginning of the program, after any\r
-preprocessor commands, such as #include <stdio.h>, and before the\r
-declaration of any functions.\r
-                                    \r
-                           THE C PREPROCESSOR\r
-\r
-C allows for commands to the compiler to be included in the source code.\r
-These commands are then called preprocessor commands and are defined by\r
-the ANSI standard to be;\r
-\r
-\r
-    #if\r
-    #ifdef\r
-    #ifndef\r
-    #else\r
-    #elif\r
-    #include\r
-    #define\r
-    #undef\r
-    #line\r
-    #error\r
-    #pragma\r
-\r
-All preprocessor commands start with a hash symbol, "#", and must be on a\r
-line on their own (although comments may follow).\r
-\r
-\r
-\r
-#define\r
-\r
-The #define command specifies an identifier and a string that the\r
-compiler will substitute every time it comes accross the identifier\r
-within that source code module. For example;\r
-\r
-\r
-#define FALSE 0\r
-#define TRUE !FALSE\r
-\r
-The compiler will replace any subsequent occurence of `FALSE' with `0'\r
-and any subsequent occurence of `TRUE' with `!0'. The substitution does\r
-NOT take place if the compiler finds that the identifier is enclosed by\r
-quotation marks, so\r
-\r
-\r
-     printf("TRUE");\r
-\r
-would NOT be replaced, but\r
-\r
-     printf("%d",FALSE);\r
-\r
-would be.\r
-\r
-The #define command also can be used to define macros that may include\r
-parameters. The parameters are best enclosed in parenthesis to ensure\r
-that correct substitution occurs.\r
-\r
-\r
-This example declares a macro `larger()' that accepts two parameters and\r
-returns the larger of the two;\r
-\r
-     #include <stdio.h>\r
-     \r
-     #define larger(a,b) (a > b) ? (a) : (b)\r
-     \r
-     int main()\r
-     {\r
-          printf("\n%d is largest",larger(5,7));\r
-     \r
-     }\r
-\r
-\r
-#error\r
-\r
-The #error command causes the compiler to stop compilation and to display\r
-the text following the #error command. For example;\r
-\r
-\r
-#error REACHED MODULE B\r
-\r
-will cause the compiler to stop compilation and display;\r
-\r
-     REACHED MODULE B\r
-\r
-\r
-\r
-#include\r
-\r
-The #include command tells the compiler to read the contents of another\r
-source file. The name of the source file must be enclosed either by\r
-quotes or by angular brackets thus;\r
-\r
-\r
-     #include "module2.c"\r
-     #include <stdio.h>\r
-\r
-Generally, if the file name is enclosed in angular brackets, then the\r
-compiler will search for the file in a directory defined in the\r
-compiler's setup. Whereas if the file name is enclosed in quotes then the\r
-compiler will look for the file in the current directory.\r
-\r
-\r
-\r
-#if, #else, #elif, #endif\r
-\r
-The #if set of commands provide conditional compilation around the\r
-general form;\r
-\r
-     #if constant_expression\r
-          statements\r
-     #else\r
-          statements\r
-     #endif\r
-\r
-#elif stands for '#else if' and follows the form;\r
-\r
-     #if expression\r
-          statements\r
-     #elif expression\r
-          statements\r
-     #endif\r
-\r
-\r
-\r
-#ifdef, #ifndef\r
-\r
-These two commands stand for '#if defined' and '#if not defined'\r
-respectively and follow the general form;\r
-\r
-     #ifdef macro_name\r
-          statements\r
-     #else\r
-          statements\r
-     #endif\r
-\r
-     #ifndef macro_name\r
-          statements\r
-     #else\r
-          statements\r
-     #endif\r
-\r
-where 'macro_name' is an identifier declared by a #define statement.\r
-\r
-\r
-\r
-#undef\r
-\r
-Undefines a macro previously defined by #define.\r
-\r
-\r
-#line\r
-\r
-Changes the compiler declared global variables __LINE__ and __FILE__. The\r
-general form of #line is;\r
-\r
-     #line number "filename"\r
-\r
-where number is inserted into the variable '__LINE__' and 'filename' is\r
-assigned to '__FILE__'.\r
-\r
-\r
-#pragma\r
-\r
-This command is used to give compiler specific commands to the compiler.\r
-The compiler's manual should give you full details of any valid options\r
-to go with the particular implementation of #pragma that it supports.\r
-                                    \r
-                       PROGRAM CONTROL STATEMENTS\r
-\r
-As with any computer language, C includes statements that test the\r
-outcome of an expression. The outcome of the test is either TRUE or\r
-FALSE. The C language defines a value of TRUE as non-zero, and FALSE as\r
-zero.\r
-\r
-\r
-\r
-Selection statements\r
-\r
-The general purpose selection statement is "if" that follows the general\r
-form;\r
-\r
-          if (expression)\r
-               statement\r
-          else\r
-               statement\r
-\r
-Where "statement" may be a single statement, or a code block enclosed in\r
-curly braces. The "else" is optional. If the result of the expression\r
-equates to TRUE, then the statement(s) following the if() will be\r
-evaluated.  Otherwise the statement(s) following the else, if there is\r
-one, will be evaluated.\r
-\r
-\r
-An alternative to the if....else combination is the ?: command that takes\r
-the form;\r
-\r
-\r
-           expression ? true_expression : false_expression\r
-\r
-Where if the expression evaluates to TRUE, then the true_expression will\r
-be evaluated, otherwise the false_expression will be evaluated. Thus we\r
-get;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     main()\r
-     {\r
-          int x;\r
-     \r
-          x = 6;\r
-     \r
-          printf("\nx is an %s number", x % 2 == 0 ? "even" : "odd");\r
-     }\r
-     \r
-C also provides a multiple branch selection statement, switch, which\r
-successively tests a value of an expression against a list of values and\r
-branches program execution to the first match found. The general form of\r
-switch is;\r
-\r
-\r
-     switch (expression)\r
-     {\r
-          case value1 :  statements\r
-                    break;\r
-          case value2 :  statements\r
-                    break;\r
-          .\r
-          .\r
-          .\r
-          .\r
-          case valuen :  statements\r
-                    break;\r
-          default :      statements\r
-     }\r
-\r
-The break statement is optional, but if omitted, program execution will\r
-continue down the list.\r
-\r
-     #include <stdio.h>\r
-     \r
-     main()\r
-     {\r
-          int x;\r
-     \r
-          x = 6;\r
-     \r
-          switch(x)\r
-          {\r
-               case 0 : printf("\nx equals zero");\r
-                     break;\r
-               case 1 : printf("\nx equals one");\r
-                     break;\r
-               case 2 : printf("\nx equals two");\r
-                     break;\r
-               case 3 : printf("\nx equals three");\r
-                     break;\r
-               default : printf("\nx is larger than three");\r
-          }\r
-     }\r
-\r
-Switch statements may be nested within one another. This is a\r
-particularly useful feature for confusing people who read your source\r
-code!\r
-\r
-\r
-Iteration statements\r
-C provides three looping or iteration statements; for, while, and do-\r
-while. The for loop has the general form;\r
-\r
-\r
-     for(initialization;condition;increment)\r
-\r
-and is useful for counters such as in this example that displays the\r
-entire ascii character set;\r
-\r
-     #include <stdio.h>\r
-     \r
-     main()\r
-     {\r
-          int x;\r
-     \r
-          for(x = 32; x < 128; x++)\r
-               printf("%d\t%c\t",x,x);\r
-     }\r
-     \r
-An infinite for loop is also quite valid;\r
-\r
-     for(;;)\r
-     {\r
-          statements\r
-     }\r
-\r
-Also, C allows empty statements. The following for loop removes leading\r
-spaces from a string;\r
-\r
-     for(; *str == ' '; str++)\r
-          ;\r
-\r
-Notice the lack of an initializer, and the empty statement following the\r
-loop.\r
-\r
-The while loop is somewhat simpler than the for loop and follows the\r
-general form;\r
-\r
-     while (condition)\r
-          statements\r
-\r
-The statement following the condition, or statements enclosed in curly\r
-braces will be executed until the condition is FALSE. If the condition is\r
-false before the loop commences, the loop statements will not be\r
-executed. The do-while loop on the other hand is always executed at least\r
-once. It takes the general form;\r
-\r
-\r
-     do\r
-     {\r
-          statements\r
-     }\r
-     while(condition);\r
-\r
-\r
-Jump statements\r
-\r
-The "return" statement is used to return from a function to the calling\r
-function. Depending upon the declared return data type of the function it\r
-may or may not return a value;\r
-\r
-\r
-     int MULT(int x, int y)\r
-     {\r
-          return(x * y);\r
-     }\r
-\r
-or;\r
-\r
-     void FUNCA()\r
-     {\r
-          printf("\nHello World");\r
-          return;\r
-     }\r
-\r
-The "break" statement is used to break out of a loop or from a switch\r
-statement. In a loop it may be used to terminate the loop prematurely, as\r
-shown here;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     main()\r
-     {\r
-          int x;\r
-     \r
-          for(x = 0; x < 256; x++)\r
-          {\r
-               if (x == 100)\r
-                    break;\r
-     \r
-               printf("%d\t",x);\r
-          }\r
-     }\r
-\r
-In contrast to "break" is "continue", which forces the next iteration of\r
-the loop to occur, effectively forcing program control back to the loop\r
-statement.\r
-\r
-\r
-C provides a function for terminating the program prematurely, "exit()".\r
-Exit() may be used with a return value to pass back to the calling\r
-program;\r
-\r
-\r
-     exit(return_value);\r
-\r
-\r
-More About ?:\r
-\r
-\r
-A powerful, but often misunderstood feature of the C programming language\r
-is ?:. This is an operator that acts upon a boolean expression, and\r
-returns one of two values dependant upon the result of the expression;\r
-\r
-\r
-     <boolean expression> ? <value for true> : <value for false>\r
-\r
-It can be used almost anywhere, for example it was used in the binary\r
-search demonstration program;\r
-\r
-     printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");\r
-\r
-Here it passes either "Not found" or "Located okay" to the printf()\r
-function dependant upon the outcome of the boolean expression `result ==\r
-0'. Alternatively it can be used for assigning values to a variable;\r
-\r
-     x = (a  == 0) ? (b) : (c);\r
-\r
-Which will assign the value of b to variable x if a is equal to zero,\r
-otherwise it will assign the value of c to variable x.\r
-\r
-This example returns the name of the executing program, without any path\r
-description;\r
-\r
-     #include <stdio.h>\r
-     #include <stddef.h>\r
-     #include <string.h>\r
-     \r
-     char *progname(char *pathname)\r
-     {\r
-          /* Return name of running program */\r
-          unsigned l;\r
-          char *p;\r
-          char *q;\r
-          static char bnbuf[256];\r
-     \r
-          return pathname? p = strrchr (pathname, '\\'),\r
-                     q = strrchr (pathname, '.'),\r
-                     l = (q == NULL? strchr (pathname, '\0'): q)\r
-                       - (p == NULL? p = pathname: ++p),\r
-                     strncpy (bnbuf, p, l),\r
-                     bnbuf[l] = '\0',\r
-                     strlwr (bnbuf)\r
-                  : NULL;\r
-     }\r
-     \r
-     void main(int argc, char *argv[])\r
-     {\r
-          printf("\n%s",progname(argv[0]));\r
-     }\r
-     \r
-     \r
-\r
-Continue\r
-\r
-The continue keyword forces control to jump to the test statement of the\r
-innermost loop (while, do...while()). This can be useful for terminating\r
-a loop gracefuly as this program that reads strings from a file until\r
-there are no more illustrates;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     void main()\r
-     {\r
-          FILE *fp;\r
-          char *p;\r
-          char buff[100];\r
-     \r
-          fp = fopen("data.txt","r");\r
-          if (fp == NULL)\r
-          {\r
-               fprintf(stderr,"Unable to open file data.txt");\r
-               exit(0);\r
-          }\r
-     \r
-          do\r
-          {\r
-               p = fgets(buff,100,fp);\r
-               if (p == NULL)\r
-                    /* Force exit from loop */\r
-                    continue;\r
-               puts(p);\r
-          }\r
-          while(p);\r
-     }\r
-     \r
-With a for() loop however, continue passes control back to the third\r
-parameter!\r
-                                    \r
-                            INPUT AND OUTPUT\r
-\r
-\r
-\r
-Input\r
-Input to a C program may occur from the console, the standard input\r
-device (unless otherwise redirected this is the console), from a file or\r
-from a data port.\r
-\r
-The general input command for reading data from the standard input stream\r
-`stdin' is scanf(). Scanf() scans a series of input fields, one character\r
-at a time. Each field is then formatted according to the appropriate\r
-format specifier passed to the scanf() function as a parameter. This\r
-field is then stored at the ADDRESS passed to scanf() following the\r
-format specifiers list.\r
-\r
-For example, the following program will read a single integer from the\r
-stream stdin;\r
-\r
-\r
-     main()\r
-     {\r
-          int x;\r
-     \r
-          scanf("%d",&x);\r
-     }\r
-\r
-Notice the address operator & prefixing the variable name `x' in the\r
-scanf() parameter list. This is because scanf() stores values at\r
-ADDRESSES rather than assigning values to variables directly.\r
-\r
-The format string is a character string that may contain three types of\r
-data:\r
-\r
-whitespace characters (space, tab and newline), non-whitespace characters\r
-(all ascii characters EXCEPT %) and format specifiers.\r
-\r
-Format specifiers have the general form;\r
-\r
-\r
-     %[*][width][h|l|L]type_character\r
-\r
-After the % sign the format specifier is comprised of:\r
-\r
- an optional assignment suppression character, *, which suppresses\r
- assignment of the next input field.\r
\r
- an optional width specifier, width, which declares the maximum number\r
- of characters to be read.\r
\r
- an optional argument type modifier, h or l or L, where:\r
-           h is a short integer\r
-           l is a long\r
-           L is a long double\r
-\r
-     the data type character that is one of;\r
-\r
-\r
-d      Decimal integer\r
-D      Decimal long integer\r
-o      Octal integer\r
-O      Octal long integer\r
-i      Decimal, octal or hexadecimal integer\r
-I      Decimal, octal or hexadecimal long\r
-       integer\r
-u      Decimal unsigned integer\r
-U      Decimal unsigned long integer\r
-x      Hexadecimal integer\r
-X      Hexadecimal long integer\r
-e      Floating point\r
-f      Floating point\r
-g      Floating point\r
-s      Character string\r
-c      Character\r
-%      % is stored\r
-\r
-An example using scanf();\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     main()\r
-     {\r
-          char name[30];\r
-          int age;\r
-     \r
-          printf("\nEnter your name and age ");\r
-          scanf("%30s%d",name,&age);\r
-          printf("\n%s %d",name,age);\r
-     }\r
-\r
-Notice the include line, "#include <stdio.h>", this is to tell the\r
-compiler to also read the file stdio.h that contains the function\r
-prototypes for scanf() and printf().\r
-\r
-\r
-If you type in and run this sample program you will see that only one\r
-name can be entered, that is you can't enter;\r
-\r
-\r
-     JOHN SMITH\r
-\r
-because scanf() detects the whitespace between "JOHN" and "SMITH" and\r
-moves on to the next input field, which is age, and attempts to assign\r
-the value "SMITH" to the age field! The limitations of scanf() as an\r
-input function are obvious.\r
-\r
-An alternative input function is gets() that reads a string of characters\r
-from the stream stdin until a newline character is detected. The newline\r
-character is replaced by a null (0 byte) in the target string. This\r
-function has the advantage of allowing whitespace to be read in. The\r
-following program is a modification to the earlier one, using gets()\r
-instead of scanf().\r
-\r
-\r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     #include <string.h>\r
-     \r
-     main()\r
-     {\r
-          char data[80];\r
-          char *p;\r
-          char name[30];\r
-          int age;\r
-     \r
-          printf("\nEnter your name and age ");\r
-          /* Read in a string of data */\r
-          gets(data);\r
-     \r
-     \r
-          /* P is a pointer to the last character in the input string */\r
-          p = &data[strlen(data) - 1];\r
-     \r
-          /* Remove any trailing spaces by replacing them with null bytes\r
-     */\r
-          while(*p == ' '){\r
-               *p = 0;\r
-               p--;\r
-          }\r
-     \r
-          /* Locate last space in the string */\r
-          p = strrchr(data,' ');\r
-     \r
-          /* Read age from string and convert to an integer */\r
-          age = atoi(p);\r
-     \r
-          /* Terminate data string at start of age field */\r
-          *p = 0;\r
-     \r
-          /* Copy data string to name variable */\r
-          strcpy(name,data);\r
-     \r
-          /* Display results */\r
-          printf("\nName is %s age is %d",name,age);\r
-     }\r
-\r
-\r
-Output\r
-The most common output function is printf() that is very similar to\r
-scanf() except that it writes formatted data out to the standard output\r
-stream stdout.  Printf() takes a list of output data fields and applies\r
-format specifiers to each and outputs the result. The format specifiers\r
-are the same as for scanf() except that flags may be added to the format\r
-specifiers. These flags include;\r
-\r
-\r
-     - Which left justifies the output padding to the right with spaces.\r
-     + Which causes numbers to be prefixed by their sign\r
-\r
-The width specifier is also slightly different for printf(). In its most\r
-useful form is the precision specifier;\r
-\r
-\r
-     width.precision\r
-\r
-So, to print a floating point number to three decimal places you would\r
-use;\r
-\r
-     printf("%.3f",x);\r
-\r
-And to pad a string with spaces to the right or truncate the string to\r
-twenty characters if it is longer, to form a fixed twenty character\r
-width;\r
-\r
-     printf("%-20.20s",data);\r
-\r
-Special character constants may appear in the printf() parameter list.\r
-These are;\r
-\r
-\n         Newline\r
-\r         Carriage return\r
-\t         Tab\r
-\b         Sound the computer's bell\r
-\f         Formfeed\r
-\v         Vertical tab\r
-\\         Backslash character\r
-\'         Single quote\r
-\"         Double quote\r
-\?         Question mark\r
-\O         Octal string\r
-\x         Hexadecimal string\r
-\r
-Thus, to display "Hello World", surrounded in quotation marks and\r
-followed by a newline you would use;\r
-\r
-     printf("\"Hello World\"\n");\r
-\r
-The following program shows how a decimal integer may be displayed as a\r
-decimal, hexadecimal or octal integer. The 04 following the % in the\r
-printf() format tells the compiler to pad the displayed figure to a width\r
-of at least four digits padded with leading zeroes if required.\r
-\r
-\r
-     /* A simple decimal to hexadecimal and octal conversion program */\r
-     \r
-     #include <stdio.h>\r
-     \r
-     main()\r
-     {\r
-          int x;\r
-     \r
-          do\r
-          {\r
-               printf("\nEnter a number, or 0 to end ");\r
-               scanf("%d",&x);\r
-               printf("%04d  %04X  %04o",x,x,x);\r
-          }\r
-          while(x != 0);\r
-     \r
-     }\r
-     \r
-There are associated functions to printf() that you should be aware of.\r
-fprintf() has the prototype;\r
-\r
-     fprintf(FILE *fp,char *format[,argument,...]);\r
-\r
-This variation on printf() simply sends the formatted output to the\r
-specified file stream.\r
-\r
-sprintf() has the function prototype;\r
-\r
-     sprintf(char *s,char *format[,argument,...]);\r
-\r
-and writes the formatted output to a string. You should take care using\r
-sprintf() that the target string has been declared long enough to hold\r
-the result of the sprintf() output. Otherwise other data will be\r
-overwritten in memory.\r
-\r
-An example of using sprintf() to copy multiple data to a string;\r
-\r
-     #include<stdio.h>\r
-     \r
-     int main()\r
-     {\r
-          char buffer[50];\r
-     \r
-          sprintf(buffer,"7 * 5 == %d\n",7 * 5);\r
-     \r
-          puts(buffer);\r
-     }\r
-     \r
-\r
-An alternative to printf() for outputting a simple string to the stream\r
-stdout is puts(). This function sends a string to the stream stdout\r
-followed by a newline character. It is faster than printf(), but far less\r
-flexible. Instead of;\r
-\r
-     printf("Hello World\n");\r
-\r
-You can use;\r
-\r
-     puts("Hello World");\r
-\r
-\r
-\r
-Direct Console I/O\r
-Data may be sent to, and read directly from the console (keyboard and\r
-screen) using the direct console I/O functions. These functions are\r
-prefixed `c'. The direct console I/O equivalent of printf() is then\r
-cprintf(), and the equivalent of puts() is cputs(). Direct console I/O\r
-functions differ from standard I?o functions:\r
-\r
-  1. They do not make use of the predefined streams, and hence may not be\r
-     redirected\r
-  2. They are not portable across operating systems (for example you cant\r
-     use direct console I/O functions in a Windows programme).\r
-  3. They are faster than their standard I/O equivalents\r
-  4. They may not work with all video modes (especially VESA display\r
-     modes).\r
-                                    \r
-                                    \r
-                                    \r
-                                POINTERS\r
-\r
-A pointer is a variable that holds the memory address of an item of data.\r
-Therefore it points to another item. A pointer is declared like an\r
-ordinary variable, but its name is prefixed by `*', thus;\r
-\r
-\r
-     char *p;\r
-\r
-This declares the variable `p' to be a pointer to a character variable.\r
-Pointers are very powerful, and similarly dangerous! If only because a\r
-pointer can be inadvertently set to point to the code segment of a\r
-program and then some value assigned to the address of the pointer!\r
-\r
-The following program illustrates a simple, though fairly useless\r
-application of a pointer;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     main()\r
-     {\r
-          int a;\r
-          int *x;\r
-     \r
-          /* x is a pointer to an integer data type */\r
-     \r
-          a = 100;\r
-          x = &a;\r
-     \r
-          printf("\nVariable 'a' holds the value %d at memory address\r
-     %p",a,x);\r
-     }\r
-\r
-Here is a more useful example of a pointer illustrating how because the\r
-compiler knows the type of data pointed to by the pointer, when the\r
-pointer is incremented it is incremented the correct number of bytes for\r
-the data type.  In this case two bytes;\r
-\r
-     #include <stdio.h>\r
-     \r
-     main()\r
-     {\r
-          int n;\r
-          int a[25];\r
-          int *x;\r
-     \r
-          /* x is a pointer to an integer data type */\r
-     \r
-          /* Assign x to point to array element zero */\r
-          x = a;\r
-     \r
-          /* Assign values to the array */\r
-          for(n = 0; n < 25; n++)\r
-               a[n] = n;\r
-     \r
-     \r
-          /* Now print out all array element values */\r
-          for(n = 0; n < 25; n++ , x++)\r
-               printf("\nElement %d holds %d",n,*x);\r
-     }\r
-     \r
-To read or assign a value to the address held by a pointer you use the\r
-indirection operator `*'. Thus in the above example, to print the value\r
-at the memory address pointed to by variable x I have used `*x'.\r
-\r
-Pointers may be incremented and decremented and have other mathematics\r
-applied to them also. For example in the above program to move variable x\r
-along the array one element at a time we put the statement `x++' in the\r
-for loop. We could move x along two elements by saying `x += 2'. Notice\r
-that this doesn't mean "add 2 bytes to the value of x", but rather it\r
-means "add 2 of the pointer's data type size units to the value of x".\r
-\r
-Pointers are used extensively in dynamic memory allocation. When a\r
-program is running it is often necessary to temporarily allocate a block\r
-of data, say a table, in memory. C provides the function malloc() for\r
-this purpose that follows the general form;\r
-\r
-\r
-     any pointer type = malloc(number_of_bytes);\r
-\r
-malloc() actually returns a void pointer type, which means it can be any\r
-type; integer, character, floating point or whatever. This example\r
-allocates a table in memory for 1000 integers;\r
-\r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     \r
-     main()\r
-     {\r
-          int *x;\r
-          int n;\r
-     \r
-          /* x is a pointer to an integer data type */\r
-     \r
-          /* Create a 1000 element table, sizeof() returns the compiler\r
-     */\r
-          /* specific number of bytes used to store an integer */\r
-     \r
-          x = malloc(1000 * sizeof(int));\r
-     \r
-     \r
-          /* Check to see if the memory allocation succeeded */\r
-          if (x == NULL)\r
-          {\r
-               printf("\nUnable to allocate a 1000 element integer\r
-     table");\r
-               exit(0);\r
-          }\r
-     \r
-          /* Assign values to each table element */\r
-          for(n = 0; n < 1000; n++)\r
-          {\r
-               *x = n;\r
-               x++;\r
-          }\r
-     \r
-          /* Return x to the start of the table */\r
-          x -= 1000;\r
-     \r
-          /* Display the values in the table */\r
-          for(n = 0; n < 1000; n++){\r
-               printf("\nElement %d holds a value of %d",n,*x);\r
-               x++;\r
-          }\r
-          /* Deallocate the block of memory now it's no longer required\r
-     */\r
-          free(x);\r
-     }\r
-     \r
-Pointers are also extensively used with character arrays, called strings.\r
-Since all C program strings are terminated by a zero byte we can count\r
-the letters in a string using a pointer;\r
-\r
-     #include <stdio.h>\r
-     #include <string.h>\r
-     \r
-     main()\r
-     {\r
-          char *p;\r
-          char text[100];\r
-          int len;\r
-     \r
-          /* Initialise variable 'text' with some writing */\r
-          strcpy(text,"This is a string of data");\r
-     \r
-          /* Set variable p to the start of variable text */\r
-          p = text;\r
-     \r
-          /* Initialise variable len to zero */\r
-          len = 0;\r
-     \r
-          /* Count the characters in variable text */\r
-          while(*p)\r
-          {\r
-               len++;\r
-               p++;\r
-          }\r
-     \r
-          /* Display the result */\r
-          printf("\nThe string of data has %d characters in it",len);\r
-     }\r
-     \r
-\r
-The 8088/8086 group of CPUs, as used in the IBM PC, divide memory into\r
-64K segments. To address all 1Mb of memory a 20 bit number is used\r
-comprised of an `offset' to and a 64K `segment'. The IBM PC uses special\r
-registers called "segment registers" to record the segments of addresses.\r
-\r
-This leads the C language on the IBM PC to have three new keywords; near,\r
-far and huge.\r
-\r
-near pointers are 16 bits wide and access only data within the current\r
-segment.\r
-\r
-far pointers are comprised of an offset and a segment address allowing\r
-them to access data any where in memory, but arithmetic with far pointers\r
-is fraught with danger! When a value is added or subtracted from a far\r
-pointer it is only actualy the segment part of the pointer that is\r
-affected, thus leading to the situation where a far pointer being\r
-incremented wraps around on its own offset 64K segment.\r
-\r
-huge pointers are a variation on the far pointer that can be successfuly\r
-incremented and decremented through the entire 1Mb range since the\r
-compiler generates code to amend the offset as applicable.\r
-\r
-It will come as no surprise that code using near pointers is executed\r
-faster than code using far pointers, which in turn is faster than code\r
-using huge pointers.\r
-\r
-to give a literal address to a far pointer IBM PC C compilers provide a\r
-macro MK-FP() that has the prototype;\r
-\r
-\r
-     void far *MK_FP(unsigned segment, unsigned offset);\r
-\r
-For example, to create a far pointer to the start of video memory on a\r
-colour IBM PC system you could use;\r
-\r
-          screen = MK_FP(0xB800,0);\r
-\r
-Two coressponding macros provided are;\r
-\r
-FP_SEG() and FP_OFF()\r
-\r
-Which return the segment and offset respectively of a far pointer. The\r
-following example uses FP_SEG() and FP_OFF() to send segment and offset\r
-addresses to a DOS call to create a new directory path;\r
-\r
-\r
-     #include <dos.h>\r
-     \r
-     int makedir(char *path)\r
-     {\r
-          /* Make sub directory, returning non zero on success */\r
-     \r
-          union REGS inreg,outreg;\r
-          struct SREGS segreg;\r
-     \r
-          inreg.h.ah = 0x39;\r
-          segreg.ds = FP_SEG(path);\r
-          inreg.x.dx = FP_OFF(path);\r
-          intdosx(&inreg,&outreg,&segreg);\r
-     \r
-          return(1 - outreg.x.cflag);\r
-     }\r
-     \r
-\r
-Finally, the CPU's segment registers can be read with the function\r
-`segread()'. This is a function unique to C compilers for the the 8086\r
-family of processors. segread() has the function prototype:\r
-\r
-\r
-     void segread(struct SREGS *segp);\r
-\r
-It returns the current values of the segment registers in the SREGS type\r
-structure pointed to by the pointer `segp'. For example:\r
-\r
-     #include <dos.h>\r
-     \r
-     main()\r
-     {\r
-          struct SREGS sregs;\r
-     \r
-          segread(&sregs);\r
-          printf("\nCode segment is currently at %04X, Data segment is at\r
-     %04X",sregs.cs,sregs.ds);\r
-          printf("\nExtra segment is at %04X, Stack segment is at\r
-     %04X",sregs.es,sregs.ss);\r
-     }\r
-                                    \r
-                               STRUCTURES\r
-\r
-\r
-C provides the means to group together variables under one name providing\r
-a convenient means of keeping related information together and providing\r
-a structured approach to data.\r
-\r
-The general form for a structure definition is;\r
-\r
-\r
-     typedef struct\r
-     {\r
-          variable_type variable_name;\r
-          variable_type variable_name;\r
-     }\r
-     structure_name;\r
-\r
-\r
-When accessing data files, with a fixed record structure, the use of a\r
-structure variable becomes essential. The following example shows a\r
-record structure for a very simple name and address file. It declares a\r
-data structure called `data', and comprised of six fields; `name',\r
-`address', `town', `county' , `post' and `telephone'.\r
-\r
-\r
-     typedef struct\r
-     {\r
-          char name[30];\r
-          char address[30];\r
-          char town[30];\r
-          char county[30];\r
-          char post[12];\r
-          char telephone[15];\r
-     }\r
-     data;\r
-\r
-The structure 'data' may then be used in the program as a variable data\r
-type for declaring variables;\r
-\r
-     data record;\r
-\r
-Thus declares a variable called 'record' to be of type 'data'.\r
-\r
-The individual fields of the structure variable are accessed by the\r
-general form;\r
-\r
-     structure_variable.field_name;\r
-\r
-Thus, the name field of the structure variable record is accessed with;\r
-\r
-     record.name\r
-\r
-There is no limit to the number of fields that may comprise a structure,\r
-nor do the fields have to be of the same types, for example;\r
-\r
-\r
-     typedef struct\r
-     {\r
-          char name[30];\r
-          int age;\r
-          char *notes;\r
-     }\r
-     dp;\r
-\r
-Declares a structure 'dp' that is comprised of a character array field,\r
-an integer field and a character pointer field.\r
-\r
-A structure variable may be passed as a parameter by passing the address\r
-of the variable as the parameter with the & operator thus;\r
-\r
-     func(&my_struct)\r
-\r
-\r
-The following is an example program that makes use of a structure to\r
-provide the basic access to the data in a simple name and address file;\r
-\r
-\r
-/* A VERY simple address book written in ANSI C */\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <io.h>\r
-#include <string.h>\r
-#include <fcntl.h>\r
-#include <sys\stat.h>\r
-\r
-/* num_lines is the number of screen display lines */\r
-#define num_lines   25\r
-\r
-\r
-typedef struct\r
-{\r
-     char name[30];\r
-     char address[30];\r
-     char town[30];\r
-     char county[30];\r
-     char post[12];\r
-     char telephone[15];\r
-}\r
-data;\r
-\r
-data record;\r
-int handle;\r
-\r
-\r
-/* Function prototypes */\r
-\r
-void ADD_REC(void);\r
-void CLS(void);\r
-void DISPDATA(void);\r
-void FATAL(char *);\r
-void GETDATA(void);\r
-void MENU(void);\r
-void OPENDATA(void);\r
-int SEARCH(void);\r
-\r
-void CLS()\r
-{\r
-     int n;\r
-\r
-     for(n = 0; n < num_lines; n++)\r
-          puts("");\r
-}\r
-\r
-void FATAL(char *error)\r
-{\r
-     printf("\nFATAL ERROR: %s",error);\r
-     exit(0);\r
-}\r
-\r
-void OPENDATA()\r
-{\r
-     /* Check for existence of data file and if not create it */\r
-     /* otherwise open it for reading/writing at end of file */\r
-\r
-     handle = open("address.dat",O_RDWR|O_APPEND,S_IWRITE);\r
-\r
-     if (handle == -1)\r
-     {\r
-          handle = open("address.dat",O_RDWR|O_CREAT,S_IWRITE);\r
-          if (handle == -1)\r
-               FATAL("Unable to create data file");\r
-     }\r
-}\r
-\r
-void GETDATA()\r
-{\r
-     /* Get address data from operator */\r
-\r
-     CLS();\r
-\r
-     printf("Name ");\r
-     gets(record.name);\r
-     printf("\nAddress ");\r
-     gets(record.address);\r
-     printf("\nTown ");\r
-     gets(record.town);\r
-     printf("\nCounty ");\r
-     gets(record.county);\r
-     printf("\nPost Code ");\r
-     gets(record.post);\r
-     printf("\nTelephone ");\r
-     gets(record.telephone);\r
-}\r
-\r
-void DISPDATA()\r
-{\r
-     /* Display address data */\r
-     char text[5];\r
-\r
-     CLS();\r
-\r
-     printf("Name %s",record.name);\r
-     printf("\nAddress %s",record.address);\r
-     printf("\nTown %s",record.town);\r
-     printf("\nCounty %s",record.county);\r
-     printf("\nPost Code %s",record.post);\r
-     printf("\nTelephone %s\n\n",record.telephone);\r
-\r
-     puts("Press RETURN to continue");\r
-     gets(text);\r
-}\r
-\r
-void ADD_REC()\r
-{\r
-     /* Insert or append a new record to the data file */\r
-     int result;\r
-\r
-     result = write(handle,&record,sizeof(data));\r
-\r
-     if (result == -1)\r
-          FATAL("Unable to write to data file");\r
-}\r
-\r
-int SEARCH()\r
-{\r
-     char text[100];\r
-     int result;\r
-\r
-     printf("Enter data to search for ");\r
-     gets(text);\r
-     if (*text == 0)\r
-          return(-1);\r
-\r
-     /* Locate start of file */\r
-     lseek(handle,0,SEEK_SET);\r
-\r
-     do\r
-     {\r
-          /* Read record into memory */\r
-          result = read(handle,&record,sizeof(data));\r
-          if (result > 0)\r
-          {\r
-               /* Scan record for matching data */\r
-               if (strstr(record.name,text) != NULL)\r
-                    return(1);\r
-               if (strstr(record.address,text) != NULL)\r
-                    return(1);\r
-               if (strstr(record.town,text) != NULL)\r
-                    return(1);\r
-               if (strstr(record.county,text) != NULL)\r
-                    return(1);\r
-               if (strstr(record.post,text) != NULL)\r
-                    return(1);\r
-               if (strstr(record.telephone,text) != NULL)\r
-                    return(1);\r
-          }\r
-     }\r
-     while(result > 0);\r
-     return(0);\r
-}\r
-\r
-void MENU()\r
-{\r
-     int option;\r
-     char text[10];\r
-\r
-     do\r
-     {\r
-          CLS();\r
-          puts("\n\t\t\tSelect Option");\r
-          puts("\n\n\t\t\t1 Add new record");\r
-          puts("\n\n\t\t\t2 Search for data");\r
-          puts("\n\n\t\t\t3 Exit");\r
-          puts("\n\n\n\n\n");\r
-          gets(text);\r
-          option = atoi(text);\r
-\r
-          switch(option)\r
-          {\r
-               case 1 : GETDATA();\r
-                     /* Go to end of file to append new record */\r
-                     lseek(handle,0,SEEK_END);\r
-                     ADD_REC();\r
-                     break;\r
-\r
-               case 2 : if (SEARCH())\r
-                          DISPDATA();\r
-                     else\r
-                     {\r
-                          puts("NOT FOUND!");\r
-                          puts("Press RETURN to continue");\r
-                          gets(text);\r
-                     }\r
-                     break;\r
-\r
-               case 3 : break;\r
-          }\r
-     }\r
-     while(option != 3);\r
-}\r
-\r
-void main()\r
-{\r
-     CLS();\r
-     OPENDATA();\r
-     MENU();\r
-}\r
-\r
-Bit Fields\r
-\r
-C allows the inclusion of variables with a size of less than eight bits\r
-to be included in structures. These variables are known as bit fields,\r
-and may be any declared size from one bit upwards.\r
-\r
-The general form for declaring a bit field is;\r
-\r
-\r
-     type name : number_of_bits;\r
-\r
-\r
-For example, to declare a set of status flags, each occupying one bit;\r
-\r
-typedef struct\r
-{\r
-     unsigned carry  : 1;\r
-     unsigned zero   : 1;\r
-     unsigned over   : 1;\r
-     unsigned parity : 1;\r
-}\r
-df;\r
-\r
-df flags;\r
-\r
-The variable `flags' then occupies only four bits in memory, and yet is\r
-comprised of four variables that may be accessed like any other structure\r
-field.\r
-\r
-\r
-\r
-Uunions\r
-\r
-Another facility provided by C for efficient use of available memory is\r
-the union structure. A union structure is a collection of variables that\r
-all share the same memory storage address. As such only one of the\r
-variables is ever accessible at a time.\r
-\r
-The general form of a union definition is;\r
-\r
-\r
-     union name\r
-     {\r
-          type variable_name;\r
-          type variable_name;\r
-          .\r
-          .\r
-          .\r
-          type variable_name;\r
-     };\r
-\r
-Thus, to declare a union structure for two integer variables;\r
-\r
-     union data\r
-     {\r
-          int vara;\r
-          int varb;\r
-     };\r
-\r
-and to declare a variable of type 'data';\r
-\r
-     data my_var;\r
-\r
-The variable 'my_var' is then comprised of the two variables 'vara' and\r
-'varb'  that are accessed like with any form of structure, eg;\r
-\r
-     my_var.vara = 5;\r
-\r
-Assigns a value of 5 to the variable 'vara' of union 'my_var'.\r
-\r
-\r
-Enumerations\r
-\r
-An enumeration assigns ascending integer values to a list of symbols. An\r
-enumeration declaration takes the general form;\r
-\r
-\r
-     enum name { enumeration list } variable_list;\r
-\r
-Thus to define a symbol list of colours, you can say;\r
-\r
-     enum COLOURS\r
-     {\r
-          BLACK,\r
-          BLUE,\r
-          GREEN,\r
-          CYAN,\r
-          RED,\r
-          MAGENTA,\r
-          BROWN,\r
-          LIGHTGRAY,\r
-          DARKGRAY,\r
-          LIGHTBLUE,\r
-          LIGHTGREEN,\r
-          LIGHTCYAN,\r
-          LIGHTRED,\r
-          LIGHTMAGENTA,\r
-          YELLOW,\r
-          WHITE\r
-     };\r
-\r
-Then, the number zero may be referred to by the symbol BLACK, the number\r
-one by the symbol BLUE, the number two by the symbol GREEN and so on.\r
-\r
-The following program illustrates the use of an enumeration list to\r
-symbolise integers;\r
-\r
-     #include <stdio.h>\r
-     \r
-     enum COLOURS\r
-     {\r
-          BLACK,\r
-          BLUE,\r
-          GREEN,\r
-          CYAN,\r
-          RED,\r
-          MAGENTA,\r
-          BROWN,\r
-          LIGHTGRAY,\r
-          DARKGRAY,\r
-          LIGHTBLUE,\r
-          LIGHTGREEN,\r
-          LIGHTCYAN,\r
-          LIGHTRED,\r
-          LIGHTMAGENTA,\r
-          YELLOW,\r
-          WHITE\r
-     };\r
-     \r
-     \r
-     void main()\r
-     {\r
-          int x;\r
-     \r
-          x = RED;\r
-     \r
-          printf("\nVariable 'x' holds %d",x);\r
-     \r
-     }\r
-                                    \r
-                                FILE I/O\r
-\r
-\r
-C provides buffered file streams for file access. Some C platforms, such\r
-as Unix and DOS provide unbuffered file handles as well.\r
-\r
-\r
-\r
-Buffered streams\r
-\r
-Buffered streams are accessed through a variable of type 'file pointer'.\r
-The data type FILE is defined in the header file stdio.h. Thus to declare\r
-a file pointer;\r
-\r
-     #include <stdio.h>\r
-\r
-     FILE *ptr;\r
-\r
-To open a stream C provides the function fopen(), which accepts two\r
-parameters, the name of the file to be opened, and the access mode for\r
-the file to be opened with. The access mode may be any one of;\r
-\r
-\r
-Mode   Description\r
-       \r
-r      Open for reading\r
-w      Create for writing, destroying any\r
-       existing file\r
-a      Open for append, creating a new file\r
-       if it doesn't\r
-        exist\r
-r+      Open an existing file for reading\r
-       and writing\r
-w+      Create for reading and writing,\r
-       destroying any\r
-        existing file\r
-a+      Open for append, creating a new file\r
-       if it doesn't exist.\r
-\r
-\r
-Optionaly either `b' or `t' may be appended for binary or text mode. If\r
-neither is appended, then the file stream will be opened in the mode\r
-described by the global variable _fmode. Data read or written from file\r
-streams opened in text mode undergoes conversion, that is the characters\r
-CR and LF are converted to CR LF pairs on writing, and the CR LF pair is\r
-converted to a single LF on reading. File streams opened in binary mode\r
-do not undergo conversion.\r
-\r
-\r
-If fopen() fails to open the file, it returns a value of NULL (defined in\r
-stdio.h) to the file pointer.\r
-\r
-Thus, the following program will create a new file called "data.txt" and\r
-open it for reading and writing;\r
-\r
-     #include <stdio.h>\r
-     \r
-     void main()\r
-     {\r
-          FILE *fp;\r
-     \r
-          fp = fopen("data.txt","w+");\r
-     \r
-     }\r
-\r
-To close a stream C provides the function fclose(), which accepts the\r
-stream's file pointer as a parameter;\r
-\r
-     fclose(fp);\r
-\r
-If an error occurs in closing the file stream, fclose() returns non zero.\r
-\r
-There are four basic functions for receiving and sending data to and from\r
-streams; fgetc(), fputc(), fgets() and fputs().\r
-\r
-fgetc() simply reads a single character from the specified input stream;\r
-\r
-     char fgetc(FILE *fp);\r
-\r
-Its opposite number is fputc(), which simply writes a single character to\r
-the specified input stream;\r
-\r
-     char fputc(char c, FILE *fp);\r
-\r
-fgets() reads a string from the input stream;\r
-\r
-     char *fgets(char s, int numbytes, FILE *fp);\r
-\r
-It stops reading when either numbytes - 1 bytes have been read, or a\r
-newline character is read in. A null terminating byte is appended to the\r
-read string, s. If an error occurs, fgets() returns NULL.\r
-\r
-\r
-fputs() writes a null terminated string to a stream;\r
-\r
-     int fputs(char *s, FILE *fp);\r
-\r
-Excepting fgets(), which returns a NULL pointer if an error occurs, all\r
-the other functions described above return EOF (defined in stdio.h) if an\r
-error occurs during the operation.\r
-\r
-\r
-The following program creates a copy of the file "data.dat" as "data.old"\r
-and illustrates the use of fopen(), fgetc(), fputc() and fclose();\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     int main()\r
-     {\r
-          FILE *in;\r
-          FILE *out;\r
-     \r
-          in = fopen("data.dat","r");\r
-     \r
-          if (in == NULL)\r
-          {\r
-               puts("\nUnable to open file data.dat for reading");\r
-               return(0);\r
-          }\r
-     \r
-          out = fopen("data.old","w+");\r
-     \r
-          if (out == NULL)\r
-          {\r
-               puts("\nUnable to create file data.old");\r
-               return(0);\r
-          }\r
-     \r
-          /* Loop reading and writing one byte at a time until end-of-\r
-     file */\r
-          while(!feof(in))\r
-               fputc(fgetc(in),out);\r
-     \r
-          /* Close the file streams */\r
-          fclose(in);\r
-          fclose(out);\r
-     \r
-          return(0);\r
-     }\r
-\r
-Example program using fputs() to copy text from stream stdin (usually\r
-typed in at the keyboard) to a new file called "data.txt".\r
-\r
-     #include <stdio.h>\r
-     \r
-     int main()\r
-     {\r
-          FILE *fp;\r
-          char text[100];\r
-     \r
-          fp = fopen("data.txt","w+");\r
-     \r
-          do\r
-          {\r
-               gets(text);\r
-               fputs(text,fp);\r
-          }\r
-          while(*text);\r
-     \r
-          fclose(fp);\r
-     }\r
-\r
-\r
-Random access using streams\r
-\r
-Random file access for streams is provided for by the fseek() function\r
-that has the following prototype;\r
-\r
-     int fseek(FILE *fp, long numbytes, int fromwhere);\r
-\r
-fseek() repositions a file pointer associated with a stream previously\r
-opened by a call to fopen(). The file pointer is positioned `numbytes'\r
-from the location `fromwhere', which may be the file beginning, the\r
-current file pointer position, or the end of the file, symbolised by the\r
-constants SEEK_SET, SEEK_CUR and SEEK_END respectively. If a call to\r
-fseek() succeeds, a value of zero is returned.\r
-\r
-\r
-Associated with fseek() is ftell(), which reports the current file\r
-pointer position of a stream, and has the following function prototype;\r
-\r
-\r
-     long int ftell(FILE *fp);\r
-\r
-ftell() returns either the position of the file pointer, measured in\r
-bytes from the start of the file, or -1 upon an error occurring.\r
-\r
-\r
-\r
-Handles\r
-\r
-File handles are opened with the open() function that has the prototype;\r
-\r
-     int open(char *filename,int access[,unsigned mode]);\r
-\r
-If open() is successful, the number of the file handle is returned.\r
-Otherwise open() returns -1.\r
-\r
-The access integer is comprised from bitwise oring together of the\r
-symbolic constants declared in fcntl.h. These vary from compiler to\r
-compiler but may be;\r
-\r
-\r
-     O_APPEND       If set, the file pointer will be set to the end of\r
-the\r
-                    file prior to each write.\r
-     O_CREAT        If the file does not exist it is created.\r
-     O_TRUNC        Truncates the existing file to a length of zero\r
-bytes.\r
-     O_EXCL          Used with O_CREAT\r
-     O_BINARY       Opens the file in binary mode\r
-     O_TEXT         Opens file in text mode\r
-\r
-The optional mode parameter is comprised by bitwise oring of the symbolic\r
-constants defined in stat.h. These vary from C compiler to C compiler but\r
-may be;\r
-\r
-     S_IWRITE       Permission to write\r
-     S_IREAD        Permission to read\r
-\r
-\r
-Once a file handle has been assigned with open(), the file may be\r
-accessed with read() and write().\r
-\r
-Read() has the function prototype;\r
-\r
-     int read(int handle, void *buf, unsigned num_bytes);\r
-\r
-It attempts to read 'num_bytes' and returns the number of bytes actually\r
-read from the file handle 'handle', and stores these bytes in the memory\r
-block pointed to by 'buf'.\r
-\r
-Write() is very similar to read() and has the same function prototype and\r
-return values, but writes 'num_bytes' from the memory block pointed to by\r
-'buf'.\r
-\r
-Files opened with open() are closed using close() that has the function\r
-prototype;\r
-\r
-     int close(int handle);\r
-\r
-close() returns zero on success, and -1 if an error occurs trying to\r
-close the handle.\r
-\r
-Random access is provided by lseek(), which is very similar to fseek(),\r
-except that it accepts an integer file handle as the first parameter\r
-rather than a stream FILE pointer.\r
-\r
-This example uses file handles to read data from stdin (usually the\r
-keyboard) and copy the text to a new file called "data.txt".\r
-\r
-     #include <io.h>\r
-     #include <fcntl.h>\r
-     #include <sys\stat.h>\r
-     \r
-     int main()\r
-     {\r
-          int handle;\r
-          char text[100];\r
-     \r
-          handle = open("data.txt",O_RDWR|O_CREAT|O_TRUNC,S_IWRITE);\r
-     \r
-          do\r
-          {\r
-               gets(text);\r
-               write(handle,&text,strlen(text));\r
-          }\r
-          while(*text);\r
-     \r
-          close(handle);\r
-     }\r
-\r
-\r
-\r
-Advanced File I/O\r
-\r
-The ANSI standard on C defines file IO as by way of file streams, and\r
-defines various functions for file access;\r
-\r
-\r
-fopen() has the prototype;\r
-\r
-     FILE *fopen(const char *name,const char *mode);\r
-\r
-fopen() attempts to open a stream to a file name in a specified mode. If\r
-successful a FILE type pointer is returned to the file stream, if the\r
-call fails NULL is returned. The mode string can be one of the following;\r
-\r
-\r
-Mode      Description\r
- r        Open for reading only\r
- w        Create for writing, overwriting any existing file with the same\r
-          name.\r
- a        Open for append (writing at end of file) or create the file if\r
-it\r
-          does not exist.\r
- r+       Open an existing file for reading and writing.\r
- w+       Create a new file for reading and writing.\r
- a+       Open for append with read and write access.\r
-\r
-\r
-fclose() is used to close a file stream previously opened by a call to\r
-fopen(). It has the prototype;\r
-\r
-     int fclose(FILE *fp);\r
-\r
-When a call to fclose() is successful, all buffers to the stream are\r
-flushed and a value of zero is returned. If the call fails fclose()\r
-returns EOF.\r
-\r
-Many host computers, and the IBM PC is no exception, use buffered file\r
-access, that is when writing to a file stream the data is stored in\r
-memory and only written to the stream when it exceeds a predefined number\r
-of bytes. A power failure occurring before the data has been written to\r
-the stream will result in the data never being written, so the function\r
-fflush() can be called to force all pending data to be written. fflush()\r
-has the prototype;\r
-\r
-     int fflush(FILE *fp);\r
-\r
-When a call to fflush() is successful, the buffers connected with the\r
-stream are flushed and a value of zero is returned. On failure fflush()\r
-returns EOF.\r
-\r
-The location of the file pointer connected with a stream can be\r
-determined with the function ftell(). ftell() has the prototype;\r
-\r
-\r
-     long int ftell(FILE *fp);\r
-\r
-and returns the offset of the file pointer in bytes from the start of the\r
-file, or -1L if the call fails.\r
-\r
-Similarly, you can move the file pointer to a new position with fseek().\r
-fseek() has the prototype;\r
-\r
-     int fseek(FILE *fp, long offset, int from_what_place);\r
-\r
-fseek() attempts to move the file pointer, 'fp' 'offset' bytes from the\r
-position 'from_what_place'. 'from_what_place' is predefined as one of;\r
-\r
-     SEEK_SET       The file's beginning\r
-     SEEK_CUR       The file pointer's current position\r
-     SEEK_END       End of file\r
-\r
-The offset may be a positive value to move the file pointer on through\r
-the file, or negative to move backwards.\r
-\r
-To move a file pointer quickly back to the start of a file, and clear any\r
-references to errors that have occurred C provides the function rewind()\r
-that has the prototype;\r
-\r
-     void rewind(FILE *fp);\r
-\r
-rewind(fp) is similar to fseek(fp,0L,SEEK_SET) in that they both set the\r
-file pointer to the start of the file, but whereas fseek() clears the EOF\r
-error marker, rewind() clears all error indicators.\r
-\r
-Errors occurring with file functions can be checked with the function\r
-ferror() that has the prototype;\r
-\r
-     int ferror(FILE *fp);\r
-\r
-ferror() returns a nonzero value if an error has occurred on the\r
-specified stream. After checking ferror() and reporting any errors you\r
-should clear the error indicators, and this can be done by a call to\r
-clearerr() that has the prototype;\r
-\r
-     void clearerr(FILE *fp);\r
-\r
-The condition of reaching end of file (EOF) can be tested for with the\r
-predefined macro feof() that has the prototype;\r
-\r
-     int feof(FILE *fp);\r
-\r
-feof() returns a nonzero value if an end of file error indicator was\r
-detected on the specified file stream, and zero if the end of file has\r
-not yet been reached.\r
-\r
-Reading data from a file stream can be achieved using several functions;\r
-A single character can be read with fgetc() that has the prototype;\r
-\r
-\r
-     int fgetc(FILE *fp);\r
-\r
-fgetc() returns either the character read converted to an integer, or EOF\r
-if an error occurred.\r
-\r
-Reading a string of data is achieved with fgets(). fgets() attempts to\r
-read a string terminated by a newline character and of no more than a\r
-specified number of bytes from the file stream. It has the prototype;\r
-\r
-\r
-     char *fgets(char s, int n, FILE *fp);\r
-\r
-A successful call to fgets() results in a string being stored in `s' that\r
-is either terminated by a newline character, or that is `n' - 1\r
-characters long, which ever came first. The newline character is retained\r
-by fgets(), and a null bytes is appended to the string. If the call fails\r
-a NULL pointer is returned.\r
-\r
-\r
-Strings may be written to a stream using fputs() that has the prototype;\r
-\r
-     int fputs(const char *s,FILE *fp);\r
-\r
-fputs() writes all the characters in the string `s' to the stream `fp'\r
-except the null terminating byte. On success, fputs() returns the last\r
-character written, on failure it returns EOF.\r
-\r
-To write a single character to a stream use fputc() that has the\r
-prototype;\r
-\r
-     int fputc(int c,FILE *fp);\r
-\r
-If successful, fputc() returns the character written, otherwise it\r
-returns EOF.\r
-\r
-To read a large block of data, or a record from a stream you can use\r
-fread() that has the prototype;\r
-\r
-     size_t fread(void *ptr,size_t size, size_t n, FILE *fp);\r
-\r
-fread() attempts to read 'n' items, each of length 'size' from the file\r
-stream 'fp' into the block of memory pointed to by 'ptr'. To check the\r
-success or failure status of fread() use ferror().\r
-\r
-The sister function to fread() is fwrite() that has the prototype;\r
-\r
-     size_t fwrite(const void *ptr,size_t size, size_t n,FILE *fp);\r
-\r
-that writes 'n' items each of length 'size' from the memory area pointed\r
-to by 'ptr' to the specified stream 'fp'.\r
-\r
-Formatted input from a stream is achieved with fscanf() that has the\r
-prototype;\r
-\r
-     int fscanf(FILE *fp, const char *format[,address ...]);\r
-\r
-fscanf() returns the number of fields successfully stored, and EOF on end\r
-of file. This short example shows how fscanf() is quite useful for\r
-reading numbers from a stream, but hopeless when it comes to strings!\r
-\r
-     #include <stdio.h>\r
-     \r
-     void main()\r
-     {\r
-          FILE *fp;\r
-          int a;\r
-          int b;\r
-          int c;\r
-          int d;\r
-          int e;\r
-          char text[100];\r
-     \r
-          fp = fopen("data.txt","w+");\r
-     \r
-          if(!fp)\r
-          {\r
-               perror("Unable to create file");\r
-               exit(0);\r
-          }\r
-     \r
-          fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");\r
-     \r
-          fflush(fp);\r
-     \r
-          if (ferror(fp))\r
-          {\r
-               fputs("Error flushing stream",stderr);\r
-               exit(1);\r
-          }\r
-     \r
-          rewind(fp);\r
-          if (ferror(fp))\r
-          {\r
-               fputs("Error rewind stream",stderr);\r
-               exit(1);\r
-          }\r
-     \r
-          fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);\r
-          if (ferror(fp))\r
-          {\r
-               fputs("Error reading from stream",stderr);\r
-               exit(1);\r
-          }\r
-     \r
-          printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);\r
-     }\r
-     \r
-As you can see from the example, fprintf() can be used to write formatted\r
-data to a stream.\r
-\r
-If you wish to store the position of a file pointer on a stream, and then\r
-later restore it to the same position you can use the functions fgetpos()\r
-and fsetpos(). fgetpos() reads the current location of the file pointer\r
-and has the prototype;\r
-\r
-     int fgetpos(FILE *fp, fpos_t *pos);\r
-\r
-fsetpos() repositions the file pointer and has the prototype;\r
-\r
-     int fsetpos(FILE *fp, const fpos_t *fpos);\r
-\r
-fpos_t is defined in stdio.h.\r
-\r
-These functions are more convenient than doing an ftell() followed by an\r
-fseek().\r
-\r
-An open stream can have a new file associated with it in place of the\r
-existing file by using the function freopen() that has the prototype;\r
-\r
-     FILE *freopen(const char *name,const char *mode,FILE *fp);\r
-\r
-freopen() closes the existing stream and then attempts to reopens it with\r
-the specified file name. This is useful for redirecting the predefined\r
-streams stdin, stdout and stderr to a file or device.\r
-\r
-For example; if you wish to redirect all output intended to stdout\r
-(usually the host computer's display device) to a printer you might use;\r
-\r
-     freopen("LPT1","w",stdout);\r
-\r
-where LPT1 is the name of the printer device (on a PC host, LPT1 is the\r
-name of the parallel port).\r
-\r
-\r
-Predefined I/O Streams\r
-\r
-There are three predefined I/O streams; stdin, stdout, and stderr. The\r
-streams stdin and stdout default to the keyboard and display\r
-respectively, but can be redirected on some hardware platforms, such as\r
-the PC and under UNIX. The stream stderr defaults to the display and is\r
-not usually redirected by the operator. Thus it can be used for the\r
-display of error messages even when program output has been redirected,\r
-such as with;\r
-\r
-      fputs("Error message",stderr);\r
-\r
-The functions printf() and puts(), output data to the stream stdout, and\r
-can therefore be redirected by the operator of the program. scanf() and\r
-gets() accept input from the stream stdin.\r
-\r
-As an example of file i/o with the PC consider the following short\r
-program that does a hex dump of a specified file to the predefined stream\r
-stdout, which may be redirected to a file using;\r
-\r
-          dump filename.ext > target.ext\r
-\r
-     #include <stdio.h>\r
-     #include <fcntl.h>\r
-     #include <io.h>\r
-     #include <string.h>\r
-     \r
-     main(int argc, char *argv[])\r
-     {\r
-          unsigned counter;\r
-          unsigned char v1[20];\r
-          int f1;\r
-          int x;\r
-          int n;\r
-     \r
-          if (argc != 2)\r
-          {\r
-               fputs("\nERROR: Syntax is dump f1\n",stderr);\r
-               return(1);\r
-          }\r
-     \r
-          f1 = open(argv[1],O_RDONLY);\r
-     \r
-          if (f1 == -1)\r
-          {\r
-               fprintf(stderr,"\nERROR: Unable to open %s\n",argv[1]);\r
-               return(1);\r
-          }\r
-     \r
-          fprintf(stdout,"\nDUMP OF FILE %s\n\n",strupr(argv[1]));\r
-     \r
-          counter = 0;\r
-     \r
-          while(1)\r
-          {\r
-               /* Set buffer to zero bytes */\r
-               memset(v1,0,20);\r
-     \r
-               /* Read buffer from file */\r
-               x = _read(f1,&v1,16);\r
-     \r
-               /* x will be 0 on EOF or -1 on error */\r
-               if (x < 1)\r
-                    break;\r
-     \r
-               /* Print file offset to stdout */\r
-               fprintf(stdout,"%06d(%05x) ",counter,counter);\r
-     \r
-               counter += 16;\r
-     \r
-               /* print hex values of buffer to stdout */\r
-               for(n = 0; n < 16; n++)\r
-                    fprintf(stdout,"%02x ",v1[n]);\r
-     \r
-               /* Print ascii values of buffer to stdout */\r
-               for(n = 0; n < 16; n++)\r
-               {\r
-                    if ((v1[n] > 31) && (v1[n] < 128))\r
-                         fprintf(stdout,"%c",v1[n]);\r
-                    else\r
-                         fputs(".",stdout);\r
-               }\r
-     \r
-               /* Finish the line with a new line */\r
-               fputs("\n",stdout);\r
-          }\r
-     \r
-          /* successful termination */\r
-          return(0);\r
-     }\r
-                                    \r
-                                 STRINGS\r
-\r
-The C language has one of the most powerful string handling capabilities\r
-of any general purpose computer language.\r
-\r
-A string is a single dimension array of characters terminated by a zero\r
-byte.\r
-\r
-Strings may be initialised in two ways. Either in the source code where\r
-they may be assigned a constant value, as in;\r
-\r
-     int main()\r
-     {\r
-          char *p = "System 5";\r
-          char name[] = "Test Program" ;\r
-     }\r
-\r
-or at run time by the function strcpy() that has the function prototype;\r
-\r
-     char *strcpy(char *destination, char *source);\r
-\r
-strcpy() copies the string pointed to by source into the location pointed\r
-to by destination as in the following example;\r
-\r
-\r
-     #include<stdio.h>\r
-     \r
-     int main()\r
-     {\r
-          char name[50];\r
-     \r
-          strcpy(name,"Servile Software");\r
-     \r
-          printf("\nName equals %s",name);\r
-     }\r
-\r
-C also allows direct access to each individual byte of the string, so the\r
-following is quite permissible;\r
-\r
-     #include<stdio.h>\r
-     \r
-     int main()\r
-     {\r
-          char name[50];\r
-     \r
-          strcpy(name,"Servile Software");\r
-     \r
-          printf("\nName equals %s",name);\r
-     \r
-          /* Replace first byte with lower case 's' */\r
-          name[0] = 's';\r
-     \r
-          printf("\nName equals %s",name);\r
-     }\r
-     \r
-The ANSI standard on the C programming language defines the following\r
-functions for use with strings;\r
-\r
-char *strcat(char *dest, char *source)            Appends string source\r
-to the end of string destination.\r
-\r
-char *strchr(char *s, int c)            Returns a pointer to the first\r
-occurence of character 'c' within s.\r
-\r
-int strcmp(char *s1, char *s2)               Compares strings s1 and s2\r
-returning        < 0 if s1 is less than s2\r
-                                                       == 0 if s1 and s2\r
-are the same\r
-                                                       > 0 if s1 is\r
-greater than s2\r
-\r
-int strcoll(char *s1, char *s2)              Compares strings s1 and s2\r
-                    according to the collating sequence set by\r
-                              setlocale() returning    < 0 if s1 is less\r
-than s2\r
-                                             == 0 if s1 and s2 are the\r
-same\r
-                                             > 0 if s1 is greater than s2\r
-\r
-char *strcpy(char *dest, char *src)          Copies string src into\r
-string dest.\r
-\r
-unsigned strcspn(char *s1, char *s2)         Returns the length of string\r
-s1 that consists entirely of characters not in\r
-string s2.\r
-\r
-unsigned strlen(char *s)                Returns the length of string s.\r
-\r
-char *strncat(char *dest, char *src, unsigned len)     Copies at most\r
-'len' characters from string src into string dest.\r
-\r
-int strncmp(char *s1, char *s2, unsigned len)     Compares at most 'len'\r
-characters from\r
-                              string s1 with string s2 returning      < 0\r
-if s1 is less than s2\r
-                                                  == 0 if s1 and s2 are\r
-the same\r
-                                                  > 0 if s1 is greater\r
-than s2\r
-\r
-char *strncpy(char *dest, char *src, unsigned len)     Copies 'len'\r
-characters from string  src into string dest, truncating or\r
-                              padding with zero bytes as required.\r
-\r
-char *strpbrk(char *s1, char *s2)            Returns a pointer to the\r
-first character in string s1 that occurs in\r
-                              string s2.\r
-\r
-char *strrchr(char *s, int c)           Returns a pointer to the last\r
-occurence of 'c' within string s.\r
-\r
-unsigned strspn(char *s1, char *s2)          Returns the length of the\r
-initial segment of string s1 that consists\r
-                              entirely of characters in string s2.\r
-\r
-char *strstr(char *s1, char *s2)             Returns a pointer to the\r
-first occurence of string s2 within string\r
-                              s1, or NULL if string s2 is not found in\r
-string s1.\r
-\r
-char *strtok(char *s1, char *s2)             Returns a pointer to the\r
-token found in string s1 that is defined by\r
-                              delimiters in string s2. Returns NULLif no\r
-tokens are found.\r
-\r
-The ANSI standard also defines various functions for converting strings\r
-into numbers and numbers into strings.\r
-\r
-Some C compilers include functions to convert strings to upper and lower\r
-case, but these functions are not defined in the ANSI standard. However,\r
-the ANSI standard does define the functions; toupper() and tolower() that\r
-return an\r
-\r
-integer parameter converted to upper and lowercase respectively. By using\r
-these functions we can create our own ANSI compatible versions;\r
-\r
-\r
-     #include<stdio.h>\r
-     \r
-     void strupr(char *source)\r
-     {\r
-          char *p;\r
-     \r
-          p = source;\r
-          while(*p)\r
-          {\r
-               *p = toupper(*p);\r
-               p++;\r
-          }\r
-     }\r
-     \r
-     void strlwr(char *source)\r
-     {\r
-          char *p;\r
-     \r
-          p = source;\r
-          while(*p)\r
-          {\r
-               *p = tolower(*p);\r
-               p++;\r
-          }\r
-     }\r
-\r
-\r
-     int main()\r
-     {\r
-          char name[50];\r
-     \r
-          strcpy(name,"Servile Software");\r
-     \r
-          printf("\nName equals %s",name);\r
-     \r
-          strupr(name);\r
-     \r
-          printf("\nName equals %s",name);\r
-     \r
-          strlwr(name);\r
-     \r
-          printf("\nName equals %s",name);\r
-     }\r
-\r
-C does not impose a maximum length that a string may be, unlike other\r
-computer languages. However, some CPUs impose restrictions on the maximum\r
-size a block of memory can be. For example, the 8088 family of CPUs, as\r
-used by the IBM PC, impose a limit of 64K bytes on a segment of memory.\r
-\r
-An example program to reverse all the characters in a string.\r
-\r
-     #include <stdio.h>\r
-     #include <string.h>\r
-     \r
-     char *strrev(char *s)\r
-     {\r
-          /* Reverses the order of all characters in a string except the\r
-     null */\r
-          /* terminating byte */\r
-     \r
-          char *start;\r
-          char *end;\r
-          char tmp;\r
-     \r
-          /* Set pointer 'end' to last character in string */\r
-          end = s + strlen(s) - 1;\r
-     \r
-          /* Preserve pointer to start of string */\r
-          start = s;\r
-     \r
-          /* Swop characters */\r
-          while(end >= s)\r
-          {\r
-               tmp = *end;\r
-               *end = *s;\r
-               *s = tmp;\r
-               end--;\r
-               s++;\r
-          }\r
-          return(start);\r
-     }\r
-     \r
-     \r
-     main()\r
-     {\r
-          char text[100];\r
-          char *p;\r
-     \r
-          strcpy(text,"This is a string of data");\r
-     \r
-          p = strrev(text);\r
-     \r
-          printf("\n%s",p);\r
-     }\r
-     \r
-\r
-Strtok()\r
-\r
-The function strtok() is a very powerful standard C feature for\r
-extracting substrings from within a single string. It is used where the\r
-substrings are separated by known delimiters, such as commas in the\r
-following example;\r
-\r
-     #include <stdio.h>\r
-     #include <string.h>\r
-     \r
-     main()\r
-     {\r
-          char data[50];\r
-          char *p;\r
-     \r
-          strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");\r
-     \r
-          p = strtok(data,",");\r
-          while(p)\r
-          {\r
-               puts(p);\r
-               p = strtok(NULL,",");\r
-          };\r
-     }\r
-     \r
-Or this program can be written with a for() loop thus;\r
-\r
-     #include <stdio.h>\r
-     #include <string.h>\r
-     \r
-     main()\r
-     {\r
-          char data[50];\r
-          char *p;\r
-     \r
-          strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");\r
-     \r
-          for(strtok(data,","); p; p = strtok(NULL,","))\r
-          {\r
-               puts(p);\r
-          };\r
-     }\r
-     \r
-They both compile to the same code but follow different programming\r
-styles.\r
-\r
-Initially, you call strtok() with the name of the string variable to be\r
-parsed, and a second string that contains the known delimiters. Strtok()\r
-then returns a pointer to the start of the first substring and replaces\r
-the first token with a zero delimiter. Subsequent calls to strtok() can\r
-be made in a loop passing NULL as the string to be parsed, and strtok()\r
-will return the subsequent substrings.\r
-\r
-\r
-Since strtok() can accept many delimiter characters in the second\r
-parameter string we can use it as the basis of a simple word counting\r
-program;\r
-\r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     #include <string.h>\r
-     \r
-     void main(int argc, char *argv[])\r
-     {\r
-          FILE *fp;\r
-          char buffer[256];\r
-          char *p;\r
-          long count;\r
-     \r
-          if (argc != 2)\r
-          {\r
-               fputs("\nERROR: Usage is wordcnt <file>\n",stderr);\r
-               exit(0);\r
-          }\r
-     \r
-          /* Open file for reading */\r
-          fp = fopen(argv[1],"r");\r
-     \r
-          /* Check the open was okay */\r
-          if (!fp)\r
-          {\r
-               fputs("\nERROR: Cannot open source file\n",stderr);\r
-               exit(0);\r
-          }\r
-     \r
-          /* Initialise word count */\r
-          count = 0;\r
-     \r
-          do\r
-          {\r
-               /* Read a line of data from the file */\r
-               fgets(buffer,255,fp);\r
-     \r
-               /* check for an error in the read or EOF */\r
-               if (ferror(fp) || feof(fp))\r
-                    continue;\r
-     \r
-               /* count words in received line */\r
-               /* Words are defined as separated by the characters */\r
-               /* \t(tab) \n(newline) , ; : . ! ? ( ) - and [space] */\r
-               p = strtok(buffer,"\t\n,;:.!?()- ");\r
-               while(p)\r
-               {\r
-                    count++;\r
-                    p = strtok(NULL,"\t\n,;:.!?()- ");\r
-               }\r
-          }\r
-          while(!ferror(fp) && !feof(fp));\r
-     \r
-          /* Finished reading. Was it due to an error? */\r
-          if (ferror(fp))\r
-          {\r
-               fputs("\nERROR: Reading source file\n",stderr);\r
-               fclose(fp);\r
-               exit(0);\r
-          }\r
-     \r
-          /* Reading finished due to EOF, quite valid so print count */\r
-          printf("\nFile %s contains %ld words\n",argv[1],count);\r
-          fclose(fp);\r
-     }\r
-     \r
-\r
-\r
-\r
-Converting Numbers To And From Strings\r
-\r
-All C compilers provide a facility for converting numbers to strings.\r
-This being sprintf(). However, as happens sprintf() is a multi-purpose\r
-function that is therefore large and slow. The following function ITOS()\r
-accepts two parameters, the first being a signed integer and the second\r
-being a pointer to a character string. It then copies the integer into\r
-the memory pointed to by the character pointer. As with sprintf() ITOS()\r
-does not check that the target string is long enough to accept the result\r
-of the conversion. You should then ensure that the target string is long\r
-enough.\r
-\r
-Example function for copying a signed integer into a string;\r
-\r
-     void ITOS(long x, char *ptr)\r
-     {\r
-          /* Convert a signed decimal integer to a string */\r
-     \r
-          long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,\r
-     1000, 100, 10, 1 };\r
-          int n;\r
-     \r
-          /* Check sign */\r
-          if (x < 0)\r
-          {\r
-               *ptr++ = '-';\r
-               /* Convert x to absolute */\r
-               x = 0 - x;\r
-          }\r
-     \r
-          for(n = 0; n < 9; n++)\r
-          {\r
-               if (x > pt[n])\r
-               {\r
-                    *ptr++ = '0' + x / pt[n];\r
-                    x %= pt[n];\r
-               }\r
-          }\r
-          return;\r
-     }\r
-     \r
-To convert a string to a floating point number, C provides two functions;\r
-atof() and strtod(). atof() has the prototype;\r
-\r
-     double atof(const char *s);\r
-\r
-strtod has the prototype;\r
-\r
-     double strtod(const char *s,char **endptr);\r
-\r
-Both functions scan the string and convert it as far as they can, until\r
-they come across a character they don't understand. The difference\r
-between the two functions is that if strtod() is passed a character\r
-pointer for parameter `endptr', it sets that pointer to the first\r
-character in the string that terminated the conversion. Because of its\r
-better error reporting, by way of endptr, strtod() is often preferred to\r
-atof().\r
-\r
-To convert a string to an integer use atoi() that has the prototype;\r
-\r
-\r
-     int atoi(const char *s);\r
-\r
-atoi() does not check for an overflow, and the results are undefined!\r
-\r
-atol() is a similar function but returns a long. Alternatively, you can\r
-use strtol() and stroul() instead that have better error checking.\r
-\r
-                                    \r
-                              TEXT HANDLING\r
-\r
-Human languages write information down as `text'. This is comprised of\r
-words, figures and punctuation. The words being made up of upper case and\r
-lower case letters. Processing text with a computer is a commonly\r
-required task, and yet quite a difficult one.\r
-\r
-The ANSI C definitions include string processing functions that are by\r
-their nature sensitive to case. That is the letter `A' is seen as\r
-distinct from the letter `a'. This is the first problem that must be\r
-overcome by the programmer. Fortunately both Borland's Turbo C compilers\r
-and Microsoft's C compilers include case insensitive forms of the string\r
-functions.\r
-\r
-stricmp() for example is the case insensitive form of strcmp(), and\r
-strnicmp() is the case insensitive form of strncmp().\r
-\r
-If you are concerned about writing portable code, then you must restrict\r
-yourself to the ANSI C functions, and write your own case insensitive\r
-functions using the tools provided.\r
-\r
-Here is a simple implementation of a case insensitive version of\r
-strstr().  The function simply makes a copy of the parameter strings,\r
-converts those copies both to upper case and then does a standard\r
-strstr() on the copies.  The offset of the target string within the\r
-source string will be the same for the copy as the original, and so it\r
-can be returned relative to the parameter string.\r
-\r
-\r
-     char *stristr(char *s1, char *s2)\r
-     {\r
-          char c1[1000];\r
-          char c2[1000];\r
-          char *p;\r
-     \r
-          strcpy(c1,s1);\r
-          strcpy(c2,s2);\r
-     \r
-          strupr(c1);\r
-          strupr(c2);\r
-     \r
-          p = strstr(c1,c2);\r
-          if (p)\r
-               return s1 + (p - c1);\r
-          return NULL;\r
-     }\r
-     \r
-This function scans a string, s1 looking for the word held in s2. The\r
-word must be a complete word, not simply a character pattern, for the\r
-function to return true. It makes use of the stristr() function described\r
-previously.\r
-\r
-     int word_in(char *s1,char *s2)\r
-     {\r
-          /* return non-zero if s2 occurs as a word in s1 */\r
-          char *p;\r
-          char *q;\r
-          int ok;\r
-     \r
-          ok = 0;\r
-          q = s1;\r
-     \r
-          do\r
-          {\r
-               /* Locate character occurence s2 in s1 */\r
-               p = stristr(q,s2);\r
-               if (p)\r
-               {\r
-                    /* Found */\r
-                    ok = 1;\r
-     \r
-                    if (p > s1)\r
-                    {\r
-                         /* Check previous character */\r
-                         if (*(p - 1) >= 'A' && *(p - 1) <= 'z')\r
-                              ok = 0;\r
-                    }\r
-     \r
-                    /* Move p to end of character set */\r
-                    p += strlen(s2);\r
-                    if (*p)\r
-                    {\r
-                         /* Check character following */\r
-                         if (*p >= 'A' && *p <= 'z')\r
-                              ok = 0;\r
-                    }\r
-               }\r
-               q = p;\r
-          }\r
-          while(p && !ok);\r
-          return ok;\r
-     }\r
-\r
-\r
-Some more useful functions for dealing with text are truncstr() that\r
-truncates a string;\r
-\r
-     void truncstr(char *p,int num)\r
-     {\r
-          /* Truncate string by losing last num characters */\r
-          if (num < strlen(p))\r
-               p[strlen(p) - num] = 0;\r
-     }\r
-     \r
-trim() that removes trailing spaces from the end of a string;\r
-\r
-     void trim(char *text)\r
-     {\r
-          /* remove trailing spaces */\r
-          char *p;\r
-     \r
-          p = &text[strlen(text) - 1];\r
-          while(*p == 32 && p >= text)\r
-               *p-- = 0;\r
-     }\r
-     \r
-strlench() that changes the length of a string by adding or deleting\r
-characters;\r
-\r
-     void strlench(char *p,int num)\r
-     {\r
-          /* Change length of string by adding or deleting characters */\r
-     \r
-          if (num > 0)\r
-               memmove(p + num,p,strlen(p) + 1);\r
-          else\r
-          {\r
-               num = 0 - num;\r
-               memmove(p,p + num,strlen(p) + 1);\r
-          }\r
-     }\r
-     \r
-strins() that inserts a string into another string;\r
-\r
-     void strins(char *p, char *q)\r
-     {\r
-          /* Insert string q into p */\r
-          strlench(p,strlen(q));\r
-          strncpy(p,q,strlen(q));\r
-     }\r
-     \r
-strchg() that replaces all occurences of one sub-string with another\r
-within a target string;\r
-\r
-     void strchg(char *data, char *s1, char *s2)\r
-     {\r
-          /* Replace all occurences of s1 with s2 */\r
-          char *p;\r
-          char changed;\r
-     \r
-          do\r
-          {\r
-               changed = 0;\r
-               p = strstr(data,s1);\r
-               if (p)\r
-               {\r
-                    /* Delete original string */\r
-                    strlench(p,0 - strlen(s1));\r
-     \r
-                    /* Insert replacement string */\r
-                    strins(p,s2);\r
-                    changed = 1;\r
-               }\r
-          }\r
-          while(changed);\r
-     }\r
-                                    \r
-                                  TIME\r
-\r
-C provides a function, time(), which reads the computer's system clock to\r
-return the system time as a number of seconds since midnight on January\r
-the first, 1970. However, this value can be converted to a useful string\r
-by the function ctime() as illustrated in the following example;\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     \r
-     int main()\r
-     {\r
-          /* Structure to hold time, as defined in time.h  */\r
-          time_t t;\r
-     \r
-          /* Get system date and time from computer */\r
-          t = time(NULL);\r
-          printf("Today's date and time: %s\n",ctime(&t));\r
-     }\r
-     \r
-The string returned by ctime() is comprised of seven fields;\r
-\r
-     Day of the week,\r
-     Month of the year,\r
-     Date of the day of the month,\r
-     hour,\r
-     minutes,\r
-     seconds,\r
-     century of the year\r
-\r
-terminated by a newline character and null terminating byte. Since the\r
-fields always occupy the same width, slicing operations can be carried\r
-out on the string with ease. The following program defines a structure\r
-`time' and a function gettime() that extracts the hours, minutes and\r
-seconds of the current time and places them in the structure;\r
-\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     \r
-     struct time\r
-     {\r
-          int ti_min;         /* Minutes */\r
-          int ti_hour;        /* Hours */\r
-          int ti_sec;         /* Seconds */\r
-     };\r
-     \r
-     void gettime(struct time *now)\r
-     {\r
-          time_t t;\r
-          char temp[26];\r
-          char *ts;\r
-     \r
-          /* Get system date and time from computer */\r
-          t = time(NULL);\r
-     \r
-          /* Translate dat and time into a string */\r
-          strcpy(temp,ctime(&t));\r
-     \r
-          /* Copy out just time part of string */\r
-          temp[19] = 0;\r
-          ts = &temp[11];\r
-     \r
-          /* Scan time string and copy into time structure */\r
-          sscanf(ts,"%2d:%2d:%2d",&now->ti_hour,&now->ti_min,&now-\r
-     >ti_sec);\r
-     }\r
-     \r
-     int main()\r
-     {\r
-          struct time now;\r
-     \r
-          gettime(&now);\r
-     \r
-          printf("\nThe time is\r
-     %02d:%02d:%02d",now.ti_hour,now.ti_min,now.ti_sec);\r
-     \r
-     }\r
-\r
-The ANSI standard on C does actually provide a function ready made to\r
-convert the value returned by time() into a structure;\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     \r
-     int main()\r
-     {\r
-          time_t t;\r
-          struct tm *tb;\r
-     \r
-          /* Get time into t */\r
-          t = time(NULL);\r
-     \r
-          /* Convert time value t into structure pointed to by tb */\r
-          tb = localtime(&t);\r
-     \r
-          printf("\nTime is %02d:%02d:%02d",tb->tm_hour,tb->tm_min,tb-\r
-     >tm_sec);\r
-     }\r
-     \r
-The structure 'tm' is defined in time.h as;\r
-\r
-     struct tm\r
-     {\r
-          int tm_sec;\r
-          int tm_min;\r
-          int tm_hour;\r
-          int tm_mday;\r
-          int tm_mon;\r
-          int tm_year;\r
-          int tm_wday;\r
-          int tm_yday;\r
-          int tm_isdst;\r
-     };\r
-\r
-\r
-Timers\r
-Often a program must determine the date and time from the host computer's\r
-non-volatile RAM. There are several time functions provided by the ANSI\r
-standard on C that allow a program to retrieve, from the host computer,\r
-the current date and time;\r
-\r
-time() returns the number of seconds that have elapsed since midnight on\r
-January the 1st 1970. It has the prototype;\r
-\r
-     time_t time(time_t *timer);\r
-\r
-time() fills in the time_t variable sent as a parameter and returns the\r
-same value. You can call time() with a NULL parameter and just collect\r
-the return value thus;\r
-\r
-     #include <time.h>\r
-     \r
-     void main()\r
-     {\r
-          time_t now;\r
-     \r
-          now = time(NULL);\r
-     }\r
-     \r
-asctime() converts a time block to a twenty six character string of the\r
-format;\r
-\r
-                Wed Oct 14 10:23:45 1992\n\0\r
-\r
-asctime() has the prototype;\r
-\r
-               char *asctime(const struct tm *tblock);\r
-\r
-ctime() converts a time value (as returned by time()) into a twenty six\r
-chracter string of the same format as asctime(). For example;\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     \r
-     void main()\r
-     {\r
-          time_t now;\r
-          char date[30];\r
-     \r
-          now = time(NULL);\r
-          strcpy(date,ctime(&now));\r
-     }\r
-     \r
-difftime() returns the difference, in seconds, between two values (as\r
-returned by time()). This can be useful for testing the elapsed time\r
-between two events, the time a function takes to execute, and for\r
-creating consistent delays that are irrelevant of the host computer.\r
-\r
-An example delay program;\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     \r
-     \r
-     void DELAY(int period)\r
-     {\r
-          time_t start;\r
-     \r
-          start = time(NULL);\r
-          while(time(NULL) < start + period)\r
-               ;\r
-     }\r
-     \r
-     void main()\r
-     {\r
-          printf("\nStarting delay now....(please wait 5 seconds)");\r
-     \r
-          DELAY(5);\r
-     \r
-          puts("\nOkay, I've finished!");\r
-     }\r
-\r
-gmtime() converts a local time value (as returned by time()) to the GMT\r
-time and stores it in a time block. This function depends upon the global\r
-variable timezone being set.\r
-\r
-\r
-The time block is a predefined structure (declared in time.h) as follows;\r
-\r
-     struct tm\r
-     {\r
-          int tm_sec;\r
-          int tm_min;\r
-          int tm_hour;\r
-          int tm_mday;\r
-          int tm_mon;\r
-          int tm_year;\r
-          int tm_wday;\r
-          int tm_yday;\r
-          int tm_isdst;\r
-     };\r
-\r
-tm_mday records the day of the month, ranging from 1 to 31; tm_wday is\r
-the day of the week with Sunday being represented by 0; the year is\r
-recorded less 1900; tm_isdst is a flag to show whether daylight saving\r
-time is in effect. The actual names of the structure and its elements may\r
-vary from compiler to compiler, but the structure should be the same in\r
-essence.\r
-\r
-mktime() converts a time block to a calendar format. It follows the\r
-prototype;\r
-\r
-                time_t mktime(struct tm *t);\r
-\r
-The following example allows entry of a date, and uses mktime() to\r
-calculate the day of the week appropriate to that date. Only dates from\r
-the first of January 1970 are recognisable by the time functions.\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     #include <string.h>\r
-     \r
-     void main()\r
-     {\r
-          struct tm tsruct;\r
-          int okay;\r
-          char data[100];\r
-          char *p;\r
-          char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday",\r
-     "Thursday", "Friday", "Saturday" ,\r
-                    "prior to 1970, thus not known" };\r
-          do\r
-          {\r
-               okay = 0;\r
-               printf("\nEnter a date as dd/mm/yy ");\r
-               p = fgets(data,8,stdin);\r
-               p = strtok(data,"/");\r
-     \r
-               if (p != NULL)\r
-                    tsruct.tm_mday = atoi(p);\r
-               else\r
-                    continue;\r
-     \r
-               p = strtok(NULL,"/");\r
-               if (p != NULL)\r
-                    tsruct.tm_mon = atoi(p);\r
-               else\r
-                    continue;\r
-     \r
-               p = strtok(NULL,"/");\r
-     \r
-               if (p != NULL)\r
-                    tsruct.tm_year = atoi(p);\r
-               else\r
-                    continue;\r
-               okay = 1;\r
-          }\r
-          while(!okay);\r
-     \r
-          tsruct.tm_hour = 0;\r
-          tsruct.tm_min = 0;\r
-          tsruct.tm_sec = 1;\r
-          tsruct.tm_isdst = -1;\r
-     \r
-          /* Now get day of the week */\r
-          if (mktime(&tsruct) == -1)\r
-          tsruct.tm_wday = 7;\r
-     \r
-          printf("That was %s\n",wday[tsruct.tm_wday]);\r
-     }\r
-     \r
-mktime() also makes the neccessary adjustments for values out of range,\r
-this can be utilised for discovering what the date will be in n number of\r
-days time thus;\r
-\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     #include <string.h>\r
-     \r
-     void main()\r
-     {\r
-          struct tm *tsruct;\r
-          time_t today;\r
-     \r
-          today = time(NULL);\r
-          tsruct = localtime(&today);\r
-     \r
-          tsruct->tm_mday += 10;\r
-          mktime(tsruct);\r
-     \r
-          printf("In ten days it will be %02d/%02d/%2d\n", tsruct-\r
-     >tm_mday,tsruct->tm_mon + 1,tsruct->tm_year);\r
-     \r
-     }\r
-     \r
-\r
-This program uses Julian Dates to decide any day of the week since the\r
-1st of October 1582 when the Gregorian calendar was introduced.\r
-\r
-     char *WDAY(int day, int month, int year)\r
-     {\r
-          /* Returns a pointer to a string representing the day of the\r
-     week */\r
-     \r
-          static char *cday[] = { "Saturday","Sunday","Monday","Tuesday",\r
-     "Wednesday","Thursday","Friday" };\r
-          double yy;\r
-          double yt;\r
-          double j;\r
-          int y1;\r
-          int y4;\r
-          int x;\r
-     \r
-          yy = year / 100;\r
-          y1 = (int)(yy);\r
-          yt = year / 400;\r
-          y4 = (int)(yt);\r
-          x = 0;\r
-     \r
-          if (month < 3)\r
-          {\r
-               year--;\r
-               x = 12;\r
-          }\r
-     \r
-          j = day + (int)(365.25*year);\r
-     \r
-          j += (int)(30.6001 * (month + 1 + x)) - y1 + y4;\r
-     \r
-          if (yy == y1 && yt != y4 && month < 3)\r
-               j++;\r
-     \r
-          j = 1 + j - 7 * (int)(j/7);\r
-     \r
-          if (j > 6)\r
-               j -= 7;\r
-     \r
-          return(cday[j]);\r
-     }\r
-     \r
-     \r
-With time() and difftime() we can create a timer for testing the\r
-execution times of functions thus;\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     \r
-     main()\r
-     {\r
-          time_t now;\r
-          time_t then;\r
-          double elapsed;\r
-     \r
-          int n;\r
-     \r
-          now = time(NULL);\r
-     \r
-          /* This loop is adjustable for multiple passes */\r
-          for(n = 0; n < 5000; n++)\r
-               /* Call the function to test */\r
-               func();\r
-     \r
-          then = time(NULL);\r
-     \r
-          elapsed = difftime(then,now);\r
-          printf("\nElapsed seconds==%lf\n",elapsed);\r
-     }\r
-     \r
-By way of time() and ctime() the current system date and time can be\r
-retrieved from the host computer thus;\r
-\r
-     #include <stdio.h>\r
-     #include <time.h>\r
-     \r
-     main()\r
-     {\r
-          time_t now;\r
-          char *date;\r
-          int n;\r
-     \r
-          /* Get system time */\r
-          now = time(NULL);\r
-     \r
-          /* Convert system time to a string */\r
-          date = ctime(&now);\r
-     \r
-          /*Display system time */\r
-          printf("\nIt is %s",date);\r
-     }\r
-     \r
-time_t is a type defined in time.h as the type of variable returned by\r
-time(). This type may vary from compiler to compiler, and therefore is\r
-represented by the type "time_t".\r
-                                    \r
-                              HEADER FILES\r
-\r
-Function prototypes for library functions supplied with the C compiler,\r
-and standard macros are declared in header files.\r
-\r
-The ANSI standard on the C programming language lists the following\r
-header files;\r
-\r
-Header file    Description\r
-               \r
-assert.h       Defines the assert debugging macro\r
-ctype.h        Character classification and\r
-               conversion macros\r
-errno.h        Constant mnemonics for error codes\r
-float.h        Defines implementation specific\r
-               macros for dealing with floating\r
-               point mathematics\r
-limits.h       Defines implementation specific\r
-               limits on type values\r
-locale.h       Country specific parameters\r
-math.h         Prototypes for mathematics functions\r
-setjmp.h       Defines typedef and functions for\r
-               setjmp/longjmp\r
-signal.h       Constants and declarations for use by\r
-               signal() and raise()\r
-stdarg.h       Macros for dealing with argument\r
-               lists\r
-stddef.h       Common data types and macros\r
-stdio.h        Types and macros required for\r
-               standard I/O\r
-stdlib.h       Prototypes of commonly used functions\r
-               and miscellany\r
-string.h       String manipulation function\r
-               prototypes\r
-time.h         Structures for time conversion\r
-               routines\r
-                                    \r
-                                DEBUGGING\r
-\r
-The ANSI standard on C includes a macro function for debugging called\r
-assert(). This expands to an if() statement, which if it returns true\r
-terminates the program and outputs to the standard error stream a message\r
-comprised of:\r
-\r
-\r
-     Assertion failed: <test>, file <module>, line <line number>\r
-     Abnormal program termination\r
-For example, the following program accidentally assigns a zero value to a\r
-pointer!\r
-\r
-     #include <stdio.h>\r
-     #include <assert.h>\r
-     \r
-     main()\r
-     {\r
-          /* Demonstration of assert */\r
-     \r
-          int *ptr;\r
-          int x;\r
-     \r
-          x = 0;\r
-     \r
-          /* Whoops! error in this line! */\r
-          ptr = x;\r
-     \r
-          assert(ptr != NULL);\r
-     }\r
-     \r
-When run, this program terminates with the message:\r
-\r
-     Assertion failed: ptr != 0, file TEST.C, line 16\r
-     Abnormal program termination\r
-\r
-When a program is running okay, the assert() functions can be removed\r
-from the compiled program by simply adding the line;\r
-\r
-     #define NDEBUG\r
-\r
-before the #include <assert.h> line. Effectively the assert functions are\r
-commented out in the preprocessed source before compilation, this means\r
-that the assert expressions are not evaluated, and thus cannot cause any\r
-side effects.\r
-                                    \r
-                               FLOAT ERRORS\r
-\r
-Floating point numbers are decimal fractions, decimal fractions do not\r
-accurately equate to normal fractions as not every number will divide\r
-precisely by ten. This creates the potential for rounding errors in\r
-calculations that use floating point numbers. The following program\r
-illustrates one such example of rounding error problems;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     void main()\r
-     {\r
-          float number;\r
-     \r
-          for(number = 1; number > 0.4; number -= 0.01)\r
-               printf("\n%f",number);\r
-     }\r
-     \r
-At about 0.47 (depending upon the host computer and compiler) the program\r
-starts to store an inaccurate value for 'number'.\r
-\r
-This problem can be minimised by using longer floating point numbers,\r
-doubles or long doubles that have larger storage space allocated to them.\r
-For really accurate work though, you should use integers and only convert\r
-to a floating point number for display. You also should notice that most\r
-C compilers default floating point numbers to `doubles' and when using\r
-`float' types have to convert the double down to a float!\r
-\r
-\r
-                                    \r
-                             ERROR HANDLING\r
-\r
-When a system error occurs within a program, for example when an attempt\r
-to open a file fails, it is helpful to the program's user to display a\r
-message reporting the failure. Equally it is useful to the program's\r
-developer to know why the error occurred, or at least as much about it as\r
-possible. To this end the ANSI standard on C describes a function,\r
-perror(), which has the prototype;\r
-\r
-\r
-     void perror(const char *s);\r
-\r
-and is used to display an error message. The program's own prefixed error\r
-message is passed to perror() as the string parameter. This error message\r
-is displayed by perror() followed by the host's system error separated by\r
-a colon. The following example illustrates a use for perror();\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     void main()\r
-     {\r
-          FILE *fp;\r
-          char fname[] = "none.xyz";\r
-     \r
-          fp = fopen(fname,"r");\r
-     \r
-          if(!fp)\r
-               perror(fname);\r
-          return;\r
-     }\r
-     \r
-If the fopen() operation fails, a message similar to;\r
-\r
-     none.xyz: No such file or directory\r
-\r
-is displayed.\r
-\r
-You should note, perror() sends its output to the predefined stream\r
-`stderr', which is usually the host computer's display unit.\r
-\r
-\r
-perror() finds its message from the host computer via the global variable\r
-'errno' that is set by most, but not all system functions.\r
-\r
-Unpleasant errors might justify the use of abort(). abort() is a function\r
-that terminates the running program with a message;\r
-\r
-     "Abnormal program termination"\r
-\r
-and returns an exit code of 3 to the parent process or operating system.\r
-\r
-\r
-\r
-Critical Error Handling With The IBM PC AND DOS\r
-\r
-The IBM PC DOS operating system provides a user amendable critical error\r
-handling function. This function is usually discovered by attempting to\r
-write to a disk drive that does not have a disk in it, in which case the\r
-familiar;\r
-\r
-     Not ready error writing drive A\r
-     Abort Retry Ignore?\r
-\r
-Message is displayed on the screen. Fine when it occurs from within a DOS\r
-program, not so fine from within your own program!\r
-\r
-The following example program shows how to redirect the DOS critical\r
-error interrupt to your own function;\r
-\r
-\r
-     /* DOS critical error handler test */\r
-     \r
-     #include <stdio.h>\r
-     #include <dos.h>\r
-     \r
-     void interrupt new_int();\r
-     void interrupt (*old_int)();\r
-     \r
-     char status;\r
-     \r
-     main()\r
-     {\r
-          FILE *fp;\r
-     \r
-          old_int = getvect(0x24);\r
-     \r
-          /* Set critical error handler to my function */\r
-          setvect(0x24,new_int);\r
-     \r
-          /* Generate an error by not having a disc in drive A */\r
-          fp = fopen("a:\\data.txt","w+");\r
-     \r
-          /* Display error status returned */\r
-          printf("\nStatus ==  %d",status);\r
-     \r
-     }\r
-     \r
-     void interrupt new_int()\r
-     {\r
-          /* set global error code */\r
-          status = _DI;\r
-     \r
-          /* ignore error and return */\r
-          _AL = 0;\r
-     }\r
-     \r
-When the DOS critical error interrupt is called, a status message is\r
-passed in the low byte of the DI register. This message is one of;\r
-\r
-Code                     Meaning\r
-                         \r
-00                       Write-protect error\r
-01                       Unknown unit\r
-02                       Drive not ready\r
-03                       Unknown command\r
-04                       Data error, bad CRC\r
-05                       Bad request structure\r
-                         length\r
-06                       Seek error\r
-07                       Unknown media type\r
-08                       Sector not found\r
-09                       Printer out of paper\r
-0A                       Write error\r
-0B                       Read error\r
-0C                       General failure\r
-\r
-Your critical error interrupt handler can transfer this status message\r
-into a global variable, and then set the result message held in register\r
-AL to one of;\r
-\r
-\r
-Code                     Action\r
-                         \r
-00                       Ignore error\r
-01                       Retry\r
-02                       Terminate program\r
-03                       Fail (Available with\r
-                         DOS 3.3 and above)\r
-\r
-\r
-If you choose to set AL to 02, terminate program, you should ensure ALL\r
-files are closed first since DOS will terminate the program abruptly,\r
-leaving files open and memory allocated, not a pretty state to be in!\r
-\r
-\r
-The example program shown returns an ignore status from the critical\r
-error interrupt, and leaves the checking of any errors to the program\r
-itself. So, in this example after the call to fopen() we could check the\r
-return value in fp, and if it reveals an error (NULL in this case) we\r
-could then check the global variable status and act accordingly, perhaps\r
-displaying a polite message to the user to put a disk in the floppy drive\r
-and ensure that the door is closed.\r
-\r
-The following is a practical function for checking whether a specified\r
-disc drive can be accessed. It should be used with the earlier critical\r
-error handler and global variable `status'.\r
-\r
-     int DISCOK(int drive)\r
-     {\r
-          /* Checks for whether a disc can be read */\r
-          /* Returns false (zero) on error */\r
-          /* Thus if(!DISCOK(drive)) */\r
-          /*          error();  */\r
-     \r
-          unsigned char buffer[25];\r
-     \r
-          /* Assume okay */\r
-          status = 0;\r
-     \r
-          /* If already logged to disc, return okay */\r
-          if ('A' + drive == diry[0])\r
-               return(1);\r
-     \r
-          /* Attempt to read disc */\r
-          memset(buffer,0,20);\r
-          sprintf(buffer,"%c:$$$.$$$",'A'+drive);\r
-     \r
-          _open(buffer,O_RDONLY);\r
-     \r
-          /* Check critical error handler status */\r
-          if (status == 0)\r
-               return(1);\r
-     \r
-          /* Disc cannot be read */\r
-          return(0);\r
-     }\r
-                                    \r
-                                  CAST\r
-\r
-\r
-Casting tells the compiler what a data type is, and can be used to change\r
-a data type. For example, consider the following;\r
-\r
-     #include <stdio.h>\r
-     \r
-     void main()\r
-     {\r
-          int x;\r
-          int y;\r
-     \r
-          x = 10;\r
-          y = 3;\r
-     \r
-          printf("\n%lf",x / y);\r
-     }\r
-\r
-The printf() function has been told to expect a double. However, the\r
-compiler sees the variables `x' and `y' as integers, and an error occurs!\r
-To make this example work we must tell the compiler that the result of\r
-the expression x / y is a double, this is done with a cast thus;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     void main()\r
-     {\r
-          int x;\r
-          int y;\r
-     \r
-          x = 10;\r
-          y = 3;\r
-     \r
-          printf("\n%lf",(double)(x / y));\r
-     }\r
-\r
-Notice the data type `double' is enclosed by parenthesis, and so is the\r
-expression to convert. But now, the compiler knows that the result of the\r
-expression is a double, but it still knows that the variables `x' and `y'\r
-are integers and so an integer division will be carried out. We have to\r
-cast the constants thus;\r
-\r
-     #include <stdio.h>\r
-     \r
-     void main()\r
-     {\r
-          int x;\r
-          int y;\r
-     \r
-          x = 10;\r
-          y = 3;\r
-     \r
-          printf("\n%lf",(double)(x) / (double)(y));\r
-     }\r
-\r
-Because both of the constants are doubles, the compiler knows that the\r
-outcome of the expression will also be a double.\r
-\r
-\r
-\r
-                                    \r
-                      THE IMPORTANCE OF PROTOTYPING\r
-\r
-Prototyping a function involves letting the compiler know in advance what\r
-type of values a function will receive and return. For example, lets look\r
-at strtok(). This has the prototype;\r
-\r
-\r
-     char *strtok(char *s1, const char *s2);\r
-\r
-This prototype tells the compiler that strtok() will return a character\r
-pointer, the first received parameter will be a pointer to a character\r
-string, and that string can be changed by strtok(), and the last\r
-parameter will be a pointer to a character string that strtok() cannot\r
-change.\r
-\r
-The compiler knows how much space to allocate for the return parameter,\r
-sizeof(char *), but without a prototype for the function the compiler\r
-will assume that the return value of strtok() is an integer, and will\r
-allocate space for a return type of int, that is sizeof(int). If an\r
-integer and a character pointer occupy the same number of bytes on the\r
-host computer no major problems will occur, but if a character pointer\r
-occupies more space than an integer, then the compiler wont have\r
-allocated enough space for the return value and the return from a call to\r
-strtok() will overwrite some other bit of memory. If, as so often happens\r
-the return value is returned via the stack, the results of confusing the\r
-compiler can be disastrous!\r
-\r
-Thankfully most C compilers will warn the programmer if a call to a\r
-function has been made without a prototype, so that you can add the\r
-required function prototypes.\r
-\r
-Consider the following example that will not compile on most modern C\r
-compilers due to the nasty error in it;\r
-\r
-\r
-     #include <stdio.h>\r
-     \r
-     int FUNCA(int x, int y)\r
-     {\r
-          return(MULT(x,y));\r
-     }\r
-     \r
-     double MULT(double x, double y)\r
-     {\r
-          return(x * y);\r
-     }\r
-     \r
-     \r
-     main()\r
-     {\r
-          printf("\n%d",FUNCA(5,5));\r
-     }\r
-     \r
-When the compiler first encounters the function MULT() it is as a call\r
-from within FUNCA(). In the absence of any prototype for MULT() the\r
-compiler assumes that MULT() returns an integer. When the compiler finds\r
-the definition for function MULT() it sees that a return of type double\r
-has been declared. The compiler then reports an error in the compilation\r
-saying something like;\r
-\r
-\r
-     "Type mismatch in redclaration of function 'MULT'"\r
-\r
-What the compiler is really trying to say is, prototype your functions\r
-before using them! If this example did compile, and was then run it\r
-probably would crash the computer's stack and cause a system hang.\r
-\r
-                                    \r
-                          POINTERS TO FUNCTIONS\r
-\r
-C allows a pointer to point to the address of a function, and this\r
-pointer to be called rather than specifying the function. This is used by\r
-interrupt changing functions and may be used for indexing functions\r
-rather than using switch statements. For example;\r
-\r
-     #include <stdio.h>\r
-     #include <math.h>\r
-     \r
-     double (*fp[7])(double x);\r
-     \r
-     void main()\r
-     {\r
-          double x;\r
-          int p;\r
-     \r
-          fp[0] = sin;\r
-          fp[1] = cos;\r
-          fp[2] = acos;\r
-          fp[3] = asin;\r
-          fp[4] = tan;\r
-          fp[5] = atan;\r
-          fp[6] = ceil;\r
-     \r
-          p = 4;\r
-     \r
-          x = fp[p](1.5);\r
-          printf("\nResult %lf",x);\r
-     }\r
-\r
-This example program defines an array of pointers to functions, (*fp[])()\r
-that are then called dependant upon the value in the indexing variable p.\r
-This program could also be written;\r
-\r
-     #include <stdio.h>\r
-     #include <math.h>\r
-     \r
-     void main()\r
-     {\r
-          double x;\r
-          int p;\r
-     \r
-          p = 4;\r
-     \r
-          switch(p)\r
-          {\r
-               case 0 :  x = sin(1.5);\r
-                     break;\r
-               case 1 :  x = cos(1.5);\r
-                     break;\r
-               case 2 :  x = acos(1.5);\r
-                     break;\r
-               case 3 :  x = asin(1.5);\r
-                     break;\r
-               case 4 :  x = tan(1.5);\r
-                     break;\r
-               case 5 :  x = atan(1.5);\r
-                     break;\r
-               case 6 :  x = ceil(1.5);\r
-                     break;\r
-          }\r
-          puts("\nResult %lf",x);\r
-     }\r
-\r
-The first example, using pointers to the functions, compiles into much\r
-smaller code and executes faster than the second example.\r
-\r
-The table of pointers to functions is a useful facility when writing\r
-language interpreters, the program compares an entered instruction\r
-against a table of key words that results in an index variable being set\r
-and then the program simply needs to call the function pointer indexed by\r
-the variable, rather than wading through a lengthy switch() statement.\r
-\r
-                                    \r
-                            DANGEROUS PITFALLS\r
-\r
-One of the most dangerous pitfalls can occur with the use of gets(). This\r
-function accepts input from the stream stdin until it receives a newline\r
-character, which it does not pass to the program. All the data it\r
-receives is stored in memory starting at the address of the specified\r
-string, and quite happily overflowing into other variables! This danger\r
-can be avoided by using fgets() that allows a maximum number of\r
-characters to be specified, so you can avoid overflow problems. Notice\r
-though that fgets() does retain the newline character scanf() is another\r
-function best avoided. It accepts input from stdin and stores the\r
-received data at the addresses provided to it. If those addresses are not\r
-really addresses where the data ends up is anybodys guess!\r
-\r
-This example is okay, since scanf() has been told to store the data at\r
-the addresses occupied by the two variables `x' and `y'.\r
-\r
-\r
-     void main()\r
-     {\r
-          int x;\r
-          int y;\r
-     \r
-          scanf("%d%d",&x,&y);\r
-     }\r
-     \r
-But in this example scanf() has been told to store the data at the\r
-addresses suggested by the current values of `x' and `y'! An easy and\r
-common mistake to make, and yet one that can have very peculiar effects.\r
-\r
-\r
-     void main()\r
-     {\r
-          int x;\r
-          int y;\r
-     \r
-          scanf("%d%d",x,y);\r
-     }\r
-     \r
-The answer is, don't use scanf(), use fgets() and parse your string\r
-manually using the standard library functions strtod(), strtol() and\r
-strtoul().\r
-\r
-Here is the basis of a simple input string parser that returns the\r
-individual input fields from an entered string;\r
-\r
-     #include <stdio.h>\r
-     #include <string.h>\r
-     \r
-     void main()\r
-     {\r
-          char input[80];\r
-          char *p;\r
-     \r
-          puts("\nEnter a string ");\r
-          fgets(input,79,stdin);\r
-     \r
-          /* now parse string for input fields */\r
-          puts("The fields entered are:");\r
-          p = strtok(input,", ");\r
-          while(p)\r
-          {\r
-               puts(p);\r
-               p = strtok(NULL,", ");\r
-          }\r
-     }\r
-     \r
-                                    \r
-                                 SIZEOF\r
-\r
-A preprocessor instruction, `sizeof', returns the size of an item, be it\r
-a structure, pointer, string or whatever. However! take care when using\r
-`sizeof'. Consider the following program;\r
-\r
-\r
-     #include <stdio.h>\r
-     #include <mem.h>\r
-     \r
-     char string1[80]; char *text = "This is a string of data" ;\r
-     \r
-     void main()\r
-     {\r
-          /* Initialise string1 correctly */\r
-          memset(string1,0,sizeof(string1));\r
-     \r
-          /* Copy some text into string1 ? */\r
-          memcpy(string1,text,sizeof(text));\r
-     \r
-          /* Display string1 */\r
-          printf("\nString 1 = %s\n",string1);\r
-     }\r
-     \r
-What it is meant to do is initialise all 80 elements of string1 to\r
-zeroes, which it does alright, and then copy the constant string `text'\r
-into the variable `string1'. However, variable text is a pointer, and so\r
-the sizeof(text) instruction returns the size of the character pointer\r
-(perhaps two bytes) rather than the length of the string pointed to by\r
-the pointer!  If the length of the string pointed to by `text' happened\r
-to be the same as the size of a character pointer then no error would be\r
-noticed.\r
-\r
-                                    \r
-                               INTERRUPTS\r
-\r
-The IBM PC BIOS and DOS contain functions that may be called by a program\r
-by way of the function's interrupt number. The address of the function\r
-assigned to each interrupt is recorded in a table in RAM called the\r
-interrupt vector table. By changing the address of an interrupt vector a\r
-program can effectively disable the original interrupt function and\r
-divert any calls to it to its own function. This was done by the critical\r
-error handler described in the section on error handling.\r
-\r
-Borland's Turbo C provides two library functions for reading and changing\r
-an interrupt vector. These are: setvect() and getvect(). The\r
-corresponding Microsoft C library functions are: _dos_getvect() and\r
-_dos_setvect().\r
-\r
-getvect() has the function prototype;\r
-\r
-     void interrupt(*getvect(int interrupt_no))();\r
-\r
-setvect() has the prototype;\r
-\r
-     void setvect(int interrupt_no, void interrupt(*func)());\r
-\r
-To read and save the address of an existing interrupt a program uses\r
-getvect() thus;\r
-\r
-     /* Declare variable to record old interrupt */\r
-     void interrupt(*old)(void);\r
-     \r
-     main()\r
-     {\r
-          /* get old interrupt vector */\r
-          old = getvect(0x1C);\r
-          .\r
-          .\r
-          .\r
-     }\r
-     \r
-Where 0x1C is the interrupt vector to be retrieved.\r
-\r
-To then set the interrupt vector to a new address, our own function, we\r
-use setvect() thus;\r
-\r
-     void interrupt new(void)\r
-     {\r
-          .\r
-          .\r
-          /* New interrupt function */\r
-          .\r
-          .\r
-          .\r
-     }\r
-     \r
-     main()\r
-     {\r
-          .\r
-          .\r
-          .\r
-          setvect(0x1C,new);\r
-          .\r
-          .\r
-          .\r
-          .\r
-     }\r
-     \r
-There are two important points to note about interrupts;\r
-\r
-First, if the interrupt is called by external events then before changing\r
-the vector you MUST disable the interrupt callers using disable() and\r
-then re-enable the interrupts after the vector has been changed using\r
-enable().  If a call is made to the interrupt while the vector is being\r
-changed ANYTHING could happen!\r
-\r
-Secondly, before your program terminates and returns to DOS you must\r
-reset any changed interrupt vectors! The exception to this is the\r
-critical error handler interrupt vector that is restored automatically by\r
-DOS, so your program needn't bother restoring it.\r
-\r
-This example program hooks the PC clock timer interrupt to provide a\r
-background clock process while the rest of the program continues to run.\r
-If included with your own program that requires a constantly displayed\r
-clock on screen, you need only amend the display coordinates in the call\r
-to puttext(). Sincle the closk display code is called by a hardware\r
-issued interrupt, your program can start the clock and forget it until it\r
-terminates.\r
-\r
-     \r
-     /* Compile in LARGE memory model */\r
-     \r
-     #include <stdio.h>\r
-     #include <dos.h>\r
-     #include <time.h>\r
-     #include <conio.h>\r
-     #include <stdlib.h>\r
-     \r
-     enum { FALSE, TRUE };\r
-     \r
-     #define COLOUR (BLUE << 4) | YELLOW\r
-     \r
-     #define BIOS_TIMER  0x1C\r
-     \r
-     static unsigned installed = FALSE;\r
-     static void interrupt (*old_tick) (void);\r
-     \r
-     static void interrupt tick (void)\r
-     {\r
-          int i;\r
-          struct tm *now;\r
-          time_t this_time;\r
-          char time_buf[9];\r
-          static time_t last_time = 0L;\r
-          static char video_buf[20] =\r
-          {\r
-               ' ', COLOUR, '0', COLOUR, '0', COLOUR, ':', COLOUR, '0',\r
-     COLOUR,\r
-               '0', COLOUR, ':', COLOUR, '0', COLOUR, '0', COLOUR, ' ',\r
-     COLOUR\r
-          };\r
-     \r
-          enable ();\r
-     \r
-          if (time (&this_time) != last_time)\r
-          {\r
-               last_time = this_time;\r
-     \r
-               now = localtime(&this_time);\r
-     \r
-               sprintf(time_buf, "%02d:%02d.%02d",now->tm_hour,now-\r
-     >tm_min,now->tm_sec);\r
-     \r
-               for (i = 0; i < 8; i++)\r
-               {\r
-                    video_buf[(i + 1) << 1] = time_buf[i];\r
-               }\r
-     \r
-               puttext (71, 1, 80, 1, video_buf);\r
-          }\r
-     \r
-          old_tick ();\r
-     }\r
-     \r
-     void stop_clock (void)\r
-     {\r
-          if (installed)\r
-          {\r
-               setvect (BIOS_TIMER, old_tick);\r
-               installed = FALSE;\r
-          }\r
-     }\r
-     \r
-     void start_clock (void)\r
-     {\r
-          static unsigned first_time = TRUE;\r
-     \r
-          if (!installed)\r
-          {\r
-               if (first_time)\r
-               {\r
-                    atexit (stop_clock);\r
-                    first_time = FALSE;\r
-               }\r
-     \r
-               old_tick = getvect (BIOS_TIMER);\r
-               setvect (BIOS_TIMER, tick);\r
-               installed = TRUE;\r
-          }\r
-     }\r
-                                    \r
-                                 SIGNAL\r
-\r
-Interrupts raised by the host computer can be trapped and diverted in\r
-several ways. A simple method is to use signal().\r
-\r
-Signal() takes two parameters in the form;\r
-\r
-     void (*signal (int sig, void (*func) (int))) (int);\r
-\r
-The first parameter, `sig' is the signal to be caught. These are often\r
-predefined by the header file `signal.h'.\r
-\r
-The second parameter is a pointer to a function to be called when the\r
-signal is raised. This can either be a user function, or a macro defined\r
-in the header file `signal.h' to do some arbitrary task, such as ignore\r
-the signal for example.\r
-\r
-On a PC platform, it is often useful to disable the `ctrl-break' key\r
-combination that is used to terminate a running program by the user. The\r
-following PC signal() call replaces the predefined signal `SIGINT', which\r
-equates to the ctrl-break interrupt request, with the predefined macro\r
-`SIG-IGN', ignore the request;\r
-\r
-\r
-     signal(SIGINT,SIG_IGN);\r
-\r
-This example catches floating point errors on a PC, and zero divisions!\r
-\r
-     #include <stdio.h>\r
-     #include <signal.h>\r
-     \r
-     void (*old_sig)();\r
-     \r
-     void catch(int sig)\r
-     {\r
-          printf("Catch was called with: %d\n",sig);\r
-     }\r
-     \r
-     \r
-     void main()\r
-     {\r
-          int a;\r
-          int b;\r
-     \r
-          old_sig = signal(SIGFPE,catch);\r
-     \r
-          a = 0;\r
-          b = 10 / a;\r
-     \r
-          /* Restore original handler before exiting! */\r
-          signal(SIGFPE,old_sig);\r
-     }\r
-     \r
-                                    \r
-                          SORTING AND SEARCHING\r
-\r
-The ANSI C standard defines qsort(), a function for sorting a table of\r
-data. The function follows the format;\r
-\r
-     qsort(void *base,size_t elements,size_t width,int (*cmp)(void *,\r
-void *));\r
-\r
-The following short program illustrates the use of qsort() with a\r
-character array.\r
-\r
-     #include <string.h>\r
-     \r
-     main()\r
-     {\r
-          int n;\r
-          char data[10][20];\r
-     \r
-          /* Initialise some arbirary data */\r
-     \r
-          strcpy(data[0],"RED");\r
-          strcpy(data[1],"BLUE");\r
-          strcpy(data[2],"GREEN");\r
-          strcpy(data[3],"YELLOW");\r
-          strcpy(data[4],"INDIGO");\r
-          strcpy(data[5],"BROWN");\r
-          strcpy(data[6],"BLACK");\r
-          strcpy(data[7],"ORANGE");\r
-          strcpy(data[8],"PINK");\r
-          strcpy(data[9],"CYAN");\r
-     \r
-          /* Sort the data table */\r
-          qsort(data[0],10,20,strcmp);\r
-     \r
-          /* Print the data table */\r
-          for(n = 0; n < 10; n++)\r
-               puts(data[n]);\r
-     }\r
-     \r
-\r
-Here is a program that implements the shell sort algorithm (this one is\r
-based on the routine in K & R), which sorts arrays of pointers based upon\r
-the data pointed to by the pointers;\r
-\r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     #include <string.h>\r
-     \r
-     #define LINELEN     80\r
-     #define MAXLINES    2000\r
-     \r
-     char *lines[MAXLINES];\r
-     int lastone;\r
-     \r
-     void SHELL(void);\r
-     \r
-     void SHELL()\r
-     {\r
-          /* SHELL Sort Courtesy of K & R */\r
-     \r
-          int gap;\r
-          int i;\r
-          int j;\r
-          char temp[LINELEN];\r
-     \r
-          for(gap = lastone / 2; gap > 0; gap /= 2)\r
-          for(i = gap; i < lastone; i++)\r
-               for(j = i - gap; j >= 0 && strcmp(lines[j] , lines[j +\r
-     gap]) >\r
-                  0; j -= gap)\r
-               {\r
-                    strcpy(temp,lines[j]);\r
-                    strcpy(lines[j] , lines[j + gap]);\r
-                    strcpy(lines[j + gap] , temp);\r
-     \r
-               }\r
-     }\r
-     \r
-     void main(int argc, char *argv[])\r
-     {\r
-          FILE *fp;\r
-          char buff[100];\r
-          int n;\r
-\r
-          /* Check command line parameter has been given */\r
-          if (argc != 2)\r
-          {\r
-               printf("\nError: Usage is SERVSORT file");\r
-               exit(0);\r
-          }\r
-     \r
-          /* Attempt to open file for updating */\r
-          fp = fopen(argv[1],"r+");\r
-          if (fp == NULL)\r
-          {\r
-               printf("\nError: Unable to open %s",argv[1]);\r
-               exit(0);\r
-          }\r
-     \r
-          /* Initialise element counter to zero */\r
-          lastone = 0;\r
-     \r
-          /* Read file to be sorted */\r
-          while((fgets(buff,100,fp)) != NULL)\r
-          {\r
-               /* Allocate memory block*/\r
-               lines[lastone] = malloc(LINELEN);\r
-               if (lines[lastone] == NULL)\r
-               {\r
-                    printf("\nError: Unable to allocate memory");\r
-                    fclose(fp);\r
-                    exit(0);\r
-               }\r
-               strcpy(lines[lastone],buff);\r
-               lastone++;\r
-     \r
-               if (lastone > MAXLINES)\r
-               {\r
-                    printf("\nError: Too many lines in source file");\r
-                    exit(0);\r
-               }\r
-          }\r
-          /* Call sort function */\r
-          SHELL();\r
-     \r
-          /* Close file */\r
-          fclose(fp);\r
-     \r
-          /* Reopen file in create mode */\r
-          fp = fopen(argv[1],"w+");\r
-     \r
-          /* Copy sorted data from memory to disk */\r
-          for(n = 0; n < lastone; n++)\r
-               fputs(lines[n],fp);\r
-     \r
-          /* Close file finally */\r
-          fclose(fp);\r
-     \r
-          /* Return to calling program */\r
-          return(1);\r
-     }\r
-     \r
-\r
-If we want to use qsort() with a table of pointers we have to be a bit\r
-more clever than usual.\r
-\r
-This example uses the colours again, but this time they are stored in\r
-main memory and indexed by a table of pointers. Because we have a table\r
-of pointers to sort there are two differences between this program's\r
-qsort() and the previous one;\r
-\r
-First we can't use strcmp() as the qsort() comparison function, secondly\r
-the width of the table being sorted is sizeof(char *), that is the size\r
-of a character pointer.\r
-\r
-Notice the comparison function cmp() that receives two parameters, both\r
-are pointers to a pointer. qsort() sends to this function the values held\r
-in data[], which are in turn pointers to the data. So we need to use this\r
-indirection to locate the data, otherwise we would be comparing the\r
-addresses at which the data is held rather than the data itself!\r
-\r
-     #include <alloc.h>\r
-     #include <string.h>\r
-     \r
-     /* Function prototype for comparison function */\r
-     int (cmp)(char **,char **);\r
-     \r
-     int cmp(char **s1, char **s2)\r
-     {\r
-          /* comparison function using pointers to pointers */\r
-          return(strcmp(*s1,*s2));\r
-     }\r
-     \r
-     main()\r
-     {\r
-          int n;\r
-          char *data[10];\r
-     \r
-          for(n = 0; n < 10; n++)\r
-               data[n] = malloc(20);\r
-     \r
-          strcpy(data[0],"RED");\r
-          strcpy(data[1],"BLUE");\r
-          strcpy(data[2],"GREEN");\r
-          strcpy(data[3],"YELLOW");\r
-          strcpy(data[4],"INDIGO");\r
-          strcpy(data[5],"BROWN");\r
-          strcpy(data[6],"BLACK");\r
-          strcpy(data[7],"ORANGE");\r
-          strcpy(data[8],"PINK");\r
-          strcpy(data[9],"CYAN");\r
-     \r
-          /* The data table is comprised of pointers */\r
-          /* so the call to qsort() must reflect this */\r
-          qsort(data,10,sizeof(char *),cmp);\r
-     \r
-          for(n = 0; n < 10; n++)\r
-               puts(data[n]);\r
-     }\r
-     \r
-The quick sort is a fast sorting algorithm that works by subdividing the\r
-data table into two sub-tables and then subdividing the sub-tables. As it\r
-subdivides the table, so it compares the elements in the table and swaps\r
-them as required.\r
-\r
-The following program implements the quick sort algorithm, which is\r
-usually already used by qsort();\r
-\r
-\r
-     #include <string.h>\r
-     \r
-     #define MAXELE  2000\r
-     \r
-     char data[10][20];\r
-     int lastone;\r
-     \r
-     void QKSORT()\r
-     {\r
-          /* Implementation of QUICKSORT algorithm */\r
-     \r
-          int i;\r
-          int j;\r
-          int l;\r
-          int p;\r
-          int r;\r
-          int s;\r
-          char temp[100];\r
-          static int sl[MAXELE][2];\r
-     \r
-          /* sl[] is an index to the sub-table */\r
-     \r
-          l = 0;\r
-          r = lastone;\r
-          p = 0;\r
-     \r
-          do\r
-          {\r
-               while(l < r)\r
-               {\r
-                    i = l;\r
-                    j = r;\r
-                    s = -1;\r
-     \r
-                    while(i < j)\r
-                    {\r
-                         if (strcmp(data[i],data[j]) > 0)\r
-                         {\r
-                              strcpy(temp,data[i]);\r
-                              strcpy(data[i],data[j]);\r
-                              strcpy(data[j],temp);\r
-                              s = 0 - s;\r
-                         }\r
-     \r
-                         if (s == 1)\r
-                              i++;\r
-                         else\r
-                              j--;\r
-                    }\r
-     \r
-                    if (i + 1 < r)\r
-                    {\r
-                         p++;\r
-                         sl[p][0] = i + 1;\r
-                         sl[p][1] = r;\r
-                    }\r
-                    r = i - 1;\r
-               }\r
-               if (p != 0)\r
-               {\r
-                    l = sl[p][0];\r
-                    r = sl[p][1];\r
-                    p--;\r
-               }\r
-          }\r
-          while(p > 0);\r
-     }\r
-     \r
-     main()\r
-     {\r
-          int n;\r
-     \r
-          /* Initialise arbitrary data */\r
-          strcpy(data[0],"RED");\r
-          strcpy(data[1],"BLUE");\r
-          strcpy(data[2],"GREEN");\r
-          strcpy(data[3],"YELLOW");\r
-          strcpy(data[4],"INDIGO");\r
-          strcpy(data[5],"BROWN");\r
-          strcpy(data[6],"BLACK");\r
-          strcpy(data[7],"ORANGE");\r
-          strcpy(data[8],"PINK");\r
-          strcpy(data[9],"CYAN");\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 9;\r
-     \r
-          /* Call quick sort function */\r
-          QKSORT();\r
-     \r
-          /* Display sorted list */\r
-          for(n = 0; n < 10; n++)\r
-               puts(data[n]);\r
-     \r
-     }\r
-     \r
-A table sorted into ascending order can be searched with bsearch(), this\r
-takes the format;\r
-\r
-     bsearch(key,base,num_elements,width,int (*cmp)(void *, void *));\r
-\r
-bsearch() returns a pointer to the first element in the table that\r
-matches the key, or zero if no match is found.\r
-\r
-Or you can write your own binary search function thus;\r
-\r
-     int BSRCH(char *key, void *data, int numele, int width)\r
-     {\r
-          /* A binary search function returning one if found */\r
-          /* Zero if not found */\r
-     \r
-          int bp;\r
-          int tp;\r
-          int mp;\r
-          int result;\r
-          char *p;\r
-     \r
-          bp = 0;\r
-          tp = numele;\r
-          mp = (tp + bp) / 2;\r
-     \r
-          /* Locate element mp in table by assigning pointer to start */\r
-          /* and incrementing it by width * mp */\r
-          p = data;\r
-          p += width * mp;\r
-     \r
-          while((result = strcmp(p,key)) != 0)\r
-          {\r
-               if (mp >= tp)\r
-                    /* Not found! */\r
-                    return(0);\r
-               if (result < 0)\r
-                    bp = mp + 1;\r
-               else\r
-                    tp = mp - 1;\r
-     \r
-               mp = (bp + tp) / 2;\r
-               p = data;\r
-               p += width * mp;\r
-          }\r
-          return(1);\r
-     }\r
-     \r
-     void main()\r
-     {\r
-          int result;\r
-          char data[10][20];\r
-     \r
-          /* Initialise some arbirary data */\r
-     \r
-          strcpy(data[0],"RED");\r
-          strcpy(data[1],"BLUE");\r
-          strcpy(data[2],"GREEN");\r
-          strcpy(data[3],"YELLOW");\r
-          strcpy(data[4],"INDIGO");\r
-          strcpy(data[5],"BROWN");\r
-          strcpy(data[6],"BLACK");\r
-          strcpy(data[7],"ORANGE");\r
-          strcpy(data[8],"PINK");\r
-          strcpy(data[9],"CYAN");\r
-     \r
-          /* Sort the data table */\r
-          qsort(data[0],10,20,strcmp);\r
-     \r
-          result = BSRCH("CYAN",data[0],10,20);\r
-     \r
-          printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");\r
-     }\r
-     \r
-There are other sorting algorithms as well. This program incorporates the\r
-QUICK SORT, BUBBLE SORT, FAST BUBBLE SORT, INSERTION SORT and SHELL SORT\r
-for comparing how each performs on a random 1000 item string list;\r
-\r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     #include <string.h>\r
-     \r
-     char data[1000][4];\r
-     char save[1000][4];\r
-     \r
-     int lastone;\r
-     \r
-     void INITDATA(void);\r
-     void QKSORT(void);\r
-     void SHELL(void);\r
-     void BUBBLE(void);\r
-     void FBUBBLE(void);\r
-     void INSERTION(void);\r
-     void MKDATA(void);\r
-     \r
-     void QKSORT()\r
-     {\r
-          /* Implementation of QUICKSORT algorithm */\r
-     \r
-          int i;\r
-          int j;\r
-          int l;\r
-          int p;\r
-          int r;\r
-          int s;\r
-          char temp[20];\r
-          static int sl[1000][2];\r
-     \r
-          l = 0;\r
-          r = lastone;\r
-          p = 0;\r
-     \r
-          do\r
-          {\r
-               while(l < r)\r
-               {\r
-                    i = l;\r
-                    j = r;\r
-                    s = -1;\r
-     \r
-                    while(i < j)\r
-                    {\r
-                         if (strcmp(data[i],data[j]) > 0)\r
-                         {\r
-                              strcpy(temp,data[i]);\r
-                              strcpy(data[i],data[j]);\r
-                              strcpy(data[j],temp);\r
-                              s = 0 - s;\r
-                         }\r
-     \r
-                         if (s == 1)\r
-                              i++;\r
-                         else\r
-                              j--;\r
-                    }\r
-     \r
-                    if (i + 1 < r)\r
-                    {\r
-                         p++;\r
-                         sl[p][0] = i + 1;\r
-                         sl[p][1] = r;\r
-                    }\r
-                    r = i - 1;\r
-               }\r
-               if (p != 0)\r
-               {\r
-                    l = sl[p][0];\r
-                    r = sl[p][1];\r
-                    p--;\r
-               }\r
-          }\r
-          while(p > 0);\r
-     }\r
-     \r
-     void SHELL()\r
-     {\r
-          /* SHELL Sort Courtesy of K & R */\r
-     \r
-          int gap;\r
-          int i;\r
-          int j;\r
-          char temp[20];\r
-     \r
-          for(gap = lastone / 2; gap > 0; gap /= 2)\r
-          for(i = gap; i < lastone; i++)\r
-               for(j = i - gap; j >= 0 && strcmp(data[j] , data[j + gap])\r
-     > 0;\r
-                      j -= gap)\r
-               {\r
-                    strcpy(temp,data[j]);\r
-                    strcpy(data[j] , data[j + gap]);\r
-                    strcpy(data[j + gap] , temp);\r
-               }\r
-     }\r
-     \r
-     void BUBBLE()\r
-     {\r
-          int a;\r
-          int b;\r
-          char temp[20];\r
-     \r
-          for(a = lastone; a >= 0; a--)\r
-          {\r
-               for(b = 0; b < a; b++)\r
-               {\r
-                    if(strcmp(data[b],data[b + 1]) > 0)\r
-                    {\r
-                         strcpy(temp,data[b]);\r
-                         strcpy(data[b] , data[b + 1]);\r
-                         strcpy(data[b + 1] , temp);\r
-                    }\r
-               }\r
-          }\r
-     }\r
-     \r
-     void FBUBBLE()\r
-     {\r
-          /* bubble sort with swap flag*/\r
-     \r
-          int a;\r
-          int b;\r
-          int s;\r
-          char temp[20];\r
-     \r
-          s = 1;\r
-     \r
-          for(a = lastone; a >= 0 && s == 1; a--)\r
-          {\r
-               s = 0;\r
-               for(b = 0; b < a; b++)\r
-               {\r
-                    if(strcmp(data[b],data[b + 1]) > 0)\r
-                    {\r
-                         strcpy(temp,data[b]);\r
-                         strcpy(data[b] , data[b + 1]);\r
-                         strcpy(data[b + 1] , temp);\r
-                         s = 1;\r
-                    }\r
-               }\r
-          }\r
-     }\r
-     \r
-     void INSERTION()\r
-     {\r
-          int a;\r
-          int b;\r
-          char temp[20];\r
-     \r
-          for(a = 0; a < lastone; a++)\r
-          {\r
-               b = a;\r
-               strcpy(temp,data[a + 1]);\r
-               while(b >= 0)\r
-               {\r
-                    if (strcmp(temp,data[b]) < 0)\r
-                    {\r
-                         strcpy(data[b+1],data[b]);\r
-                         b--;\r
-                    }\r
-                    else\r
-                         break;\r
-               }\r
-               strcpy(data[b+1],temp);\r
-          }\r
-     }\r
-     \r
-     void MKDATA()\r
-     {\r
-          /* Initialise arbitrary data */\r
-          /* Uses random(), which is not ANSI C */\r
-          /* Returns a random number between 0 and n - 1 */\r
-     \r
-          int n;\r
-          for(n = 0; n < 1000; n++)\r
-               sprintf(save[n],"%d",random(1000));\r
-     }\r
-     \r
-     void INITDATA()\r
-     {\r
-          int n;\r
-     \r
-          for(n = 0 ; n < 1000; n++)\r
-               strcpy(data[n],save[n]);\r
-     }\r
-     \r
-     void main()\r
-     {\r
-          MKDATA();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 999;\r
-     \r
-          /* Call quick sort function */\r
-          QKSORT();\r
-     \r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 1000;\r
-     \r
-          /* Call shell sort function */\r
-          SHELL();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 999;\r
-     \r
-          /* Call bubble sort function */\r
-          BUBBLE();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 999;\r
-     \r
-          /* Call bubble sort with swap flag function */\r
-          FBUBBLE();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 999;\r
-     \r
-          /* Call insertion sort function */\r
-          INSERTION();\r
-     }\r
-\r
-Here are the profiler results of the above test program run on 1000 and\r
-5000 random items;\r
-\r
-STRING SORT - 1000 RANDOM ITEMS\r
-\r
-FBUBBLE        26.436 sec  41%\r
-|********************************************\r
-BUBBLE         26.315 sec  41%\r
-|*******************************************\r
-INSERTION      10.210 sec  15% |***************\r
-SHELL               0.8050 sec   1%  |*\r
-QKSORT         0.3252 sec  <1% |\r
-\r
-STRING SORT - 5000 RANDOM ITEMS\r
-\r
-FBUBBLE        563.70 sec  41%\r
-|********************************************\r
-BUBBLE         558.01 sec  41%\r
-|********************************************\r
-INSERTION      220.61 sec  16% |***************\r
-SHELL               5.2531 sec  <1% |\r
-QKSORT         0.8379 sec  <1% |\r
-\r
-Here is the same test program amended for sorting tables of integers;\r
-\r
-     /* Integer sort test program */\r
-     \r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     \r
-     void INITDATA(void);\r
-     void QKSORT(void);\r
-     void SHELL(void);\r
-     void BUBBLE(void);\r
-     void FBUBBLE(void);\r
-     void INSERTION(void);\r
-     void MKDATA(void);\r
-     \r
-     int data[1000];\r
-     int save[1000];\r
-     \r
-     int lastone;\r
-     \r
-     void QKSORT()\r
-     {\r
-          /* Implementation of QUICKSORT algorithm */\r
-     \r
-          int i;\r
-          int j;\r
-          int l;\r
-          int p;\r
-          int r;\r
-          int s;\r
-          int temp;\r
-          static int sl[1000][2];\r
-     \r
-          l = 0;\r
-          r = lastone;\r
-          p = 0;\r
-     \r
-          do\r
-          {\r
-               while(l < r)\r
-               {\r
-                    i = l;\r
-                    j = r;\r
-                    s = -1;\r
-     \r
-                    while(i < j)\r
-                    {\r
-                         if (data[i] > data[j])\r
-                         {\r
-                              temp = data[i];\r
-                              data[i] = data[j];\r
-                              data[j] = temp;\r
-                              s = 0 - s;\r
-                         }\r
-     \r
-                         if (s == 1)\r
-                              i++;\r
-                         else\r
-                              j--;\r
-                    }\r
-     \r
-                    if (i + 1 < r)\r
-                    {\r
-                         p++;\r
-                         sl[p][0] = i + 1;\r
-                         sl[p][1] = r;\r
-                    }\r
-                    r = i - 1;\r
-               }\r
-               if (p != 0)\r
-               {\r
-                    l = sl[p][0];\r
-                    r = sl[p][1];\r
-                    p--;\r
-               }\r
-          }\r
-          while(p > 0);\r
-     }\r
-     \r
-     void SHELL()\r
-     {\r
-          /* SHELL Sort Courtesy of K & R */\r
-     \r
-          int gap;\r
-          int i;\r
-          int j;\r
-          int temp;\r
-     \r
-          for(gap = lastone / 2; gap > 0; gap /= 2)\r
-          for(i = gap; i < lastone; i++)\r
-               for(j = i - gap; j >= 0 && data[j] > data[j + gap];\r
-                      j -= gap)\r
-               {\r
-                    temp = data[j];\r
-                    data[j] = data[j + gap];\r
-                    data[j + gap] = temp;\r
-               }\r
-     }\r
-     \r
-     void BUBBLE()\r
-     {\r
-          int a;\r
-          int b;\r
-          int temp;\r
-     \r
-          for(a = lastone; a >= 0; a--)\r
-          {\r
-               for(b = 0; b < a; b++)\r
-               {\r
-                    if(data[b] > data[b + 1])\r
-                    {\r
-                         temp = data[b];\r
-                         data[b] = data[b + 1];\r
-                         data[b + 1] = temp;\r
-                    }\r
-               }\r
-          }\r
-     }\r
-     \r
-     void FBUBBLE()\r
-     {\r
-          /* bubble sort with swap flag */\r
-     \r
-          int a;\r
-          int b;\r
-          int s;\r
-          int temp;\r
-     \r
-          s = 1;\r
-     \r
-          for(a = lastone; a >= 0 && s == 1; a--)\r
-          {\r
-               s = 0;\r
-               for(b = 0; b < lastone - a; b++)\r
-               {\r
-                    if(data[b] > data[b + 1])\r
-                    {\r
-                         temp = data[b];\r
-                         data[b] = data[b + 1];\r
-                         data[b + 1] = temp;\r
-                         s = 1;\r
-                    }\r
-               }\r
-          }\r
-     }\r
-     \r
-     void INSERTION()\r
-     {\r
-          int a;\r
-          int b;\r
-          int temp;\r
-     \r
-          for(a = 0; a < lastone; a++)\r
-          {\r
-               b = a;\r
-               temp = data[a + 1];\r
-               while(b >= 0)\r
-               {\r
-                    if (temp < data[b])\r
-                    {\r
-                         data[b+1] = data[b];\r
-                         b--;\r
-                    }\r
-                    else\r
-                         break;\r
-               }\r
-               data[b+1] = temp;\r
-          }\r
-     }\r
-     \r
-     void MKDATA()\r
-     {\r
-          int n;\r
-     \r
-          for(n = 0; n < 1000; n++)\r
-               save[n] = random(1000);\r
-     }\r
-     \r
-     void INITDATA()\r
-     {\r
-          int n;\r
-     \r
-          for(n = 0; n < 1000; n++)\r
-          data[n] = save[n];\r
-     }\r
-     \r
-     void main()\r
-     {\r
-          int n;\r
-     \r
-          /* Create 1000 random elements */\r
-          MKDATA();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 999;\r
-     \r
-          /* Call quick sort function */\r
-          QKSORT();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 1000;\r
-     \r
-          /* Call shell sort function */\r
-          SHELL();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 999;\r
-     \r
-          /* Call bubble sort function */\r
-          BUBBLE();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 999;\r
-     \r
-          /* Call bubble sort with swap flag function */\r
-          FBUBBLE();\r
-     \r
-          /* Initialise arbitrary data */\r
-          INITDATA();\r
-     \r
-          /* Set last element indicator */\r
-          lastone = 999;\r
-     \r
-          /* Call insertion sort function */\r
-          INSERTION();\r
-     }\r
-\r
-And here are the profiler results for this program;\r
-\r
-INTEGER SORTS - 1000 RANDOM NUMBERS (0 - 999)\r
-\r
-FBUBBLE        3.7197 sec  41%\r
-|********************************************\r
-BUBBLE         3.5981 sec  39%\r
-|******************************************\r
-INSERTION      1.4258 sec  15% |***************\r
-SHELL               0.1207 sec   1%  |*\r
-QKSORT         0.0081 sec  <1% |\r
-\r
-INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 999)\r
-\r
-FBUBBLE        92.749 sec  42%\r
-|********************************************\r
-BUBBLE         89.731 sec  41%\r
-|********************************************\r
-INSERTION      35.201 sec  16% |***************\r
-SHELL               0.9838 sec  <1% |\r
-QKSORT         0.0420 sec  <1% |\r
-\r
-INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 99)\r
-\r
-FBUBBLE        92.594 sec  42% |*****************************************\r
-BUBBLE         89.595 sec  40% |****************************************\r
-INSERTION      35.026 sec  16% |***************\r
-SHELL               0.7563 sec  <1% |\r
-QKSORT         0.6018 sec  <1% |\r
-\r
-INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 9)\r
-\r
-FBUBBLE        89.003 sec  41%\r
-|*******************************************\r
-BUBBLE         86.921 sec  40%\r
-|*******************************************\r
-INSERTION      31.544 sec  14% |***************\r
-QKSORT         6.0358 sec   2%  |**\r
-SHELL               0.5424 sec  <1% |\r
-\r
-INTEGER SORTS - 5000 DESCENDING ORDERED NUMBERS\r
-\r
-FBUBBLE        122.99 sec  39%\r
-|******************************************\r
-BUBBLE         117.22 sec  37% |****************************************\r
-INSERTION      70.595 sec  22% |**********************\r
-SHELL               0.6438 sec  <1% |\r
-QKSORT         0.0741 sec  <1% |\r
-\r
-INTEGER SORTS - 5000 ORDERED NUMBERS\r
-\r
-BUBBLE         62.908 sec  99%\r
-|******************************************\r
-SHELL               0.3971 sec  <1% |\r
-INSERTION      0.0510 sec  <1% |\r
-QKSORT         0.0382 sec  <1% |\r
-FBUBBLE        0.0251 sec  <1% |\r
-\r
-INTEGER SORTS - 10000 RANDOM NUMBERS (0 - 999)\r
-\r
-FBUBBLE        371.18 sec  42% |****************************************\r
-BUBBLE         359.06 sec  41% |***************************************\r
-INSERTION      140.88 sec  16% |**************\r
-SHELL               2.0423 sec  <1% |\r
-QKSORT         0.6183 sec  <1% |\r
-\r
-Theory has it that the performance of a sorting algorithm is dependant\r
-upon;\r
-\r
-     a) the number of items to be sorted and\r
-     b) how unsorted the list is to start with.\r
-\r
-With this in mind it is worth testing the various sorting routines\r
-described here to determine which one will best suit your particular\r
-application. If you examine the above profiler results you will see that:\r
-\r
-     1) With an already sorted list FBUBBLE() executes fastest\r
-     \r
-     2) With a random list of small variations between the values SHELL()\r
-         executes fastest\r
-     \r
-     3) With a random list of large variations between the values\r
-     QKSORT()\r
-          executes the fastest\r
-\r
-What the profiler does not highlight is that when the comparison aspect\r
-of a sort function takes a disproportionately long time to execute in\r
-relation to the rest of the sort function, then the bubble sort with a\r
-swap flag will execute faster than the bubble sort with out a swap flag.\r
-\r
-When considering a sort routine take into consideration memory\r
-constraints and the type of data to be sorted as well as the relative\r
-performances of the sort functions. Generally, the faster a sort\r
-operates, the more memory it requires. Compare the simple bubble sort\r
-with the quick sort, and you will see that the quick sort requires far\r
-more memory than the bubble sort.\r
-\r
-                                    \r
-                        DYNAMIC MEMORY ALLOCATION\r
-\r
-\r
-If a program needs a table of data, but the size of the table is\r
-variable, perhaps for a list of all file names in the current directory,\r
-it is inefficient to waste memory by declaring a data table of the\r
-maximum possible size. Rather it is better to dynamically allocate the\r
-table as required.\r
-\r
-Turbo C allocates RAM as being available for dynamic allocation into an\r
-area called the "heap". The size of the heap varies with memory model.\r
-The tiny memory model defaults to occupy 64K of RAM. The small memory\r
-model allocates upto 64K for the program/code and heap with a far heap\r
-being available within the remainder of conventional memory. The other\r
-memory models make all conventional memory available to the heap. This is\r
-significant when programming in the tiny memory model when you want to\r
-reduce the memory overhead of your program to a minimum. The way to do\r
-this is to reduce the heap to a minimum size. The smallest is 1 byte.\r
-\r
-C provides a function malloc() which allocates a block of free memory of\r
-a specified size and returns a pointer to the start of the block; it also\r
-provides free() which deallocates a block of memory previously allocated\r
-by malloc(). Notice, however, that the IBM PC doesnot properly free\r
-blocks of memory, and contiuous use of malloc() and free() will\r
-fragmentise memory, eventually causing no memory to be available untill\r
-the program terminates.\r
-\r
-This program searches a specified file for a specified string (with case\r
-sensitivity). It uses malloc() to allocate just enough memory for the\r
-file to be read into memory.\r
-\r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     \r
-     char *buffer;\r
-     \r
-     void main(int argc, char *argv[])\r
-     {\r
-          FILE *fp;\r
-          long flen;\r
-     \r
-          /* Check number of parameters */\r
-          if (argc != 3)\r
-          {\r
-               fputs("Usage is sgrep <text> <file spec>",stderr);\r
-               exit(0);\r
-          }\r
-     \r
-          /* Open stream fp to file */\r
-          fp = fopen(argv[2],"r");\r
-          if (!fp)\r
-          {\r
-               perror("Unable to open source file");\r
-               exit(0);\r
-          }\r
-     \r
-          /* Locate file end */\r
-          if(fseek(fp,0L,SEEK_END))\r
-          {\r
-               fputs("Unable to determine file length",stderr);\r
-               fclose(fp);\r
-               exit(0);\r
-          }\r
-     \r
-          /* Determine file length */\r
-          flen = ftell(fp);\r
-     \r
-          /* Check for error */\r
-          if (flen == -1L)\r
-          {\r
-               fputs("Unable to determine file length",stderr);\r
-               fclose(fp);\r
-               exit(0);\r
-          }\r
-     \r
-          /* Set file pointer to start of file */\r
-          rewind(fp);\r
-     \r
-          /* Allocate memory buffer */\r
-          buffer = malloc(flen);\r
-     \r
-          if (!buffer)\r
-          {\r
-               fputs("Unable to allocate memory",stderr);\r
-               fclose(fp);\r
-               exit(0);\r
-          }\r
-     \r
-          /* Read file into buffer */\r
-          fread(buffer,flen,1,fp);\r
-     \r
-          /* Check for read error */\r
-          if(ferror(fp))\r
-          {\r
-               fputs("Unable to read file",stderr);\r
-     \r
-               /* Deallocate memory block */\r
-               free(buffer);\r
-     \r
-               fclose(fp);\r
-               exit(0);\r
-          }\r
-     \r
-          printf("%s %s in %s",argv[1],(strstr(buffer,argv[1])) ? "was\r
-     found" : "was not found",argv[2]);\r
-     \r
-          /* Deallocate memory block before exiting */\r
-          free(buffer);\r
-          fclose(fp);\r
-     }\r
-                                    \r
-                         VARIABLE ARGUMENT LISTS\r
-\r
-\r
-Some functions, such as printf(), accept a variable number and type of\r
-arguments. C provides a mechanism to write your own functions which can\r
-accept a variable argument list. This mechanism is the va_ family defined\r
-in the header file `stdarg.h'.\r
-\r
-There are three macros which allow implementation of variable argument\r
-lists; va_start(), va_arg() and va_end() and a variable type va_list\r
-which defines an array which holds the information required by the\r
-macros.\r
-\r
-va_start() takes two parameters, the first is the va_list variable and\r
-the second is the last fixed parameter sent to the function. va_start()\r
-must be called before attempting to access the variable argument list as\r
-it sets up pointers required by the other macros.\r
-\r
-va_arg() returns the next variable from the argument list. It is called\r
-with two parameters, the first is the va_list variable and the second is\r
-the type of the argument to be extracted. So, if the next variable in the\r
-argument list is an integer, it can be extracted with;\r
-\r
-\r
-     <int> = va_arg(<va_list>,int);\r
-\r
-va_end() is called after extracting all required variables from the\r
-argument list, and simply tidies up the internal stack if appropriate.\r
-va_end() accepts a single parameter, the va_list variable.\r
-\r
-The following simple example program illustrates the basis for a printf()\r
-type implementation where the types of the arguments is not known, but\r
-can be determined from the fixed parameter. This example only caters for\r
-integer, string and character types, but could easily by extended to\r
-cater for other variable types as well by following the method\r
-illustrated;\r
-\r
-     #include <stdarg.h>\r
-     \r
-     char *ITOS(long x, char *ptr)\r
-     {\r
-          /* Convert a signed decimal integer to a string */\r
-     \r
-          long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,\r
-     1000, 100, 10, 1 };\r
-          int n;\r
-     \r
-          /* Check sign */\r
-          if (x < 0)\r
-          {\r
-               *ptr++ = '-';\r
-               /* Convert x to absolute */\r
-               x = 0 - x;\r
-          }\r
-     \r
-          for(n = 0; n < 9; n++)\r
-          {\r
-               if (x > pt[n])\r
-               {\r
-                    *ptr++ = 48 + x / pt[n];\r
-                    x %= pt[n];\r
-               }\r
-          }\r
-          return(ptr);\r
-     }\r
-     \r
-     void varfunc(char *format, ...)\r
-     {\r
-          va_list arg_ptr;\r
-          char output[1000];\r
-          char *ptr;\r
-          int bytes;\r
-          int x;\r
-          char *y;\r
-          char z;\r
-     \r
-          /* Initialise pointer to argument list */\r
-          va_start(arg_ptr, format);\r
-     \r
-          /* loop format string */\r
-          ptr = output;\r
-          bytes = 0;\r
-          while(*format)\r
-          {\r
-               /* locate next argument */\r
-               while(*format != '%')\r
-               {\r
-                    *ptr++ = *format++;\r
-                    bytes++;\r
-               }\r
-               /* A % has been located */\r
-               format++;\r
-               switch(*format)\r
-               {\r
-                    case '%' :  *ptr++ = '%';\r
-                             break;\r
-     \r
-                    case 'd' : /* integer expression follows */\r
-                            x = va_arg(arg_ptr,int);\r
-                            ptr = ITOS(x,ptr);\r
-                            *ptr = 0;\r
-                            format++;\r
-                            bytes += strlen(output) - bytes;\r
-                            break;\r
-     \r
-                    case 's' : /* String expression follows */\r
-                            y = va_arg(arg_ptr,char *);\r
-                            strcat(output,y);\r
-                            x = strlen(y);\r
-                            format++;\r
-                            ptr += x;\r
-                            bytes += x;\r
-                            break;\r
-     \r
-                    case 'c' : /* Char expression follows */\r
-                            z = va_arg(arg_ptr,char);\r
-                            *ptr++ = z;\r
-                            format++;\r
-                            bytes++;\r
-                            break;\r
-               }\r
-     \r
-          }\r
-     \r
-          /* Clean stack just in case! */\r
-          va_end(arg_ptr);\r
-     \r
-          /* Null terminate output string */\r
-          *ptr = 0;\r
-     \r
-          /* Display what we got */\r
-          printf("\nOUTPUT==%s",output);\r
-     }\r
-     \r
-     void main()\r
-     {\r
-          varfunc("%d %s %c",5,"hello world",49);\r
-     }\r
-     \r
-\r
-A simpler variation is to use vsprintf() rather than implementing our own\r
-variable argument list access. However, it is beneficial to understand\r
-how variable argument lists behave. The following is a simplification of\r
-the same program, but leaving the dirty work to the compiler;\r
-\r
-     #include <stdio.h>\r
-     #include <stdarg.h>\r
-     \r
-     void varfunc(char *format, ...)\r
-     {\r
-          va_list arg_ptr;\r
-          char output[1000];\r
-     \r
-          va_start(arg_ptr, format);\r
-     \r
-          vsprintf(output,format,arg_ptr);\r
-     \r
-          va_end(arg_ptr);\r
-     \r
-          /* Display what we got */\r
-          printf("\nOUTPUT==%s",output);\r
-     }\r
-     \r
-     void main()\r
-     {\r
-          varfunc("%d %s %c",5,"hello world",49);\r
-     }\r
-                                    \r
-                         TRIGONOMETRY FUNCTIONS\r
-\r
-The ANSI standard on C defines a number of trigonometry functions, all of\r
-which accept an angle parameter in radians;\r
-\r
-\r
-FUNCTION  PROTOTYPE                DESCRIPTION\r
-acos      double acos(double x)              arc cosine of x\r
-asin      double asin(double x)              arc sine of x\r
-atan      double atan(double x)              arc tangent of x\r
-atan2          double atan2(double x, double y)        arc tangent of y/x\r
-cos       double cos(double x)               cosine of x\r
-cosh      double cosh(double x)              hyperbolic cosine of x\r
-sin       double sin(double x)               sine of x\r
-sinh      double sinh(double x)              hyperbolic sine of x\r
-tan       double tan(double x)               tangent of x\r
-tanh      double tanh(double x)              hyperbolic tangent of x\r
-\r
-There are 2PI radians in a circle, therefore 1 radian is equal to 360/2PI\r
-degrees or approximately 57 degrees in 1 radian. The calculation of any\r
-of the above functions requires large floating point numbers to be used\r
-which is a very slow process. If you are going to use calls to a trig'\r
-function, it is a good idea to use a lookup table of values rather than\r
-keep on calling the function. This approach is used in the discussion on\r
-circle drawing later in this book.\r
-\r
-                                    \r
-                                 ATEXIT\r
-\r
-\r
-When ever a program terminates, it should close any open files (this is\r
-done for you by the C compiler's startup/termination code which it\r
-surrounds your program with), and restore the host computer to some\r
-semblance of order.  Within a large program where exit may occur from a\r
-number of locations it is a pain to have to keep on writing calls to the\r
-cleanup routine. Fortunately we don't have to!\r
-\r
-The ANSI standard on C describes a function, atexit(), which registers\r
-the specified function, supplied as a parameter to atexit(), as a\r
-function which is called immediately before terminating the program. This\r
-function is called automatically, so the following program calls\r
-`leave()' whether an error occurs or not;\r
-\r
-     #include <stdio.h>\r
-     \r
-     void leave()\r
-     {\r
-          puts("\nBye Bye!");\r
-     }\r
-     \r
-     void main()\r
-     {\r
-          FILE *fp;\r
-          int a;\r
-          int b;\r
-          int c;\r
-          int d;\r
-          int e;\r
-          char text[100];\r
-     \r
-          atexit(leave);\r
-     \r
-          fp = fopen("data.txt","w");\r
-     \r
-          if(!fp)\r
-          {\r
-               perror("Unable to create file");\r
-               exit(0);\r
-          }\r
-     \r
-          fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");\r
-     \r
-          fflush(fp);\r
-     \r
-          if (ferror(fp))\r
-          {\r
-               fputs("Error flushing stream",stderr);\r
-               exit(1);\r
-          }\r
-     \r
-          rewind(fp);\r
-          if (ferror(fp))\r
-          {\r
-               fputs("Error rewind stream",stderr);\r
-               exit(1);\r
-          }\r
-     \r
-          fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);\r
-          if (ferror(fp))\r
-          {\r
-               /* Unless you noticed the deliberate bug earlier */\r
-               /* The program terminates here */\r
-               fputs("Error reading from stream",stderr);\r
-               exit(1);\r
-          }\r
-     \r
-          printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);\r
-     }\r
-                                    \r
-                            INCREASING SPEED\r
-\r
-\r
-In order to reduce the time your program spends executing it is essential\r
-to know your host computer. Most computers are very slow at displaying\r
-information on the screen. And the IBM PC is no exception to this. C\r
-offers various functions for displaying data, printf() being one of the\r
-most commonly used and also the slowest. Whenever possible try to use\r
-puts(varname) in place of printf("%s\n",varname). Remembering that puts()\r
-appends a newline to the string sent to the screen.\r
-\r
-When multiplying a variable by a constant which is a factor of 2 many C\r
-compilers will recognise that a left shift is all that is required in the\r
-assembler code to carry out the multiplication rapidly. When multiplying\r
-by other values it is often faster to do a multiple addition instead, so;\r
-\r
-\r
-     'x * 3' becomes 'x + x + x'\r
-\r
-Don't try this with variable multipliers in a loop because it becomes\r
-very slow! But, where the multiplier is a constant it can be faster.\r
-(Sometimes!) Another way to speed up multiplication and division is with\r
-the shift commands, << and >>.\r
-\r
-The instruction x /= 2 can equally well be written x >>= 1, shift the\r
-bits of x right one place. Many compilers actually convert integer\r
-divisions by 2 into a shift right instruction. You can use the shifts for\r
-multiplying and dividing by 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 &c.\r
-If you have difficulty understanding the shift commands consider the\r
-binary form of a number;\r
-\r
-\r
-     01001101  equal to   77\r
-\r
-shifted right one place it becomes;\r
-\r
-     00100110  equal to   38\r
-\r
-Try to use integers rather than floating point numbers where ever\r
-possible.  Sometimes you can use integers where you didn't think you\r
-could! For example, to convert a fraction to a decimal one would normally\r
-say;\r
-\r
-     percentage = x / y * 100\r
-\r
-This requires floating point variables. However, it can also be written\r
-as;\r
-\r
-     z = x * 100;\r
-     percentage = z / y\r
-\r
-Which works fine with integers, so long as you don't mind the percentage\r
-being truncated. eg;\r
-\r
-     5 / 7 * 100 is equal to 71.43 with floating point\r
-\r
-but with integers;\r
-\r
-     5 * 100 / 7 is equal to 71\r
-\r
-(Assuming left to right expression evaluation. You may need to force the\r
-multiplication to be done first as with `z = x * 100').\r
-\r
-Here is a test program using this idea;\r
-\r
-     float funca(double x, double y)\r
-     {\r
-          return(x / y * 100);\r
-     }\r
-     \r
-     int funcb(int x,int y)\r
-     {\r
-          return(x * 100 / y);\r
-     }\r
-     \r
-     void main()\r
-     {\r
-          int n;\r
-          double x;\r
-          int y;\r
-     \r
-          for(n = 0; n < 5000; n++)\r
-          {\r
-               x = funca(5,7);\r
-               y = funcb(5,7);\r
-          }\r
-     }\r
-     \r
-And here is the results of the test program fed through a profiler;\r
-\r
-funca            1.9169 sec  96%\r
-|**********************************************\r
-funcb            0.0753 sec   3%  |*\r
-\r
-You can clearly see that the floating point function is 25 times slower\r
-than the integer equivalent!\r
-\r
-NB: Although it is normal practice for expressions to be evaluated left\r
-to right, the ANSI standard on C does not specify an order of preference\r
-for expression evaluation, and as such you should check your compiler\r
-manual.\r
-\r
-Another way of increasing speed is to use pointers rather than array\r
-indexing. When you access an array through an index, for example with;\r
-\r
-     x = data[i];\r
-\r
-the compiler has to calculate the offset of data[i] from the beginning of\r
-the array. A slow process. Using pointers can often improve things as the\r
-following two bubble sorts, one with array indexing and one with pointers\r
-illustrates;\r
-\r
-     void BUBBLE()\r
-     {\r
-          /* Bubble sort using array indexing */\r
-     \r
-          int a;\r
-          int b;\r
-          int temp;\r
-     \r
-          for(a = lastone; a >= 0; a--)\r
-          {\r
-               for(b = 0; b < a; b++)\r
-               {\r
-                    if(data[b] > data[b + 1])\r
-                    {\r
-                         temp = data[b];\r
-                         data[b] = data[b + 1];\r
-                         data[b + 1] = temp;\r
-                    }\r
-               }\r
-          }\r
-     }\r
-     \r
-     void PTRBUBBLE()\r
-     {\r
-          /* Bubble sort using pointers */\r
-     \r
-          int temp;\r
-          int *ptr;\r
-          int *ptr2;\r
-     \r
-          for(ptr = &data[lastone]; ptr >= data; ptr--)\r
-          {\r
-               for(ptr2 = data; ptr2 < ptr; ptr2++)\r
-               {\r
-                    if(*ptr2 > *(ptr2 + 1))\r
-                    {\r
-                         temp = *ptr2;\r
-                         *ptr2 = *(ptr2 + 1);\r
-                         *(ptr2 + 1) = temp;\r
-                    }\r
-               }\r
-          }\r
-     }\r
-     \r
-Here are the profiler results for the two versions of the same bubble\r
-sort operating on the same 1000 item, randomly sorted list;\r
-\r
-BUBBLE       3.1307 sec  59% |******************************************\r
-PTRBUBBLE    2.1686 sec  40% |***************************\r
-\r
-\r
-Here is another example of how to initialise an array using first the\r
-common indexing approach, and secondly the pointer approach;\r
-\r
-     /* Index array initialisation */\r
-     int n;\r
-     \r
-     for(n = 0; n < 1000; n++)\r
-          data[n] = random(1000);\r
-     \r
-     \r
-     /* Pointer array initialisation */\r
-     int *n;\r
-     \r
-     for(n = data; n < &data[1000]; n++)\r
-          *n = random(1000);\r
-     \r
-\r
-Needless to say, the pointer approach is faster than the index. The\r
-pointer approach is only really of benefit when an array is going to be\r
-traversed, as in the above examples. In the case of say a binary search\r
-where a different and non-adjacent element is going to be tested each\r
-pass then the pointer approach is no better than using array indexing.\r
-\r
-The exception to this rule of using pointers rather than indexed access,\r
-comes with pointer to pointers. Say your program has declared a table of\r
-static data, such as:\r
-\r
-static char *colours[] = { "Black", "Blue", "Green", "Yellow", "Red",\r
-"White" };\r
-\r
-It is faster to access the table with colours[n] than it is with a\r
-pointer, since each element in the table colours[], is a pointer. If you\r
-need to scan a string table for a value you can use this very fast\r
-approach instead;\r
-\r
-First the table is changed into a single string, with some delimiter\r
-between the elements.\r
-\r
-     static char *colours = "Black/Blue/Green/Yellow/Red/White";\r
-\r
-Then to confirm that a value is held in the table you can use strstr();\r
-\r
-     result = strstr(colours,"Cyan");\r
-\r
-Using in-line assembler code can provide the greatest speed increase.\r
-Care must be taken however not to interfere with the compiled C code. It\r
-is usually safe to write a complete function with in-line assembler code,\r
-but mixing in-line assembler with C code can be hazardous. As a rule of\r
-thumb, get your program working without assembler code, and then if you\r
-want to use in-line assembler, convert small portions of the code at a\r
-time, testing the program at each stage. Video I/O is a very slow process\r
-with C, and usually benefits from in-line assembler, and we have used\r
-this principle quite widely in the example programs which follow later.\r
-\r
-                                    \r
-                               PC GRAPHICS\r
-\r
-When programming graphics you should bear in mind that they are a machine\r
-dependant subject. Code to produce graphics on an IBM PC will not port to\r
-an Amiga, or VAX or any other type of computer.\r
-\r
-\r
-\r
-Introduction To PC Graphics\r
-\r
-The IBM PC and compatible range of computers display information on a\r
-visual display unit (VDU). To enable the computer to send information to\r
-the VDU a component is included within the computer called a "display\r
-card". There are various display cards and VDUs which have been produced\r
-for the IBM PC range of computers; monochrome display adapter (MDA),\r
-colour graphics adapter (CGA), Hercules graphics card (HGC), Enhanced\r
-graphics adapter (EGA), video graphics array (VGA), super video graphics\r
-array (SVGA), memory controller gate array (MCGA), 8514/A and the Txas\r
-Instruments Graphics Architecture (TIGA). For simplicity, this section\r
-will concern itself only with the three more common types of display;\r
-\r
-     CGA, EGA and VGA\r
-\r
-Information about the VGA display is also relevant to the SVGA display\r
-which in simple terms can do the same and more. This section will not\r
-concern itself with monochrome displays since they are of limited use in\r
-graphics work.\r
-\r
-\r
-Display Modes\r
-\r
-When an IBM PC computer is first switched on is set to display 80 columns\r
-by 25 rows of writing. This measurement, 80 x 25 is called the\r
-"resolution". A display mode which is intended for displaying writing is\r
-called a "text" mode.  Where as a display mode which is intended for\r
-displaying pictures is called a "graphics" mode.\r
-\r
-If you look closely at the display you will see that each displayed\r
-character is comprised of dots. In reality the entire display is\r
-comprised of dots, called "pixels", which may be set to different\r
-colours. In text display modes these pixels are not very relevant,\r
-however, in graphics display modes each pixel may be selected and set by\r
-a program. The size of the pixels varies with the display resolution. In\r
-80 x 25 text mode the pixels are half the width they are in 40 x 25 text\r
-display mode.\r
-\r
-Depending upon the display card installed in the computer, there are a\r
-number of display modes which may be used;\r
-\r
-\r
-MODE   TYPE               RESOLUTION         COLOURS\r
-                                             \r
- 0       Text             40 x 25            4 (CGA), 16 (EGA,\r
-                                             VGA) Shades of\r
-                                             grey\r
- 1       Text             40 x 25            4 (CGA), 16 (EGA,\r
-                                             VGA)\r
- 2       Text             80 x 25            4 (CGA), 16 (EGA,\r
-                                             VGA) Shades of\r
-                                             grey\r
- 3       Text             80 x 25            4 (CGA), 16 (EGA,\r
-                                             VGA)\r
- 4       Graphics         320 x 200          4\r
- 5       Graphics         320 x 200          4 (grey on CGA\r
-                                             and EGA)\r
- 6       Graphics         640 x 200          2\r
- 7       Text             80 x 25            Mono (EGA, VGA)\r
-13       Graphics         320 x 200          16 (EGA, VGA)\r
-14       Graphics         640 x 200          16 (EGA, VGA)\r
-15       Graphics         640 x 350          Mono (EGA, VGA)\r
-16       Graphics         640 x 350          16 (EGA, VGA)\r
-17       Graphics         640 x 480          2 (VGA)\r
-18       Graphics         640 x 480          16 (VGA)\r
-19       Graphics         320 x 200          256 (VGA)\r
-\r
-The term resolution in graphics modes refers to the number of pixels\r
-across and down the VDU. The larger the number of pixels, the smaller\r
-each is and the sharper any displayed image appears. As you can see from\r
-the table, the VGA display can produce a higher resolution than the other\r
-display cards, resulting in sharper images being produced.\r
-\r
-\r
-The CGA display card can produce a maximum resolution of 320 x 200\r
-pixels, where as the VGA display card can produce a resolution of 640 x\r
-480 pixels.  This is why writing on a VGA VDU looks so much sharper than\r
-the writing displayed on a CGA VDU.\r
-\r
-\r
-\r
-Accessing The Display\r
-\r
-Inside the IBM PC computer is a silicon chip called the BIOS ROM, this\r
-chip contains functions which may be called by an external computer\r
-program to access the display card, which in turn passes the information\r
-on to the VDU.  The BIOS display functions are all accessed by generating\r
-interrupt 10 calls, with the number of the appropriate function stored in\r
-the assembly language AH register.\r
-\r
-A programmer interested in creating graphics displays must first be able\r
-to switch the display card to an appropriate graphics mode. This is\r
-achieved by calling the BIOS display function 0, with th number of the\r
-desired display mode from the table stored in the assembly language AL\r
-register thus the following assembly language code segment will switch\r
-the display card to CGA graphics mode 4, assuming that is that the\r
-display card is capable of this mode;\r
-\r
-          mov   ah , 00\r
-          mov   al , 04\r
-          int   10h\r
-\r
-\r
-A C function for selecting video display modes can be written;\r
-\r
-     #include <dos.h>\r
-     \r
-     void setmode(unsigned char mode)\r
-     {\r
-          /* Sets the video display mode */\r
-     \r
-          union REGS inregs outreg;\r
-     \r
-          inreg.h.ah = 0;\r
-          inreg.h.al = mode;\r
-          int86(0x10,&inreg,&outregs);\r
-     }\r
-     \r
-Any graphics are created by setting different pixels to different\r
-colours, this is termed "plotting", and is achieved by calling BIOS\r
-display function 12 with the pixel's horizontal coordinate in the\r
-assembly language CX register and it's vertical coordinate in the\r
-assembly language DX register and the required colour in the assembly\r
-language AL register thus;\r
-\r
-\r
-          mov   ah, 12\r
-          mov   al, colour\r
-          mov   bh, 0\r
-          mov   cx, x_coord\r
-          mov   dx, y_coord\r
-          int   10h\r
-\r
-The corresponding C function is;\r
-\r
-     #include <dos.h>\r
-     \r
-     void plot(int x_coord, int y_coord, unsigned char colour)\r
-     {\r
-          /* Sets the colour of a pixel */\r
-     \r
-          union REGS regs;\r
-     \r
-          regs.h.ah = 12;\r
-          regs.h.al = colour;\r
-          regs.h.bh = 0;\r
-          regs.x.cx = x_coord;\r
-          regs.x.dx = y_coord;\r
-          int86(0x10,&regs,&regs);\r
-     }\r
-\r
-The inverse function of plot is to read the existing colour setting of a\r
-pixel. This is done by calling BIOS ROM display function 13, again with\r
-the pixel's horizontal coordinate in the assembly language CX register\r
-and it's vertical coordinate in the assembly language DX register. This\r
-function then returns the pixel's colour in the assembly language AL\r
-register;\r
-\r
-\r
-     #include <dos.h>\r
-     \r
-     unsigned char get_pixel(int x_coord, int y_coord)\r
-     {\r
-          /* Reads the colour of a pixel */\r
-     \r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.ah = 13;\r
-          inreg.h.bh = 0;\r
-          inreg.x.cx = x_coord;\r
-          inreg.x.dx = y_coord;\r
-          int86(0x10,&inreg,&outreg);\r
-          return(outreg.h.al);\r
-     }\r
-\r
-\r
-Colour And The CGA\r
-\r
-The CGA display card can display a maximum of 4 colours simultaneously at\r
-any time. However, the display card can generate a total of 8 colours.\r
-There are two sets of colours, called "palettes". The first palette\r
-contains the colours;\r
-\r
-     background, cyan, magenta and white.\r
-\r
-the second palette contains the colours;\r
-\r
-       background, green, red and yellow.\r
-\r
-Colour 0 is always the same as the background colour.\r
-\r
-The pixels displayed on the VDU take their colours from the currently\r
-active palette, and are continuously being refreshed. So, if the active\r
-palette changes, so to do the colours of the displayed pixels on the VDU.\r
-\r
-Selection of the active CGA palette is achieved by calling the BIOS\r
-display function 11 with the number of the desired palette (either 0 or\r
-1) in the assembly language BH register;\r
-\r
-\r
-          mov   ah, 11\r
-          mov   bh, palette\r
-          int   10h\r
-\r
-The C function for selecting the CGA palette is;\r
-\r
-     void palette(unsigned char palette)\r
-     {\r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.ah = 11;\r
-          inreg.h.bh = palette;\r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-     \r
-The background colour may be selected independantly from any of the eight\r
-available colours by calling the same BIOS display function with a value\r
-of 0 stored in the assembly language BH register and the desired\r
-background colour in the assembly language BL register;\r
-\r
-\r
-     mov   ah, 11\r
-     mov   bh, 0\r
-     mov   bl, colour\r
-     int   10h\r
-\r
-In C this function can be written;\r
-\r
-     void background(unsigned char colour)\r
-     {\r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.ah = 11;\r
-          inreg.h.bh = 0;\r
-          inreg.h.bl = colour;\r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-     \r
-The background colours available are;\r
-\r
-     0       Black\r
-     1       Blue\r
-     2       Green\r
-     3       Cyan\r
-     4       Red\r
-     5       Magenta\r
-     6       Yellow\r
-     7       White\r
-\r
-\r
-Colour And The EGA\r
-\r
-The EGA display card can display a maximum of 16 colours simultaneously\r
-at any time. The colour of all pixels is continuously refreshed by the\r
-display card by reading the colour from the EGA palette. Unlike the CGA\r
-display card, the EGA display card allows you to redefine any or all of\r
-the colours in the palette. Unfortunately only the first 8 colours may be\r
-loaded into other palette colours.\r
-\r
-The colours are;\r
-\r
-     0    Black\r
-     1    Blue\r
-     2    Green\r
-     3    Cyan\r
-     4    Red\r
-     5    Magenta\r
-     6    Brown\r
-     7    Light grey\r
-     8    Dark grey\r
-     9    Light blue\r
-     10        Light green\r
-     11   Light cyan\r
-     12        Light red\r
-     13        Light magenta\r
-     14        Yellow\r
-     15        White\r
-\r
-Changing a palette colour is achieved by calling the BIOS display\r
-function 16 with a value of 0 in the assembly language AL register, the\r
-colour value (0 to 7) in the assembly language BH register and the number\r
-of the palette colour (0 to 15) in the assembly language BL register\r
-thus;\r
-\r
-\r
-     mov   ah,16\r
-     mov   al,0\r
-     mov   bl,palette\r
-     mov   bh,colour\r
-     int   10h\r
-\r
-In C this function may be written;\r
-\r
-     void ega_palette(unsigned char colour, unsigned char palette)\r
-     {\r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 16;\r
-          inreg.h.al = 0;\r
-          inreg.h.bl = palette;\r
-          inreg.h.bh = colour;\r
-     \r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-\r
-\r
-\r
-Colour And The VGA\r
-\r
-The VGA display card can display a maximum of 256 colours on the VDU at\r
-any one time, these colours are defined by information held in 256\r
-special registers called "DAC" registers. As with the CGA and EGA\r
-displays, the colour of displayed pixels is continuously being refreshed,\r
-and as such any change to a DAC register is immediately visible. Each DAC\r
-register has three component data parts which record the amount of green,\r
-blue and red colours which make up the displayed colour. Each of these\r
-seperate data components can hold a value between 0 and 63 giving the VGA\r
-display card the ability to display 262,144 colours! Although only a\r
-small subset of them can be displayed at any one time.\r
-\r
-Setting the value of a DAC register is achieved by calling the BIOS\r
-display function 16 with a value of 16 stored in the assembly language AL\r
-register, the green value stored in the assembly language CH register,\r
-the blue value stored in the assembly language CL register and the red\r
-value stored in the assembly language DH register and the number of the\r
-DAC register to be set stored in the assembly language BX register;\r
-\r
-\r
-          mov   ah,16\r
-          mov   al,16\r
-          mov   ch,green\r
-          mov   cl,blue\r
-          mov   dh,red\r
-          mov   bx,dac\r
-          int   10h\r
-\r
-\r
-The C function to set a DAC register looks lik this;\r
-\r
-     void set_dac(int dac, unsigned char green, unsigned char blue,\r
-     unsigned char red)\r
-     {\r
-          union REGS regs;\r
-     \r
-          regs.h.ah = 16;\r
-          regs.h.al = 16;\r
-          regs.x.bx = dac;\r
-          regs.h.ch = green;\r
-          regs.h.cl = blue;\r
-          regs.h.dh = red;\r
-          int86(0x10,&regs,&regs);\r
-     }\r
-     \r
-\r
-\r
-Displaying Text\r
-\r
-The BIOS ROM provides three functions for displaying a single character.\r
-The first function to consider is the one used extensively by DOS for\r
-displaying messages, this is function 14 called "write text in teletype\r
-mode". This function interprets some control characters; bell (ascii 7),\r
-backspace (ascii 8), carriage return (ascii 10) and line feed (ascii 13)\r
-but all other ascii codes are displayed, and the current cursor position\r
-updated accordingly, moving down a row when a character is displayed in\r
-the far right column. To call this function the assembly language\r
-register AL holds the ascii code of the character to be displayed and\r
-assembly language register BL holds the foreground colour for the\r
-character to be displayed in if a graphics mode is active;\r
-\r
-\r
-          mov   ah,14\r
-          mov   al,character\r
-          mov   bh,0\r
-          mov   bl,foreground\r
-          int   10h\r
-\r
-\r
-A C function for accessing the write text in teletype mode may be written\r
-like this;\r
-\r
-     #include <dos.h>\r
-     \r
-     void teletype(unsigned char character, unsigned char foreground)\r
-     {\r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.ah = 14;\r
-          inreg.h.al = character;\r
-          inreg.h.bh = 0;\r
-          inreg.h.bl = foreground;\r
-          int86(0x10,&inrg,&outreg);\r
-     }\r
-     \r
-The second BIOS ROM display function for displaying a character allows\r
-the foreground and background colours of the displayed character to be\r
-defined. It also allows multiple copies of the character to be displayed\r
-one after another automatically displaying subsequent characters at the\r
-next display position, although the current cursor position is not\r
-changed by this function.\r
-\r
-This function is called "write character and attribute", and is BIOS ROM\r
-display function number 9. It is called with the ascii code of the\r
-character to be displayed in the assembly language AL register, the\r
-display page in assembly language register BH, the foreground colour in\r
-the first four bits of the assembly language register BL and the\r
-background colour in the last four bits of the assembly language register\r
-BL, the number of times the character is to be displayed is stored in the\r
-assembly language CX register thus;\r
-\r
-\r
-          mov   ah,9\r
-          mov   al,character\r
-          mov   bh,0\r
-          mov   bl,foreground + 16 * background\r
-          mov   cx,number\r
-          int   10h\r
-\r
-And in C;\r
-\r
-     #include <dos.h>\r
-     \r
-     void char_attrib(unsigned char character, unsigned char foreground,\r
-     unsigned char background, int number)\r
-     {\r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 9;\r
-          inreg.h.al = character;\r
-          inreg.h.bh = 0;\r
-          inreg.h.bl = (background << 4) + foreground;\r
-          inreg.x.cx = number;\r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-\r
-The last BIOS ROM display function for displaying a character retains the\r
-foreground and background colours of the display position.\r
-\r
-This function is called "write character", and is BIOS ROM display\r
-function number 10. It is identical to BIOS ROM display function 9 except\r
-that the colours of the displayed character are those which are prevalent\r
-at the display position, except in graphics modes when the foreground\r
-colour of the character is determined by the value in the assembly\r
-language BL register.  Its use is as follows;\r
-\r
-\r
-          mov   ah,10\r
-          mov   al,character\r
-          mov   bh,0\r
-          mov   bl,foreground   ; For graphics modes ONLY\r
-          mov   cx,number\r
-          int   10h\r
-\r
-And in C;\r
-\r
-     #include <dos.h>\r
-     \r
-     void char_attrib(unsigned char character, unsigned char foreground,\r
-     int number)\r
-     {\r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 10;\r
-          inreg.h.al = character;\r
-          inreg.h.bh = 0;\r
-          inreg.h.bl = foreground; /* For graphics modes ONLY */\r
-          inreg.x.cx = number;\r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-     \r
-\r
-Positioning of the text cursor is provided for by the ROM BIOS display\r
-function number 2. It is called with the row number in the assembly\r
-language register DH and the column number in the assembly language\r
-register DL;\r
-\r
-          mov   ah,2\r
-          mov   bh,0\r
-          mov   dh,row\r
-          mov   dl,column\r
-          int   10h\r
-\r
-The corresponding function in C looks like this;\r
-\r
-     #include <dos.h>\r
-     \r
-     void at(unsigned char row, unsigned char column)\r
-     {\r
-          union REGS regs;\r
-     \r
-          regs.h.ah = 2;\r
-          regs.h.bh = 0;\r
-          regs.h.dh = row;\r
-          regs.h.dl = column;\r
-          int86(0x10,&regs,&regs);\r
-     }\r
-\r
-From these basic functions a more useful replacement for the C language's\r
-"printf()" function can be written which allows data to be displayed at\r
-the current cursor position, previously set by a call to "at()", with\r
-prescribed attributes;\r
-\r
-\r
-     #include <dos.h>\r
-     #include <stdarg.h>\r
-     \r
-     void at(unsigned char row, unsigned char column)\r
-     {\r
-          union REGS regs;\r
-     \r
-          regs.h.ah = 2;\r
-          regs.h.bh = 0;\r
-          regs.h.dh = row;\r
-          regs.h.dl = column;\r
-          int86(0x10,&regs,&regs);\r
-     }\r
-     \r
-     void xprintf(unsigned char foreground, unsigned char background,\r
-     char *format,...)\r
-     {\r
-          union REGS inreg,outreg;\r
-     \r
-          va_list arg_ptr;\r
-          static char output[1000];\r
-          unsigned char col;\r
-          unsigned char row;\r
-          unsigned char n;\r
-          unsigned char p;\r
-          unsigned char text;\r
-          unsigned char attr;\r
-     \r
-          /* Convert foreground and background colours into a single\r
-     attribute */\r
-          attr = (background << 4) + foreground;\r
-     \r
-          /* Copy data into a single string */\r
-          va_start(arg_ptr, format);\r
-          vsprintf(output, format, arg_ptr);\r
-     \r
-          /* Determine number of display columns */\r
-          inreg.h.ah = 15;\r
-          int86(0x10,&inreg,&outreg);\r
-          n = outreg.h.ah;\r
-     \r
-          /* Determine current cursor position */\r
-          inreg.h.ah = 3;\r
-          inreg.h.bh = 0;\r
-          int86(0x10,&inreg,&outreg);\r
-          row = outreg.h.dh;\r
-          col = outreg.h.dl;\r
-     \r
-          /* Now display data */\r
-          p = 0;\r
-          while (output[p])\r
-          {\r
-               /* Display this character */\r
-               inreg.h.bh = 0;\r
-               inreg.h.bl = attr;\r
-               inreg.x.cx = 01;\r
-               inreg.h.ah = 9;\r
-               inreg.h.al = output[p++];\r
-               int86(0x10,&inreg,&outreg);\r
-     \r
-               /* Update cursor position */\r
-               /* moving down a row if required */\r
-               col++;\r
-               if (col < (n - 1))\r
-                    at(row, col);\r
-               else\r
-               {\r
-                    col = 0;\r
-                    at(++row, col);\r
-               }\r
-          }\r
-     }\r
-     \r
-This function, "xprintf()" illustrates two more functions of the BIOS\r
-ROM. The first is the call to function 15 which returns the number of\r
-text display columns for the currently active display mode.\r
-\r
-The other function illustrated, but not yet discussed, is BIOS ROM\r
-function 3 which returns information about the cursor. The cursor's row\r
-is returned in the assembly language register DH, and it's column in the\r
-assembly language register DL.\r
-\r
-\r
-                                    \r
-                     ADVANCED GRAPHICS ON THE IBM PC\r
-\r
-This section aims to reveal more about the graphics facilities offered by\r
-the IBM PC, in particular topics which are of a more complex nature than\r
-those addressed in the previous section.\r
-\r
-\r
-\r
-Display Pages\r
-\r
-The information for display by the display card is stored in an area of\r
-memory called the "video RAM". The size of the video RAM varies from one\r
-display card to another, and the amount of video RAM required for a\r
-display varies with the selected display mode. Display modes which do not\r
-require all of the video RAM use the remaining video RAM for additional\r
-display pages.\r
-\r
-\r
-MODE               PAGES\r
- 0                 8\r
- 1                 8\r
- 2                 4 (CGA) 8 (EGA, VGA)\r
- 3                 4 (CGA) 8 (EGA, VGA)\r
- 4                 1\r
- 5                 1\r
- 6                 1\r
- 7                 8 (EGA, VGA)\r
-13                 8 (EGA, VGA)\r
-14                 4 (EGA, VGA)\r
-15                 2 (EGA, VGA)\r
-16                 2 (EGA, VGA)\r
-17                 1 (VGA)\r
-18                 1 (VGA)\r
-19                 1 (VGA)\r
-\r
-Many of the BIOS ROM display functions allow selection of the display\r
-page to be written to, regardless of which page is currently being\r
-displayed.\r
-\r
-The display card continuously updates the VDU from the information in the\r
-active display page. Changing the active display page instantaneously\r
-changes the display.\r
-\r
-This provides the graphics programmer with the means to build a display\r
-on an undisplayed page, and to then change the active display page so\r
-that the viewer does not see the display being drawn.\r
-\r
-Selection of the active display page is achieved by calling BIOS ROM\r
-display function 5 with the number of the required display page stored in\r
-the assembly language register AL;\r
-\r
-          mov   ah,5\r
-          mov   al,page\r
-          int   10h\r
-\r
-Or, from C this function becomes;\r
-\r
-     #include <dos.h>\r
-     \r
-     void set_page(unsigned char page)\r
-     {\r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.ah = 5;\r
-          inreg.h.al = page;\r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-     \r
-The display page to which BIOS ROM display functions write is decided by\r
-the value stored in the assembly language BH register. The functions for\r
-setting a pixel's colour may be amended thus;\r
-\r
-          mov   ah, 12\r
-          mov   al, colour\r
-          mov   bh, page\r
-          mov   cx, x_coord\r
-          mov   dx, y_coord\r
-          int   10h\r
-\r
-And the corresponding C function becomes;\r
-\r
-     #include <dos.h>\r
-     \r
-     void plot(int x_coord, int y_coord, unsigned char colour, unsigned\r
-     char page)\r
-     {\r
-          /* Sets the colour of a pixel */\r
-     \r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.ah = 12;\r
-          inreg.h.al = colour;\r
-          inreg.h.bh = page;\r
-          inreg.x.cx = x_coord;\r
-          inreg.x.dx = y_coord;\r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-\r
-The currently active display page can be determined by calling BIOS ROM\r
-display function 15. This function returns the active display page in the\r
-assembly language register BH;\r
-\r
-          mov   ah,15\r
-          int   10h\r
-                              ; BH now holds active page number\r
-\r
-                                    \r
-                         Advanced Text Routines\r
-\r
-When the IBM PC display is in a text mode a blinking cursor is displayed\r
-at the current cursor position. This cursor is formed of a rectangle\r
-which is one complete character width, but it's top and bottom pixel\r
-lines are definable within the limits of the character height by calling\r
-BIOS ROM display function 1. A CGA display has a character height of 8\r
-pixel lines, an EGA display has a character height of 14 lines and a VGA\r
-display has a character height of 16 lines.\r
-\r
-BIOS ROM function 1 is called with the top pixel line number of the\r
-desired cursor shape in assembly language register CH and the bottom\r
-pixel line number in assembly language register CL;\r
-\r
-\r
-          mov   ah,1\r
-          mov   ch,top\r
-          mov   cl,bottom\r
-          int   10h\r
-\r
-A C function to set the cursor shape may be be written thus;\r
-\r
-     #include <dos.h>\r
-     \r
-     void setcursor(unsigned char top, unsigned char bottom)\r
-     {\r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.ch = start;\r
-          inreg.h.cl = end;\r
-          inreg.h.ah = 1;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-If the top pixel line is defined as larger than the bottom line, then the\r
-cursor will appear as a pair of parallel rectangles.\r
-\r
-The cursor may be removed from view by calling BIOS ROM display function\r
-1 with a value of 32 in the assembly language CH register.\r
-\r
-The current shape of the cursor can be determined by calling BIOS ROM\r
-function 3, which returns the top pixel line number of the cursor shape\r
-in the assembly language CH register, and the bottom line number in the\r
-assembly language register CL.\r
-\r
-Two functions are provided by the BIOS ROM for scrolling of the currently\r
-active display page. These are function 6, which scrolls the display up\r
-and function 7 which scrolls the display down.\r
-\r
-Both functions accept the same parameters, these being the number of\r
-lines to scroll in the assembly language register AL, the colour\r
-attribute for the resulting blank line in the assembly language BH\r
-register, the top row of the area to be scrolled in the assembly language\r
-CH register, the left column of the area to be scrolled in the assembly\r
-language CL register, the bottom row to be scrolled in the assembly\r
-language DH register and the right most column to be scrolled in the\r
-assembly language DL register.\r
-\r
-If the number of lines being scrolled is greater than the number of lines\r
-in the specified area, then the result is to clear the specified area,\r
-filling it with spaces in the attribute specified in the assembly\r
-language BH register.\r
-\r
-\r
-Scrolling\r
-\r
-A C function to scroll the entire screen down one line can be written\r
-thus;\r
-\r
-     #include <dos.h>\r
-     \r
-     void scroll_down(unsigned char attr)\r
-     {\r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.al = 1;\r
-          inreg.h.cl = 0;\r
-          inreg.h.ch = 0;\r
-          inreg.h.dl = 79;\r
-          inreg.h.dh = 24;    /* Assuming a 25 line display */\r
-          inreg.h.bh = attr;\r
-          inreg.h.ah = 7;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-\r
-\r
-Clear Screen\r
-A simple clear screen function can be written in C based upon the\r
-"scroll_down()" function simply by changing the value assigned to\r
-inreg.h.al to 0;\r
-\r
-     #include <dos.h>\r
-     \r
-     void cls(unsigned char attr)\r
-     {\r
-          union REGS inreg, outreg;\r
-     \r
-          inreg.h.al = 0;\r
-          inreg.h.cl = 0;\r
-          inreg.h.ch = 0;\r
-          inreg.h.dl = 79;\r
-          inreg.h.dh = 24;    /* Assuming a 25 line display */\r
-          inreg.h.bh = attr;\r
-          inreg.h.ah = 7;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-\r
-\r
-\r
-Windowing\r
-Windowing functions need to preserve the display they overwrite, and\r
-restore it when the window is removed from display. The BIOS ROM provides\r
-a display function which enables this to be done.\r
-\r
-Function 8 requires the appropriate display page number to be stored in\r
-assembly language register BH, and then when called it returns the ascii\r
-code of the character at the current cursor position of that display page\r
-in the assembly language AL register, and the display attribute of the\r
-character in the assembly language AH register.\r
-\r
-The following C functions allow an area of the display to be preserved,\r
-and later restored;\r
-\r
-     #include <dos.h>\r
-     \r
-     void at(unsigned char row, unsigned char column, unsigned char page)\r
-     {\r
-          /* Position the cursor */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 2;\r
-          inreg.h.bh = page;\r
-          inreg.h.dh = row;\r
-          inreg.h.dl = column;\r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-     \r
-     void get_win(unsigned char left, unsigned char top, unsigned char\r
-     right,unsigned char bottom, unsigned char page,        char *buffer)\r
-     {\r
-          /* Read a text window into a variable */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          unsigned char old_left;\r
-          unsigned char old_row;\r
-          unsigned char old_col;\r
-     \r
-          /* save current cursor position */\r
-          inreg.h.ah = 3;\r
-          inreg.h.bh = page;\r
-          int86(0x10,&inreg,&outreg);\r
-          old_row = outreg.h.dh;\r
-          old_col = outreg.h.dl;\r
-     \r
-          while(top <= bottom)\r
-          {\r
-               old_left = left;\r
-               while(left <= right)\r
-               {\r
-                    at(top,left,page);\r
-                    inreg.h.bh = page;\r
-                    inreg.h.ah = 8;\r
-                    int86(0x10,&inreg,&outreg);\r
-                    *buffer++ = outreg.h.al;\r
-                    *buffer++ = outreg.h.ah;\r
-                    left++;\r
-               }\r
-     \r
-               left = old_left;\r
-               top++;\r
-          }\r
-     \r
-          /* Restore cursor to original location */\r
-          at(old_row,old_col,page);\r
-     }\r
-     \r
-     void put_win(unsigned char left, unsigned char top, unsigned char\r
-     right,  unsigned char bottom, unsigned char            page, char\r
-     *buffer)\r
-     {\r
-          /* Display a text window from a variable */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          unsigned char old_left;\r
-          unsigned char chr;\r
-          unsigned char attr;\r
-          unsigned char old_row;\r
-          unsigned char old_col;\r
-     \r
-          /* save current cursor position */\r
-          inreg.h.ah = 3;\r
-          inreg.h.bh = page;\r
-          int86(0x10,&inreg,&outreg);\r
-          old_row = outreg.h.dh;\r
-          old_col = outreg.h.dl;\r
-     \r
-          while(top <= bottom)\r
-          {\r
-               old_left = left;\r
-               while(left <= right)\r
-               {\r
-                    at(top,left,page);\r
-                    chr = *buffer++;\r
-                    attr = *buffer++;\r
-                    inreg.h.bh = page;\r
-                    inreg.h.ah = 9;\r
-                    inreg.h.al = chr;\r
-                    inreg.h.bl = attr;\r
-                    inreg.x.cx = 1;\r
-                    int86(0x10,&inreg,&outreg);\r
-                    left++;\r
-               }\r
-               left = old_left;\r
-               top++;\r
-          }\r
-     \r
-          /* Restore cursor to original location */\r
-          at(old_row,old_col,page);\r
-     }\r
-                                    \r
-                   DIRECT VIDEO ACCESS WITH THE IBM PC\r
-\r
-Accessing video RAM directly is much faster than using the BIOS ROM\r
-display functions. There are problems however. Different video modes\r
-arrange their use of video RAM in different ways so a number of functions\r
-are required for plotting using direct video access, where as only one\r
-function is required if use is made of the BIOS ROM display function.\r
-\r
-The following C function will set a pixel in CGA display modes 4 and 5\r
-directly;\r
-\r
-\r
-     void dplot4(int y, int x, int colour)\r
-     {\r
-          /* Direct plotting in modes 4 & 5 ONLY! */\r
-     \r
-          union mask\r
-          {\r
-               char c[2];\r
-               int i;\r
-          }bit_mask;\r
-     \r
-          int index;\r
-          int bit_position;\r
-     \r
-          unsigned char t;\r
-          char xor;\r
-     \r
-          char far *ptr = (char far *) 0xB8000000;\r
-     \r
-          bit_mask.i = 0xFF3F;\r
-     \r
-          if ( y < 0 || y > 319 || x < 0 || x > 199)\r
-               return;\r
-     \r
-          xor = colour & 128;\r
-     \r
-          colour = colour & 127;\r
-     \r
-          bit_position = y % 4;\r
-     \r
-          colour <<= 2 * (3 - bit_position);\r
-     \r
-          bit_mask.i >>= 2 * bit_position;\r
-     \r
-          index = x * 40 + (y / 4);\r
-     \r
-          if (x % 2)\r
-               index += 8152;\r
-     \r
-     \r
-     \r
-     \r
-          if (!xor)\r
-          {\r
-               t = *(ptr + index) & bit_mask.c[0];\r
-               *(ptr + index) = t | colour;\r
-          }\r
-          else\r
-          {\r
-               t = *(ptr + index) | (char)0;\r
-               *(ptr + index) = t ^ colour;\r
-          }\r
-     }\r
-     \r
-\r
-Direct plotting in VGA mode 19 is very much simpler;\r
-\r
-     void dplot19(int x, int y, unsigned char colour)\r
-     {\r
-          /* Direct plot in mode 19 ONLY */\r
-          char far *video;\r
-     \r
-          video = MK_FP(0xA000,0);\r
-          video[x + y * 320] = colour;\r
-}\r
-                                    \r
-              ADVANCED GRAPHICS TECHNIQUES WITH THE IBM PC\r
-\r
-\r
-\r
-\r
-Increasing Colours\r
-\r
-The EGA display is limited displaying a maximum of 16 colours, however in\r
-high resolution graphics modes (such as mode 16) the small physical size\r
-of the pixels allows blending of adjacent colours to produce additional\r
-shades.\r
-\r
-If a line is drawn straight across a black background display in blue,\r
-and then a subsequent line is drawn beneath it also in blue but only\r
-plotting alternate pixels, the second line will appear in a darker shade\r
-of the same colour.\r
-\r
-The following C program illustrates this idea;\r
-\r
-\r
-     #include <dos.h>\r
-     \r
-     union REGS inreg, outreg;\r
-     \r
-     void setvideo(unsigned char mode)\r
-     {\r
-          /* Sets the video display mode */\r
-     \r
-          inreg.h.al = mode;\r
-          inreg.h.ah = 0;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     void plot(int x, int y, unsigned char colour)\r
-     {\r
-          /* Sets a pixel at the specified coordinates */\r
-     \r
-          inreg.h.al = colour;\r
-          inreg.h.bh = 0;\r
-          inreg.x.cx = x;\r
-          inreg.x.dx = y;\r
-          inreg.h.ah = 0x0C;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     void line(int a, int b, int c, int d, unsigned char colour)\r
-     {\r
-          /* Draws a straight line from (a,b) to (c,d) */\r
-     \r
-          int u;\r
-          int v;\r
-          int d1x;\r
-          int d1y;\r
-          int d2x;\r
-          int d2y;\r
-          int m;\r
-          int n;\r
-          int s;\r
-          int i;\r
-     \r
-          u = c - a;\r
-          v = d - b;\r
-          if (u == 0)\r
-          {\r
-               d1x = 0;\r
-               m = 0;\r
-          }\r
-          else\r
-          {\r
-               m = abs(u);\r
-               if (u < 0)\r
-                    d1x = -1;\r
-               else\r
-               if (u > 0)\r
-                    d1x = 1;\r
-          }\r
-          if ( v == 0)\r
-          {\r
-               d1y = 0;\r
-               n = 0;\r
-          }\r
-          else\r
-          {\r
-               n = abs(v);\r
-               if (v < 0)\r
-                    d1y = -1;\r
-               else\r
-               if (v > 0)\r
-                    d1y = 1;\r
-          }\r
-          if (m > n)\r
-          {\r
-               d2x = d1x;\r
-               d2y = 0;\r
-          }\r
-          else\r
-          {\r
-               d2x = 0;\r
-               d2y = d1y;\r
-               m = n;\r
-               n = abs(u);\r
-          }\r
-          s = m / 2;\r
-     \r
-          for (i = 0; i <= m; i++)\r
-          {\r
-               plot(a,b,colour);\r
-               s += n;\r
-               if (s >= m)\r
-               {\r
-                    s -= m;\r
-                    a += d1x;\r
-                    b += d1y;\r
-               }\r
-               else\r
-               {\r
-                    a += d2x;\r
-                    b += d2y;\r
-               }\r
-          }\r
-     }\r
-     \r
-     void dot_line(int a, int b, int c, int d, int colour)\r
-     {\r
-          /* Draws a dotted straight line from (a,b) to (c,d) */\r
-     \r
-          int u;\r
-          int v;\r
-          int d1x;\r
-          int d1y;\r
-          int d2x;\r
-          int d2y;\r
-          int m;\r
-          int n;\r
-          int s;\r
-          int i;\r
-     \r
-          u = c - a;\r
-          v = d - b;\r
-          if (u == 0)\r
-          {\r
-               d1x = 0;\r
-               m = 0;\r
-          }\r
-          else\r
-          {\r
-               m = abs(u);\r
-               if (u < 0)\r
-                    d1x = -2;\r
-               else\r
-               if (u > 0)\r
-                    d1x = 2;\r
-          }\r
-          if (v == 0)\r
-          {\r
-               d1y = 0;\r
-               n = 0;\r
-          }\r
-          else\r
-          {\r
-               n = abs(v);\r
-               if (v < 0)\r
-                    d1y = -2;\r
-               else\r
-               if (v > 0)\r
-                    d1y = 2;\r
-          }\r
-          if (m > n)\r
-          {\r
-               d2x = d1x;\r
-               d2y = 0;\r
-          }\r
-          else\r
-          {\r
-               d2x = 0;\r
-               d2y = d1y;\r
-               m = n;\r
-               n = abs(u);\r
-          }\r
-          s = m / 2;\r
-     \r
-          for (i = 0; i <= m; i+=2)\r
-          {\r
-               plot(a,b,colour);\r
-               s += n;\r
-               if (s >= m)\r
-               {\r
-                    s -= m;\r
-                    a += d1x;\r
-                    b += d1y;\r
-               }\r
-               else\r
-               {\r
-                    a += d2x;\r
-                    b += d2y;\r
-               }\r
-          }\r
-     }\r
-     \r
-     \r
-     void main(void)\r
-     {\r
-          int n;\r
-     \r
-          /* Display different colour bands */\r
-     \r
-          setvideo(16);\r
-     \r
-          for(n = 1; n < 16; n++)\r
-          {\r
-               line(0,n * 20,639,n * 20,n);\r
-               line(0,1 + n * 20,639,1 + n * 20,n);\r
-               line(0,2 + n * 20,639,2 + n * 20,n);\r
-     \r
-               dot_line(0,4 + n * 20,639,4 + n * 20,n);\r
-               dot_line(1,5 + n * 20,639,5 + n * 20,n);\r
-               dot_line(0,6 + n * 20,639,6 + n * 20,n);\r
-     \r
-               dot_line(0,8 + n * 20,639,8 + n * 20,n);\r
-               dot_line(1,9 + n * 20,639,9 + n * 20,n);\r
-               dot_line(0,10 + n * 20,639,10 + n * 20,n);\r
-     \r
-               dot_line(1,8 + n * 20,639,8 + n * 20,7);\r
-               dot_line(0,9 + n * 20,639,9 + n * 20,7);\r
-               dot_line(1,10 + n * 20,639,10 + n * 20,7);\r
-     \r
-               dot_line(1,12 + n * 20,639,12 + n * 20,n);\r
-               dot_line(0,13 + n * 20,639,13 + n * 20,n);\r
-               dot_line(1,14 + n * 20,639,14 + n * 20,n);\r
-     \r
-               dot_line(0,12 + n * 20,639,12 + n * 20,14);\r
-               dot_line(1,13 + n * 20,639,13 + n * 20,14);\r
-               dot_line(0,14 + n * 20,639,14 + n * 20,14);\r
-          }\r
-     }\r
-\r
-This technique can be put to good use for drawing three dimensional\r
-boxes;\r
-\r
-     void box3d(int xa,int ya, int xb, int yb, int col)\r
-     {\r
-          /* Draws a box for use in 3d histogram graphs etc */\r
-     \r
-          int xc;\r
-          int xd;\r
-     \r
-          int n;\r
-     \r
-          xd = (xb - xa) / 2;\r
-          xc = xa + xd;\r
-     \r
-          /* First draw the solid face */\r
-          for(n = xa; n < xb; n++)\r
-               line(n,ya,n,yb,col);\r
-     \r
-          /* Now "shaded" top and side */\r
-          for(n = 0; n < xd; n++)\r
-          {\r
-               dotline(xa + n,yb - n ,xc + n,yb - n,col);\r
-               dotline(xa + xd + n,yb - n ,xc + xd + n,yb - n,col);\r
-               dotline(xb +n ,ya - n ,xb + n,yb - n,col);\r
-          }\r
-     }\r
-     \r
-\r
-\r
-Displaying Text At Pixel Coordinates\r
-\r
-When using graphics display modes it is useful to be able to display text\r
-not at the fixed character boundaries, but at pixel coordinates. This can\r
-be achieved by implementing a print function which reads the character\r
-definition data from the BIOS ROM and uses it to plot pixels to create\r
-the shapes of the desired text. The following C function, "gr_print()"\r
-illustrates this idea using the ROM CGA (8x8) character set;\r
-\r
-     void gr_print(char *output, int x, int y, unsigned char colour)\r
-     {\r
-          unsigned char far *ptr;\r
-          unsigned char chr;\r
-          unsigned char bmask;\r
-          int i;\r
-          int k;\r
-          int oldy;\r
-          int p;\r
-          int height;\r
-     \r
-          /* The height of the characters in the font being accessed */\r
-          height = 8;\r
-     \r
-          /* Set pointer to start of font definition in the ROM */\r
-          ptr = getfontptr(3);\r
-     \r
-          oldy = y;\r
-          p = 0;\r
-     \r
-          /* Loop output string */\r
-          while(output[p])\r
-          {\r
-               /* Get first character to be displayed */\r
-               chr = output[p];\r
-     \r
-               /* Loop pixel lines in character definition */\r
-               for(i = 0; i < height; i++)\r
-               {\r
-                    /* Get pixel line definition from the ROM */\r
-                    bmask = *(ptr + (chr * height) + i);\r
-     \r
-                    /* Loop pixel columns */\r
-                    for (k = 0; k < 8; ++k, bmask <<= 1)\r
-                    {\r
-                         /* Test for a set bit */\r
-                         if(bmask & 128)\r
-                              /* Plot a pixel if appropriate */\r
-                              plot(x,y,colour);\r
-                         else\r
-                              plot(x,y,0);\r
-                         x++;\r
-                    }\r
-                    /* Down to next row and left to start of character */\r
-                    y++;\r
-                    x -= 8;\r
-               }\r
-               /* Next character to be displayed */\r
-               p++;\r
-     \r
-               /* Back to top row of the display position */\r
-               y = oldy;\r
-     \r
-               /* Right to next character display position */\r
-               x += 8;\r
-          }\r
-     }\r
-\r
-The following assembly language support function is required to retrieve\r
-the address of the ROM font by calling the BIOS ROM display function\r
-which will return the address;\r
-\r
-\r
-     ; GET FONT POINTER\r
-     ; Small memory model\r
-     ; compile with tasm /mx\r
-     \r
-     _TEXT     segment   byte public 'CODE'\r
-                     assume   cs:_TEXT,ds:NOTHING\r
-     \r
-     _getfontptr    proc near\r
-               push bp\r
-               mov   bp,sp\r
-               mov   ax,1130h\r
-               mov   bh, [bp+4]         ; Number for font to be retrieved\r
-               int   10h\r
-               mov   dx,es\r
-               mov   ax,bp\r
-               pop   bp\r
-               ret\r
-     _getfontptr    endp\r
-     \r
-     _TEXT     ends\r
-     \r
-          public    _getfontptr\r
-          end\r
-     \r
-The font number supplied to "getfontptr()" can be one of;\r
-\r
-     2    ROM EGA 8 x 14 font\r
-     3    ROM CGA 8 x 8 font\r
-     6    ROM VGA 8 x 16 font\r
-\r
-A Graphics Function Library For Turbo C\r
-\r
-     /* Graphics library for 'Turbo C'  (V2.01) */\r
-     \r
-     /* (C)1992 Copyright Servile Software */\r
-     \r
-     #include <stdio.h>\r
-     #include <dos.h>\r
-     #include <stdlib.h>\r
-     #include <stdarg.h>\r
-     #include <string.h>\r
-     #include <math.h>\r
-     \r
-     /* Global variables */\r
-     static unsigned char attribute;\r
-     int _dmode;\r
-     int _lastx;\r
-     int _lasty;\r
-     \r
-     /* Maximum coordinates for graphics screen */\r
-     static int maxx;\r
-     static int maxy;\r
-     \r
-     /* Sprite structure */\r
-     struct SP\r
-     {\r
-          int x;\r
-          int y;\r
-          char data[256];\r
-          char save[256];\r
-     };\r
-     typedef struct SP SPRITE;\r
-     \r
-     \r
-     /* Sine and cosine tables for trig' operations */\r
-     \r
-     static double sintable[360] =\r
-     {0.000000000000000001,0.017452406437283512,\r
-                    0.034899496702500969,0.052335956242943835,\r
-                    0.069756473744125302,0.087155742747658166,\r
-                    0.104528463267653457,0.121869343405147462,\r
-                    0.139173100960065438,0.156434465040230869,\r
-                    0.173648177666930331,0.190808995376544804,\r
-                    0.207911690817759315,0.224951054343864976,\r
-                    0.241921895599667702,0.258819045102520739,\r
-                    0.275637355816999163,0.292371704722736769,\r
-                    0.309016994374947451,0.325568154457156755,\r
-                    0.342020143325668824,0.358367949545300379,\r
-                    0.374606593415912181,0.390731128489273882,\r
-                    0.406736643075800375,0.422618261740699608,\r
-                    0.438371146789077626,0.453990499739547027,\r
-                    0.469471562785891028,0.484809620246337225,\r
-                    0.500000000000000222,0.515038074910054489,\r
-                    0.529919264233205234,0.544639035015027417,\r
-                    0.559192903470747127,0.573576436351046381,\r
-                    0.587785252292473470,0.601815023152048600,\r
-                    0.615661475325658625,0.629320391049837835,\r
-                    0.642787609686539696,0.656059028990507720,\r
-                    0.669130606358858682,0.681998360062498921,\r
-                    0.694658370458997698,0.707106781186548017,\r
-                    0.719339800338651636,0.731353701619170904,\r
-                    0.743144825477394688,0.754709580222772458,\r
-                    0.766044443118978569,0.777145961456971346,\r
-                    0.788010753606722458,0.798635510047293384,\r
-                    0.809016994374947895,0.819152044288992243,\r
-                    0.829037572555042179,0.838670567945424494,\r
-                    0.848048096156426512,0.857167300702112778,\r
-                    0.866025403784439152,0.874619707139396296,\r
-                    0.882947592858927432,0.891006524188368343,\r
-                    0.898794046299167482,0.906307787036650381,\r
-                    0.913545457642601311,0.920504853452440819,\r
-                    0.927183854566787868,0.933580426497202187,\r
-                    0.939692620785908761,0.945518575599317179,\r
-                    0.951056516295153975,0.956304755963035880,\r
-                    0.961261695938319227,0.965925826289068645,\r
-                    0.970295726275996806,0.974370064785235579,\r
-                    0.978147600733805911,0.981627183447664198,\r
-                    0.984807753012208353,0.987688340595137992,\r
-                    0.990268068741570473,0.992546151641322205,\r
-                    0.994521895368273512,0.996194698091745656,\r
-                    0.997564050259824309,0.998629534754573944,\r
-                    0.999390827019095762,0.999847695156391270,\r
-                    1.000000000000000000,0.999847695156391159,\r
-                    0.999390827019095651,0.998629534754573833,\r
-                    0.997564050259824087,0.996194698091745323,\r
-                    0.994521895368273179,0.992546151641321761,\r
-                    0.990268068741570029,0.987688340595137437,\r
-                    0.984807753012207687,0.981627183447663532,\r
-                    0.978147600733805245,0.974370064785234802,\r
-                    0.970295726275996029,0.965925826289067757,\r
-                    0.961261695938318228,0.956304755963034880,\r
-                    0.951056516295152865,0.945518575599316069,\r
-                    0.939692620785907651,0.933580426497200966,\r
-                    0.927183854566786536,0.920504853452439486,\r
-                    0.913545457642599978,0.906307787036649048,\r
-                    0.898794046299166149,0.891006524188367122,\r
-                    0.882947592858926211,0.874619707139395186,\r
-                    0.866025403784438042,0.857167300702111778,\r
-                    0.848048096156425624,0.838670567945423717,\r
-                    0.829037572555041513,0.819152044288991688,\r
-                    0.809016994374947451,0.798635510047293051,\r
-                    0.788010753606722236,0.777145961456971346,\r
-                    0.766044443118978569,0.754709580222772680,\r
-                    0.743144825477395132,0.731353701619171459,\r
-                    0.719339800338652302,0.707106781186548794,\r
-                    0.694658370458998808,0.681998360062500142,\r
-                    0.669130606358860014,0.656059028990509274,\r
-                    0.642787609686541472,0.629320391049839833,\r
-                    0.615661475325660845,0.601815023152051043,\r
-                    0.587785252292476135,0.573576436351049268,\r
-                    0.559192903470750236,0.544639035015030637,\r
-                    0.529919264233208676,0.515038074910058152,\r
-                    0.500000000000004219,0.484809620246341444,\r
-                    0.469471562785895413,0.453990499739551634,\r
-                    0.438371146789082455,0.422618261740704715,\r
-                    0.406736643075805704,0.390731128489279489,\r
-                    0.374606593415918010,0.358367949545306430,\r
-                    0.342020143325675152,0.325568154457163306,\r
-                    0.309016994374954279,0.292371704722743819,\r
-                    0.275637355817006491,0.258819045102528289,\r
-                    0.241921895599675502,0.224951054343872997,\r
-                    0.207911690817767558,0.190808995376553270,\r
-                    0.173648177666939019,0.156434465040239751,\r
-                    0.139173100960074542,0.121869343405156802,\r
-                    0.104528463267663005,0.087155742747667922,\r
-                    0.069756473744135267,0.052335956242954007,\r
-                    0.034899496702511350,0.017452406437294093,\r
-                    0.000000000000010781,-0.017452406437272534,\r
-                    -0.034899496702489805,-0.052335956242932476,\r
-                    -0.069756473744113756,-0.087155742747646453,\r
-                    -0.104528463267641564,-0.121869343405135402,\r
-                    -0.139173100960053198,-0.156434465040218462,\r
-                    -0.173648177666917786,-0.190808995376532092,\r
-                    -0.207911690817746464,-0.224951054343851986,\r
-                    -0.241921895599654574,-0.258819045102507472,\r
-                    -0.275637355816985785,-0.292371704722723225,\r
-                    -0.309016994374933796,-0.325568154457142933,\r
-                    -0.342020143325654891,-0.358367949545286335,\r
-                    -0.374606593415898026,-0.390731128489259616,\r
-                    -0.406736643075785997,-0.422618261740685175,\r
-                    -0.438371146789063082,-0.453990499739532427,\r
-                    -0.469471562785876373,-0.484809620246322570,\r
-                    -0.499999999999985512,-0.515038074910039723,\r
-                    -0.529919264233190468,-0.544639035015012540,\r
-                    -0.559192903470732361,-0.573576436351031616,\r
-                    -0.587785252292458593,-0.601815023152033834,\r
-                    -0.615661475325643859,-0.629320391049823069,\r
-                    -0.642787609686525041,-0.656059028990493065,\r
-                    -0.669130606358844027,-0.681998360062484377,\r
-                    -0.694658370458983265,-0.707106781186533584,\r
-                    -0.719339800338637314,-0.731353701619156804,\r
-                    -0.743144825477380699,-0.754709580222758580,\r
-                    -0.766044443118964691,-0.777145961456957690,\r
-                    -0.788010753606709025,-0.798635510047280062,\r
-                    -0.809016994374934795,-0.819152044288979364,\r
-                    -0.829037572555029412,-0.838670567945412060,\r
-                    -0.848048096156414188,-0.857167300702100676,\r
-                    -0.866025403784427272,-0.874619707139384750,\r
-                    -0.882947592858916108,-0.891006524188357352,\r
-                    -0.898794046299156713,-0.906307787036639945,\r
-                    -0.913545457642591208,-0.920504853452430938,\r
-                    -0.927183854566778320,-0.933580426497192972,\r
-                    -0.939692620785899990,-0.945518575599308742,\r
-                    -0.951056516295145871,-0.956304755963028108,\r
-                    -0.961261695938311900,-0.965925826289061651,\r
-                    -0.970295726275990256,-0.974370064785229362,\r
-                    -0.978147600733800138,-0.981627183447658869,\r
-                    -0.984807753012203468,-0.987688340595133552,\r
-                    -0.990268068741566587,-0.992546151641318764,\r
-                    -0.994521895368270514,-0.996194698091743103,\r
-                    -0.997564050259822310,-0.998629534754572390,\r
-                    -0.999390827019094763,-0.999847695156390714,\r
-                    -1.000000000000000000,-0.999847695156391714,\r
-                    -0.999390827019096761,-0.998629534754575388,\r
-                    -0.997564050259826307,-0.996194698091748099,\r
-                    -0.994521895368276398,-0.992546151641325647,\r
-                    -0.990268068741574470,-0.987688340595142433,\r
-                    -0.984807753012213349,-0.981627183447669860,\r
-                    -0.978147600733812128,-0.974370064785242240,\r
-                    -0.970295726276004022,-0.965925826289076417,\r
-                    -0.961261695938327665,-0.956304755963044872,\r
-                    -0.951056516295163523,-0.945518575599327393,\r
-                    -0.939692620785919530,-0.933580426497213511,\r
-                    -0.927183854566799748,-0.920504853452453253,\r
-                    -0.913545457642614411,-0.906307787036664148,\r
-                    -0.898794046299181804,-0.891006524188383331,\r
-                    -0.882947592858942976,-0.874619707139412506,\r
-                    -0.866025403784455916,-0.857167300702130208,\r
-                    -0.848048096156444498,-0.838670567945443146,\r
-                    -0.829037572555061497,-0.819152044289012227,\r
-                    -0.809016994374968434,-0.798635510047314479,\r
-                    -0.788010753606744219,-0.777145961456993772,\r
-                    -0.766044443119001550,-0.754709580222796106,\r
-                    -0.743144825477418891,-0.731353701619195773,\r
-                    -0.719339800338677060,-0.707106781186574107,\r
-                    -0.694658370459024455,-0.681998360062526232,\r
-                    -0.669130606358886548,-0.656059028990536253,\r
-                    -0.642787609686568784,-0.629320391049867478,\r
-                    -0.615661475325688934,-0.601815023152079465,\r
-                    -0.587785252292504890,-0.573576436351078467,\r
-                    -0.559192903470779767,-0.544639035015060502,\r
-                    -0.529919264233238985,-0.515038074910088794,\r
-                    -0.500000000000035083,-0.484809620246372641,\r
-                    -0.469471562785926888,-0.453990499739583386,\r
-                    -0.438371146789114541,-0.422618261740737022,\r
-                    -0.406736643075838289,-0.390731128489312296,\r
-                    -0.374606593415951039,-0.358367949545339737,\r
-                    -0.342020143325708625,-0.325568154457197001,\r
-                    -0.309016994374988196,-0.292371704722777903,\r
-                    -0.275637355817040797,-0.258819045102562761,\r
-                    -0.241921895599710085,-0.224951054343907719,\r
-                    -0.207911690817802419,-0.190808995376588242,\r
-                    -0.173648177666974129,-0.156434465040274973,\r
-                    -0.139173100960109847,-0.121869343405192190,\r
-                    -0.104528463267698463,-0.087155742747703435,\r
-                    -0.069756473744170822,-0.052335956242989604,\r
-                    -0.034899496702546981,-0.017452406437329739\r
-          };\r
-\r
-static double costable[360] = {\r
-1.000000000000000000,0.999847695156391270,\r
-                    0.999390827019095762,0.998629534754573833,\r
-                    0.997564050259824198,0.996194698091745545,\r
-                    0.994521895368273290,0.992546151641321983,\r
-                    0.990268068741570362,0.987688340595137770,\r
-                    0.984807753012208020,0.981627183447663976,\r
-                    0.978147600733805689,0.974370064785235246,\r
-                    0.970295726275996473,0.965925826289068312,\r
-                    0.961261695938318894,0.956304755963035436,\r
-                    0.951056516295153531,0.945518575599316735,\r
-                    0.939692620785908317,0.933580426497201743,\r
-                    0.927183854566787313,0.920504853452440264,\r
-                    0.913545457642600867,0.906307787036649826,\r
-                    0.898794046299166927,0.891006524188367788,\r
-                    0.882947592858926766,0.874619707139395630,\r
-                    0.866025403784438486,0.857167300702112112,\r
-                    0.848048096156425846,0.838670567945423828,\r
-                    0.829037572555041513,0.819152044288991576,\r
-                    0.809016994374947229,0.798635510047292607,\r
-                    0.788010753606721681,0.777145961456970569,\r
-                    0.766044443118977680,0.754709580222771681,\r
-                    0.743144825477393911,0.731353701619170127,\r
-                    0.719339800338650748,0.707106781186547129,\r
-                    0.694658370458996810,0.681998360062498032,\r
-                    0.669130606358857682,0.656059028990506721,\r
-                    0.642787609686538697,0.629320391049836836,\r
-                    0.615661475325657626,0.601815023152047601,\r
-                    0.587785252292472471,0.573576436351045382,\r
-                    0.559192903470746128,0.544639035015026307,\r
-                    0.529919264233204124,0.515038074910053378,\r
-                    0.499999999999999112,0.484809620246336115,\r
-                    0.469471562785889862,0.453990499739545861,\r
-                    0.438371146789076460,0.422618261740698442,\r
-                    0.406736643075799154,0.390731128489272661,\r
-                    0.374606593415910960,0.358367949545299158,\r
-                    0.342020143325667547,0.325568154457155479,\r
-                    0.309016994374946230,0.292371704722735493,\r
-                    0.275637355816997887,0.258819045102519463,\r
-                    0.241921895599666398,0.224951054343863643,\r
-                    0.207911690817757955,0.190808995376543389,\r
-                    0.173648177666928888,0.156434465040229398,\r
-                    0.139173100960063939,0.121869343405145950,\r
-                    0.104528463267651903,0.087155742747656584,\r
-                    0.069756473744123679,0.052335956242942190,\r
-                    0.034899496702499304,0.017452406437281822,\r
-                    -0.000000000000001715,-0.017452406437285253,\r
-                    -0.034899496702502732,-0.052335956242945618,\r
-                    -0.069756473744127107,-0.087155742747659998,\r
-                    -0.104528463267655317,-0.121869343405149350,\r
-                    -0.139173100960067325,-0.156434465040232784,\r
-                    -0.173648177666932274,-0.190808995376546747,\r
-                    -0.207911690817761285,-0.224951054343866974,\r
-                    -0.241921895599669701,-0.258819045102522793,\r
-                    -0.275637355817001217,-0.292371704722738768,\r
-                    -0.309016994374949450,-0.325568154457158698,\r
-                    -0.342020143325670822,-0.358367949545302322,\r
-                    -0.374606593415914124,-0.390731128489275825,\r
-                    -0.406736643075802318,-0.422618261740701329,\r
-                    -0.438371146789079125,-0.453990499739548303,\r
-                    -0.469471562785892083,-0.484809620246338169,\r
-                    -0.500000000000000999,-0.515038074910054933,\r
-                    -0.529919264233205567,-0.544639035015027528,\r
-                    -0.559192903470747127,-0.573576436351046159,\r
-                    -0.587785252292473026,-0.601815023152048045,\r
-                    -0.615661475325657848,-0.629320391049836947,\r
-                    -0.642787609686538697,-0.656059028990506499,\r
-                    -0.669130606358857238,-0.681998360062497477,\r
-                    -0.694658370458996033,-0.707106781186546240,\r
-                    -0.719339800338649749,-0.731353701619168906,\r
-                    -0.743144825477392579,-0.754709580222770238,\r
-                    -0.766044443118976237,-0.777145961456968903,\r
-                    -0.788010753606719905,-0.798635510047290720,\r
-                    -0.809016994374945231,-0.819152044288989578,\r
-                    -0.829037572555039404,-0.838670567945421719,\r
-                    -0.848048096156423625,-0.857167300702109891,\r
-                    -0.866025403784436265,-0.874619707139393410,\r
-                    -0.882947592858924435,-0.891006524188365345,\r
-                    -0.898794046299164484,-0.906307787036647494,\r
-                    -0.913545457642598424,-0.920504853452437932,\r
-                    -0.927183854566784982,-0.933580426497199412,\r
-                    -0.939692620785906096,-0.945518575599314515,\r
-                    -0.951056516295151311,-0.956304755963033326,\r
-                    -0.961261695938316785,-0.965925826289066314,\r
-                    -0.970295726275994586,-0.974370064785233358,\r
-                    -0.978147600733803912,-0.981627183447662310,\r
-                    -0.984807753012206577,-0.987688340595136327,\r
-                    -0.990268068741569030,-0.992546151641320873,\r
-                    -0.994521895368272291,-0.996194698091744657,\r
-                    -0.997564050259823532,-0.998629534754573389,\r
-                    -0.999390827019095318,-0.999847695156391048,\r
-                    -1.000000000000000000,-0.999847695156391381,\r
-                    -0.999390827019096095,-0.998629534754574499,\r
-                    -0.997564050259825086,-0.996194698091746544,\r
-                    -0.994521895368274622,-0.992546151641323537,\r
-                    -0.990268068741572027,-0.987688340595139658,\r
-                    -0.984807753012210241,-0.981627183447666418,\r
-                    -0.978147600733808353,-0.974370064785238244,\r
-                    -0.970295726275999804,-0.965925826289071865,\r
-                    -0.961261695938322669,-0.956304755963039654,\r
-                    -0.951056516295157972,-0.945518575599321509,\r
-                    -0.939692620785913424,-0.933580426497207072,\r
-                    -0.927183854566793086,-0.920504853452446370,\r
-                    -0.913545457642607195,-0.906307787036656598,\r
-                    -0.898794046299173921,-0.891006524188375226,\r
-                    -0.882947592858934649,-0.874619707139403846,\r
-                    -0.866025403784447034,-0.857167300702120993,\r
-                    -0.848048096156435061,-0.838670567945433487,\r
-                    -0.829037572555051505,-0.819152044289001902,\r
-                    -0.809016994374957998,-0.798635510047303709,\r
-                    -0.788010753606733227,-0.777145961456982559,\r
-                    -0.766044443118990004,-0.754709580222784449,\r
-                    -0.743144825477407012,-0.731353701619183672,\r
-                    -0.719339800338664737,-0.707106781186561450,\r
-                    -0.694658370459011576,-0.681998360062513242,\r
-                    -0.669130606358873337,-0.656059028990522708,\r
-                    -0.642787609686555128,-0.629320391049853711,\r
-                    -0.615661475325674945,-0.601815023152065254,\r
-                    -0.587785252292490457,-0.573576436351063812,\r
-                    -0.559192903470765002,-0.544639035015045625,\r
-                    -0.529919264233223886,-0.515038074910073473,\r
-                    -0.500000000000019651,-0.484809620246357043,\r
-                    -0.469471562785911123,-0.453990499739567510,\r
-                    -0.438371146789098498,-0.422618261740720869,\r
-                    -0.406736643075822024,-0.390731128489295920,\r
-                    -0.374606593415934497,-0.358367949545323083,\r
-                    -0.342020143325691917,-0.325568154457180181,\r
-                    -0.309016994374971266,-0.292371704722760861,\r
-                    -0.275637355817023644,-0.258819045102545497,\r
-                    -0.241921895599692793,-0.224951054343890372,\r
-                    -0.207911690817784989,-0.190808995376570756,\r
-                    -0.173648177666956560,-0.156434465040257376,\r
-                    -0.139173100960092194,-0.121869343405174496,\r
-                    -0.104528463267680741,-0.087155742747685686,\r
-                    -0.069756473744153044,-0.052335956242971805,\r
-                    -0.034899496702529162,-0.017452406437311916,\r
-                    -0.000000000000028605,0.017452406437254715,\r
-                    0.034899496702471985,0.052335956242914670,\r
-                    0.069756473744095979,0.087155742747628689,\r
-                    0.104528463267623842,0.121869343405117708,\r
-                    0.139173100960035545,0.156434465040200865,\r
-                    0.173648177666900216,0.190808995376514606,\r
-                    0.207911690817729033,0.224951054343834611,\r
-                    0.241921895599637282,0.258819045102490264,\r
-                    0.275637355816968632,0.292371704722706127,\r
-                    0.309016994374916809,0.325568154457126058,\r
-                    0.342020143325638126,0.358367949545269682,\r
-                    0.374606593415881484,0.390731128489243240,\r
-                    0.406736643075769733,0.422618261740669021,\r
-                    0.438371146789047095,0.453990499739516551,\r
-                    0.469471562785860608,0.484809620246306972,\r
-                    0.499999999999970080,0.515038074910024402,\r
-                    0.529919264233175369,0.544639035014997663,\r
-                    0.559192903470717484,0.573576436351016961,\r
-                    0.587785252292444271,0.601815023152019624,\r
-                    0.615661475325629759,0.629320391049809191,\r
-                    0.642787609686511385,0.656059028990479520,\r
-                    0.669130606358830815,0.681998360062471387,\r
-                    0.694658370458970387,0.707106781186521038,\r
-                    0.719339800338624991,0.731353701619144592,\r
-                    0.743144825477368709,0.754709580222746812,\r
-                    0.766044443118953255,0.777145961456946477,\r
-                    0.788010753606698033,0.798635510047269292,\r
-                    0.809016994374924359,0.819152044288969150,\r
-                    0.829037572555019531,0.838670567945402290,\r
-                    0.848048096156404752,0.857167300702091572,\r
-                    0.866025403784418391,0.874619707139376090,\r
-                    0.882947592858907782,0.891006524188349247,\r
-                    0.898794046299148941,0.906307787036632395,\r
-                    0.913545457642583991,0.920504853452423943,\r
-                    0.927183854566771659,0.933580426497186644,\r
-                    0.939692620785893884,0.945518575599302968,\r
-                    0.951056516295140320,0.956304755963022890,\r
-                    0.961261695938306904,0.965925826289056988,\r
-                    0.970295726275985926,0.974370064785225365,\r
-                    0.978147600733796474,0.981627183447655538,\r
-                    0.984807753012200360,0.987688340595130776,\r
-                    0.990268068741564034,0.992546151641316543,\r
-                    0.994521895368268627,0.996194698091741548,\r
-                    0.997564050259821089,0.998629534754571502,\r
-                    0.999390827019094097,0.999847695156390381\r
-     \r
-     int dtoi(double x)\r
-     {\r
-          /* Rounds a floating point number to an integer */\r
-     \r
-          int y;\r
-     \r
-          y = x;\r
-          if (y < (x + 0.5))\r
-               y++;\r
-          return(y);\r
-     }\r
-     \r
-     int getmode(int *ncols)\r
-     {\r
-          /* Returns current video mode and number of columns in ncols */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 0x0F;\r
-          int86(0x10, &inreg, &outreg);\r
-          *ncols = outreg.h.ah;\r
-          return(outreg.h.al);\r
-     }\r
-     \r
-     void setvideo(unsigned char mode)\r
-     {\r
-          /* Sets the video display mode and clears the screen */\r
-          /* (modes above 127 on VGA monitors do not clear the screen) */\r
-     \r
-          asm mov ax , mode;\r
-          asm int 10h;\r
-     \r
-          /* Set global variables */\r
-          switch(mode)\r
-          {\r
-               case 0:\r
-               case 1:\r
-               case 2:\r
-               case 3: break;\r
-               case 4:\r
-               case 9:\r
-               case 13:\r
-               case 19:\r
-               case 5: maxx = 320;\r
-                    maxy = 200;\r
-                    break;\r
-               case 14:\r
-               case 10:\r
-               case 6: maxx = 640;\r
-                    maxy = 200;\r
-                    break;\r
-               case 7: maxx = 720;\r
-                    maxy = 400;\r
-                    break;\r
-               case 8: maxx = 160;\r
-                    maxy = 200;\r
-                    break;\r
-               case 15:\r
-               case 16: maxx = 640;\r
-                     maxy = 350;\r
-                     break;\r
-               case 17:\r
-               case 18: maxx = 640;\r
-                     maxy = 480;\r
-                     break;\r
-     \r
-          }\r
-          _dmode = mode;\r
-     }\r
-     \r
-     void getcursor(int *row, int *col)\r
-     {\r
-          /* Returns the cursor position and size for the currently\r
-     active\r
-             video display page */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.bh = 0;\r
-          inreg.h.ah = 0x03;\r
-          int86(0x10, &inreg, &outreg);\r
-          *row = outreg.h.dh;\r
-          *col = outreg.h.dl;\r
-     }\r
-     \r
-     void setcursor(char status)\r
-     {\r
-          /* Set cursor size, if status is 0x20 the cursor is not\r
-     displayed */\r
-     \r
-          asm mov ah,1\r
-          asm mov ch,status\r
-          asm mov cl,7\r
-          asm int 10h\r
-     }\r
-     \r
-     void at(int row, int col)\r
-     {\r
-          /* Position text cursor on current screen */\r
-     \r
-          asm mov bh , 0;\r
-          asm mov dh , row;\r
-          asm mov dl , col;\r
-          asm mov ah , 02h;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void clsc(unsigned char attrib)\r
-     {\r
-          /* Clear display and fill with new attribute */\r
-     \r
-          asm mov ax , 073dh;\r
-          asm mov bh , attrib;\r
-          asm mov cx , 0;\r
-          asm mov dx , 3c4fh;\r
-          asm int 10h;\r
-     \r
-          /* Now move text cursor to origin (0,0) */\r
-          asm mov bh , 0;\r
-          asm mov dx , 0;\r
-          asm mov ah , 02h;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void clrwindow(int x1, int y1,int x2, int y2, unsigned char attrib)\r
-     {\r
-          /* Clear a text window */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.al = (unsigned char)(y2 - y1 + 1);\r
-          inreg.h.bh = attrib;\r
-          inreg.h.cl = (unsigned char)x1;\r
-          inreg.h.ch = (unsigned char)y1;\r
-          inreg.h.dl = (unsigned char)x2;\r
-          inreg.h.dh = (unsigned char)y2;\r
-          inreg.h.ah = 0x06;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     void scrollsc(void)\r
-     {\r
-          /* Scroll the text screen */\r
-     \r
-          asm mov al,01h;\r
-          asm mov cx,0\r
-          asm mov dx,3c4fh;\r
-          asm mov bh,attribute;\r
-          asm mov ah, 06h;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void winscr(unsigned char left,unsigned char top, unsigned char\r
-     right,unsigned char bottom,\r
-               unsigned char direction, unsigned char numlines)\r
-     {\r
-          /* Scroll a text window */\r
-     \r
-          asm mov al , numlines;\r
-          asm mov cl , left;\r
-          asm mov ch , top;\r
-          asm mov dl , right;\r
-          asm mov dh , bottom;\r
-          asm mov bh , attribute;\r
-          asm mov ah , direction;\r
-          asm int 10h;\r
-     }\r
-     \r
-     unsigned char attr(int foregrnd, int backgrnd)\r
-     {\r
-          /* Convert a colour pair into a single attribute */\r
-     \r
-          return((char)((backgrnd << 4) + foregrnd));\r
-     }\r
-     \r
-     void shadewin(unsigned char left, unsigned char top, unsigned char\r
-     right,   unsigned char bottom)\r
-     {\r
-          /* Shade a text window */\r
-     \r
-          int row;\r
-          int col;\r
-          int oldrow;\r
-          int oldcol;\r
-     \r
-          /* Preserve existing coords */\r
-          getcursor(&oldrow,&oldcol);\r
-     \r
-          col = right + 1;\r
-          for (row = top + 1; row <= bottom + 1; row++)\r
-          {\r
-               /* Move to row,col */\r
-               asm mov bh , 0;\r
-               asm mov dh , row;\r
-               asm mov dl , col;\r
-               asm mov ah , 02h;\r
-               asm int 10h;\r
-               /* Get character */\r
-               asm mov ah , 0Fh;\r
-               asm int 10h;\r
-               asm mov ah , 08h;\r
-               asm int 10h;\r
-     \r
-               /* Write in attribute 7 */\r
-               asm mov ah , 09h;\r
-               asm mov bl, 07h;\r
-               asm mov cx, 01h;\r
-               asm int 10h;\r
-          }\r
-          bottom++;\r
-          for (col = left + 1; col <= right + 1; col++)\r
-          {\r
-               /* Move to row,col */\r
-               asm mov bh , 0;\r
-               asm mov dh , bottom;\r
-               asm mov dl , col;\r
-               asm mov ah , 02h;\r
-               asm int 10h;\r
-     \r
-               /* Get character */\r
-               asm mov ah , 0Fh;\r
-               asm int 10h;\r
-               asm mov ah , 08h;\r
-               asm int 10h;\r
-     \r
-               /* Write in attribute 7 */\r
-               asm mov ah , 09h;\r
-               asm mov bl, 07h;\r
-               asm mov cx, 01h;\r
-               asm int 10h;\r
-          }\r
-     \r
-          /* Return to original position */\r
-          /* Move to row,col */\r
-          asm mov bh , 0;\r
-          asm mov dh , oldrow;\r
-          asm mov dl , oldcol;\r
-          asm mov ah , 02h;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void bprintf(char *format, ...)\r
-     {\r
-          /* print text to graphics screen correctly */\r
-     \r
-          va_list arg_ptr;\r
-          char output[1000];\r
-          int c, r, n, p = 0;\r
-          unsigned char text;\r
-          unsigned char page;\r
-     \r
-          va_start(arg_ptr, format);\r
-          vsprintf(output, format, arg_ptr);\r
-     \r
-          if (strcmp(output,"\r\n") == 0)\r
-               fputs(output,stdout);\r
-     \r
-          else\r
-          {\r
-               asm mov ah , 0Fh;\r
-               asm int 10h;\r
-               asm mov page, bh;\r
-     \r
-               getmode(&n);\r
-               getcursor(&r,&c);\r
-     \r
-               while (output[p])\r
-               {\r
-                    text = output[p++];\r
-                    asm mov bh , page;\r
-                    asm mov bl , attribute;\r
-                    asm mov cx , 01h;\r
-                    asm mov ah , 09h;\r
-                    asm mov al , text;\r
-                    asm int 10h;\r
-     \r
-                    c++;\r
-                    if (c < (n-1))\r
-                         at( r, c);\r
-                    else\r
-                    {\r
-                         c = 0;\r
-                         at(++r,0);\r
-                    }\r
-               }\r
-          }\r
-     }\r
-     \r
-     void wrtstr(char *output)\r
-     {\r
-          /* TTY text output. The original screen attributes remain\r
-     unaffected */\r
-     \r
-          int p = 0;\r
-          unsigned char page;\r
-          unsigned char text;\r
-     \r
-          asm mov ah , 0Fh;\r
-          asm int 10h;\r
-          asm mov page, bh;\r
-     \r
-          while (output[p])\r
-          {\r
-               text = output[p++];\r
-               asm mov bh , page;\r
-               asm mov ah , 0Eh;\r
-               asm mov al , text;\r
-               asm int 10h;\r
-          }\r
-     }\r
-     \r
-     void getwin(int left, int top, int right, int bottom, char *buffer)\r
-     {\r
-          /* Read a text window into a variable */\r
-     \r
-          int oldleft;\r
-          unsigned char page;\r
-     \r
-          asm mov ah , 0Fh;\r
-          asm int 10h;\r
-          asm mov page, bh;\r
-     \r
-          while(top <= bottom)\r
-          {\r
-               oldleft = left;\r
-               while(left <= right)\r
-               {\r
-                    at(top,left);\r
-                    asm mov bh , page;\r
-                    asm mov ah , 08h;\r
-                    asm int 10h;\r
-                    *buffer++ = _AL;\r
-                    *buffer++ = _AH;\r
-                    left++;\r
-               }\r
-               left = oldleft;\r
-               top++;\r
-          }\r
-     }\r
-     \r
-     void putwin(int left, int top, int right, int bottom,char *buffer)\r
-     {\r
-          /* Display a text window from a variable */\r
-     \r
-          int oldleft;\r
-          unsigned char chr;\r
-          unsigned char attr;\r
-          unsigned char page;\r
-     \r
-          asm mov ah , 0Fh;\r
-          asm int 10h;\r
-          asm mov page, bh;\r
-     \r
-          while(top <= bottom)\r
-          {\r
-               oldleft = left;\r
-               while(left <= right)\r
-               {\r
-                    at(top,left);\r
-                    chr = *buffer++;\r
-                    attr = *buffer++;\r
-                    asm mov bh , page;\r
-                    asm mov ah , 09h;\r
-                    asm mov al, chr;\r
-                    asm mov bl, attr;\r
-                    asm mov cx,1;\r
-                    asm int 10h;\r
-                    left++;\r
-               }\r
-               left = oldleft;\r
-               top++;\r
-          }\r
-     }\r
-     \r
-     void setpalette(unsigned char palno)\r
-     {\r
-          /* Sets the video palette */\r
-     \r
-          asm mov bh,01h;\r
-          asm mov bl,palno;\r
-          asm mov ah,0Bh;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void setborder(unsigned char x)\r
-     {\r
-          /* Set border colour */\r
-     \r
-          asm mov bh, x;\r
-          asm mov ax ,1001h;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void setlines(unsigned char x)\r
-     {\r
-          /* Set text display number of lines */\r
-     \r
-          asm mov ah,11h;\r
-          asm mov al,x;\r
-          asm mov bl,0;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void graphbackground(unsigned char colour)\r
-     {\r
-          /* Selects the background colour for a graphics mode */\r
-     \r
-          asm mov bh,0;\r
-          asm mov bl,colour;\r
-          asm mov ah, 0Bh;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void plot(int x, int y, unsigned char colour)\r
-     {\r
-          /* Sets a pixel at the specified coordinates to the specified\r
-     colour.\r
-             The variables _lastx and _lasty are updated. */\r
-     \r
-          unsigned char far *video;\r
-     \r
-          _lastx = x;\r
-          _lasty = y;\r
-     \r
-          if (_dmode == 19)\r
-          {\r
-               video = MK_FP(0xA000,0);\r
-               video[x+y*320] = colour;\r
-          }\r
-          else\r
-          {\r
-               asm mov al , colour;\r
-               asm mov bh , 00;\r
-               asm mov cx , x;\r
-               asm mov dx , y;\r
-               asm mov ah , 0Ch;\r
-               asm int 10h;\r
-          }\r
-     }\r
-     \r
-     int pixset(int x, int y)\r
-     {\r
-          /* Returns the colour of the specified pixel */\r
-     \r
-          asm mov cx ,x;\r
-          asm mov dx ,y;\r
-          asm mov ah ,0Dh;\r
-          asm int 10h;\r
-          return(_AL);\r
-     }\r
-     \r
-     void move(int x, int y)\r
-     {\r
-          /* Sets the value of the variables _lastx and _lasty */\r
-     \r
-          _lastx = x;\r
-          _lasty = y;\r
-     }\r
-     \r
-     void line(int a, int b, int c, int d, int col)\r
-     {\r
-          /* Draws a straight line from (a,b) to (c,d) in colour col */\r
-     \r
-          int u;\r
-          int v;\r
-          int d1x;\r
-          int d1y;\r
-          int d2x;\r
-          int d2y;\r
-          int m;\r
-          int n;\r
-          int s;\r
-          int i;\r
-     \r
-          if (a < 0)\r
-               a = 0;\r
-          else\r
-          if (a > maxx)\r
-               a = maxx;\r
-     \r
-          if (c < 0)\r
-               c = 0;\r
-          else\r
-          if (c > maxx)\r
-               c = maxx;\r
-     \r
-          if (b < 0)\r
-               b = 0;\r
-          else\r
-          if (b > maxy)\r
-               b = maxy;\r
-     \r
-          if (d < 0)\r
-               d = 0;\r
-          else\r
-          if (d > maxy)\r
-               d = maxy;\r
-     \r
-          u = c - a;\r
-          v = d - b;\r
-     \r
-          if (u == 0)\r
-          {\r
-               d1x = 0;\r
-               m = 0;\r
-          }\r
-          else\r
-          {\r
-               m = abs(u);\r
-               if (u < 0)\r
-                    d1x = -1;\r
-               else\r
-               if (u > 0)\r
-                    d1x = 1;\r
-          }\r
-          if ( v == 0)\r
-          {\r
-               d1y = 0;\r
-               n = 0;\r
-          }\r
-          else\r
-          {\r
-               n = abs(v);\r
-               if (v < 0)\r
-                    d1y = -1;\r
-               else\r
-               if (v > 0)\r
-                    d1y = 1;\r
-          }\r
-          if (m > n)\r
-          {\r
-               d2x = d1x;\r
-               d2y = 0;\r
-          }\r
-          else\r
-          {\r
-               d2x = 0;\r
-               d2y = d1y;\r
-               m = n;\r
-               n = abs(u);\r
-          }\r
-          s = m / 2;\r
-     \r
-          for (i = 0; i <= m; i++)\r
-          {\r
-               asm mov al , col;\r
-               asm mov bh , 0;\r
-               asm mov ah ,0Ch;\r
-               asm mov cx ,a;\r
-               asm mov dx ,b;\r
-               asm int 10h;\r
-     \r
-               s += n;\r
-               if (s >= m)\r
-               {\r
-                    s -= m;\r
-                    a += d1x;\r
-                    b += d1y;\r
-               }\r
-               else\r
-               {\r
-                    a += d2x;\r
-                    b += d2y;\r
-               }\r
-          }\r
-          _lastx = a;\r
-          _lasty = b;\r
-     }\r
-     \r
-     void ellipse(int x, int y, int xrad, int yrad,double incline,int\r
-     col)\r
-     {\r
-          /* Draws an ellipse */\r
-     \r
-          int f;\r
-          float a;\r
-          float b;\r
-          float c;\r
-          float d;\r
-          int cols;\r
-          double div;\r
-     \r
-          incline = 1 / sintable[(int)incline];\r
-     \r
-          if (getmode(&cols) == 6)\r
-               div = 2.2;\r
-          else\r
-               div = 1.3;\r
-     \r
-               /* Compensate for pixel shape */\r
-     \r
-          a = x + xrad;\r
-          b = y + sintable[0] * yrad + xrad/incline / div;\r
-     \r
-          for(f = 5; f < 360; f += 5)\r
-          {\r
-               c = x + costable[f] * xrad;\r
-               d = y + sintable[f] * yrad + (costable[f] *\r
-     xrad)/incline/div;\r
-     \r
-               line(a,b,c,d,col);\r
-     \r
-               a = c;\r
-               b = d;\r
-          }\r
-          /* Ensure join */\r
-          line(a,b,x + xrad,y + sintable[0] * yrad + xrad/incline /\r
-     div,col);\r
-     }\r
-     \r
-     void polygon(int x, int y, int rad, int col, int sides, int start)\r
-     {\r
-          /* Draws a regular polygon */\r
-     \r
-          double f;\r
-          double div;\r
-          double a;\r
-          double b;\r
-          double c;\r
-          double d;\r
-          double aa;\r
-          double bb;\r
-          int cols;\r
-          double step;\r
-     \r
-          step = 360 / sides;\r
-     \r
-          if (getmode(&cols) == 6)\r
-               div = 2.2;\r
-          else\r
-               div = 1.3;\r
-          aa = a = x + costable[start] * rad;\r
-          bb = b = y + sintable[start] * rad / div;\r
-     \r
-          for(f = start + step; f < start + 360; f += step)\r
-          {\r
-               c = x + costable[(int)f] * rad;\r
-               d = y + sintable[(int)f] * rad / div;\r
-               line(a,b,c,d,col);\r
-               a = c;\r
-               b = d;\r
-          }\r
-          line(a,b,aa,bb,col);\r
-     }\r
-     \r
-     void arc(int x, int y, int rad, int start, int end,int col)\r
-     {\r
-          /* Draw an arc */\r
-     \r
-          int f;\r
-          float a;\r
-          float b;\r
-          float c;\r
-          float d;\r
-          int cols;\r
-          float div;\r
-     \r
-          if (getmode(&cols) == 6)\r
-               div = 2.2;\r
-          else\r
-               div = 1.3;\r
-          a = x + costable[start] * rad;\r
-          b = y + sintable[start] * rad / div;\r
-     \r
-          for(f = start; f <= end; f ++)\r
-          {\r
-               c = x + costable[f] * rad;\r
-               d = y + sintable[f] * rad / div;\r
-               line(a,b,c,d,col);\r
-               a = c;\r
-               b = d;\r
-          }\r
-     }\r
-     \r
-     void segm(int x, int y, int rad, int start, int end,int col)\r
-     {\r
-          /* Draw a segment of a circle */\r
-     \r
-          int f;\r
-          float a;\r
-          float b;\r
-          float c;\r
-          float d;\r
-          int cols;\r
-          double div;\r
-     \r
-          if (getmode(&cols) == 6)\r
-               div = 2.2;\r
-          else\r
-               div = 1.3;\r
-          a = x + costable[start] * rad;\r
-          b = y + sintable[start] * rad / div;\r
-     \r
-          line(x,y,a,b,col);\r
-     \r
-          for(f = start; f <= end ; f ++)\r
-          {\r
-               c = x + costable[f] * rad;\r
-               d = y + sintable[f] * rad / div;\r
-               line(a,b,c,d,col);\r
-               a = c;\r
-               b = d;\r
-          }\r
-          line(x,y,a,b,col);\r
-     }\r
-     \r
-     void box(int xa,int ya, int xb, int yb, int col)\r
-     {\r
-          /* Draws a box for use in 2d histogram graphs etc */\r
-     \r
-          line(xa,ya,xa,yb,col);\r
-          line(xa,yb,xb,yb,col);\r
-          line(xb,yb,xb,ya,col);\r
-          line(xb,ya,xa,ya,col);\r
-     }\r
-     \r
-     void tri(int xa,int ya, int xb, int yb, int xc, int yc,int col)\r
-     {\r
-          /* Draw a triangle */\r
-     \r
-          line(xa,ya,xb,yb,col);\r
-          line(xb,yb,xc,yc,col);\r
-          line(xc,yc,xa,ya,col);\r
-     }\r
-     \r
-     void fill(int x, int y, int col,int pattern)\r
-     {\r
-          /* Fill a boundered shape using a hatch pattern */\r
-     \r
-          int xa;\r
-          int ya;\r
-          int bn;\r
-          int byn;\r
-     \r
-          int hatch[10][8] = {     255,255,255,255,255,255,255,255,\r
-                         128,64,32,16,8,4,2,1,\r
-                         1,2,4,8,16,32,64,128,\r
-                         1,2,4,8,8,4,2,1,\r
-                         238,238,238,238,238,238,238,238,\r
-                         170,85,170,85,170,85,170,85,\r
-                         192,96,48,24,12,6,3,1,\r
-                         62,62,62,0,227,227,227,0,\r
-                         129,66,36,24,24,36,66,129,\r
-                         146,36,146,36,146,36,146,36\r
-                    };\r
-     \r
-          /* Patterns for fill, each integer describes a row of dots */\r
-     \r
-          xa = x;\r
-          ya = y;  /* Save Origin */\r
-     \r
-          if(pixset(x,y))\r
-               return;\r
-     \r
-          bn = 1;\r
-          byn = 0;\r
-     \r
-          do\r
-          {\r
-               if (hatch[pattern][byn] != 0)\r
-               {\r
-                    /* If blank ignore */\r
-                    do\r
-                    {\r
-                         if ((bn & hatch[pattern][byn]) == bn)\r
-                         {\r
-                              asm mov al , col;\r
-                              asm mov bh , 00;\r
-                              asm mov cx , x;\r
-                              asm mov dx , y;\r
-                              asm mov ah , 0Ch;\r
-                              asm int 10h;\r
-                         }\r
-                         x--;\r
-                         bn <<= 1;\r
-                         if (bn > 128)\r
-                              bn = 1;\r
-                    }\r
-                    while(!pixset(x,y) && (x > -1));\r
-     \r
-                    x = xa + 1;\r
-                    bn = 128;\r
-     \r
-                    do\r
-                    {\r
-                         if ((bn & hatch[pattern][byn]) == bn)\r
-                         {\r
-                              asm mov al , col;\r
-                              asm mov bh , 00;\r
-                              asm mov cx , x;\r
-                              asm mov dx , y;\r
-                              asm mov ah , 0Ch;\r
-                              asm int 10h;\r
-                         }\r
-                         x++;\r
-                         bn >>=1;\r
-                         if (bn <1)\r
-                              bn = 128;\r
-                    }\r
-                    while((!pixset(x,y)) && (x <= maxx));\r
-               }\r
-               x = xa;\r
-               y--;\r
-               bn = 1;\r
-               byn++;\r
-               if (byn > 7)\r
-                    byn = 0;\r
-     \r
-          }\r
-          while(!pixset(x,y) && ( y > -1));\r
-     \r
-          /* Now travel downwards */\r
-     \r
-          y = ya + 1;\r
-     \r
-          byn = 7;\r
-          bn = 1;\r
-          do\r
-          {\r
-               /* Travel left */\r
-               if (hatch[pattern][byn] !=0)\r
-               {\r
-                    do\r
-                    {\r
-                         if ( (bn & hatch[pattern][byn]) == bn)\r
-                         {\r
-                              asm mov al , col;\r
-                              asm mov bh , 00;\r
-                              asm mov cx , x;\r
-                              asm mov dx , y;\r
-                              asm mov ah , 0Ch;\r
-                              asm int 10h;\r
-                         }\r
-     \r
-                         x--;\r
-                         bn <<= 1;\r
-                         if (bn > 128)\r
-                              bn = 1;\r
-                    }\r
-                    while(!pixset(x,y) && (x > -1));\r
-     \r
-                    /* Back to x origin */\r
-                    x = xa + 1 ;\r
-                    bn = 128;\r
-     \r
-                    /* Travel right */\r
-                    do\r
-                    {\r
-                         if ((bn & hatch[pattern][byn]) == bn)\r
-                         {\r
-                              asm mov al , col;\r
-                              asm mov bh , 00;\r
-                              asm mov cx , x;\r
-                              asm mov dx , y;\r
-                              asm mov ah , 0Ch;\r
-                              asm int 10h;\r
-                         }\r
-                         x++;\r
-                         bn >>=1;\r
-                         if (bn <1)\r
-                              bn = 128;\r
-                    }\r
-                    while((!pixset(x,y)) && (x <= maxx));\r
-               }\r
-               x = xa;\r
-               bn = 1;\r
-               y++;\r
-               byn--;\r
-               if (byn < 0)\r
-                    byn = 7;\r
-          }\r
-          while((!pixset(x,y)) && (y <= maxy));\r
-     }\r
-     \r
-     void invert(int xa,int ya, int xb, int yb, int col)\r
-     {\r
-          /* Invert a pixel window */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.al = (unsigned char)(128 | col);\r
-          inreg.h.ah = 0x0C;\r
-          for(inreg.x.cx = (unsigned int)xa;      inreg.x.cx <= (unsigned\r
-     int)xb; inreg.x.cx++)\r
-               for(inreg.x.dx = (unsigned)ya; inreg.x.dx <= (unsigned)yb;\r
-     inreg.x.dx++)\r
-                    int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     void circle(int x_centre , int y_centre, int radius, int colour)\r
-     {\r
-          int x,y,delta;\r
-          int startx,endx,x1,starty,endy,y1;\r
-          int asp_ratio;\r
-     \r
-          if (_dmode == 6)\r
-               asp_ratio = 22;\r
-          else\r
-               asp_ratio = 13;\r
-     \r
-          y = radius;\r
-          delta = 3 - 2 * radius;\r
-          for(x = 0; x < y; )\r
-          {\r
-               starty = y * asp_ratio / 10;\r
-               endy = (y + 1) * asp_ratio / 10;\r
-               startx = x * asp_ratio / 10;\r
-               endx = (x + 1) * asp_ratio / 10;\r
-               for(x1 = startx; x1 < endx; ++x1)\r
-               {\r
-                    plot(x1+x_centre,y+y_centre,colour);\r
-                    plot(x1+x_centre,y_centre - y,colour);\r
-                    plot(x_centre - x1,y_centre - y,colour);\r
-                    plot(x_centre - x1,y + y_centre,colour);\r
-               }\r
-     \r
-               for(y1 = starty; y1 < endy; ++y1)\r
-               {\r
-                    plot(y1+x_centre,x+y_centre,colour);\r
-                    plot(y1+x_centre,y_centre - x,colour);\r
-                    plot(x_centre - y1,y_centre - x,colour);\r
-                    plot(x_centre - y1,x + y_centre,colour);\r
-               }\r
-     \r
-               if (delta < 0)\r
-                    delta += 4 * x + 6;\r
-               else\r
-               {\r
-                    delta += 4*(x-y)+10;\r
-                    y--;\r
-               }\r
-               x++;\r
-          }\r
-     \r
-          if(y)\r
-          {\r
-               starty = y * asp_ratio / 10;\r
-               endy = (y + 1) * asp_ratio / 10;\r
-               startx = x * asp_ratio / 10;\r
-               endx = (x + 1) * asp_ratio / 10;\r
-               for(x1 = startx; x1 < endx; ++x1)\r
-               {\r
-                    plot(x1+x_centre,y+y_centre,colour);\r
-                    plot(x1+x_centre,y_centre - y,colour);\r
-                    plot(x_centre - x1,y_centre - y,colour);\r
-                    plot(x_centre - x1,y + y_centre,colour);\r
-               }\r
-     \r
-               for(y1 = starty; y1 < endy; ++y1)\r
-               {\r
-                    plot(y1+x_centre,x+y_centre,colour);\r
-                    plot(y1+x_centre,y_centre - x,colour);\r
-                    plot(x_centre - y1,y_centre - x,colour);\r
-                    plot(x_centre - y1,x + y_centre,colour);\r
-               }\r
-          }\r
-     }\r
-     \r
-     void draw(int x, int y, int colour)\r
-     {\r
-          /* Draws a line from _lastx,_lasty to x,y */\r
-     \r
-          line(_lastx,_lasty,x,y,colour);\r
-     }\r
-     \r
-     void psprite(SPRITE *sprite,int x,int y)\r
-     {\r
-          int origx;\r
-          int origy;\r
-          int z;\r
-          int count;\r
-          int col;\r
-          int pos;\r
-          unsigned char far *video;\r
-     \r
-          if (_dmode == 19)\r
-          {\r
-               /* Super fast direct video write in mode 19 for sprites */\r
-     \r
-               video = MK_FP(0xA000,0);\r
-     \r
-               origx = x;\r
-               origy = y;\r
-     \r
-               if (sprite->x != -1)\r
-               {\r
-                    /* This sprite has been displayed before */\r
-                    /* replace background */\r
-                    /* This must be done first in case the sprite\r
-     overlaps itself */\r
-                    x = sprite->x;\r
-                    y = sprite->y;\r
-                    col = 0;\r
-                    pos = x + y * 320;\r
-                    for(count = 0; count < 256; count++)\r
-                    {\r
-                         video[pos] = sprite->save[count];\r
-                         col++;\r
-                         if (col == 16)\r
-                         {\r
-                              pos += 305;\r
-                              col = 0;\r
-                         }\r
-                         else\r
-                              pos++;\r
-                    }\r
-               }\r
-     \r
-               x = origx;\r
-               y = origy;\r
-               col = 0;\r
-     \r
-               pos = x + y * 320;\r
-     \r
-               for(count = 0; count < 256; count++)\r
-               {\r
-                    sprite->save[count] = video[pos];\r
-                    z = sprite->data[count];\r
-                    if (z != 255)\r
-                         video[pos] = z;\r
-     \r
-                    col++;\r
-                    if (col == 16)\r
-                    {\r
-                         pos += 305;\r
-                         col = 0;\r
-                    }\r
-                    else\r
-                         pos++;\r
-               }\r
-               sprite->x = origx;\r
-               sprite->y = origy;\r
-     \r
-               return;\r
-          }\r
-     \r
-          origx = x;\r
-          origy = y;\r
-     \r
-          if (sprite->x != -1)\r
-          {\r
-               /* This sprite has been displayed before */\r
-               /* replace background */\r
-               /* This must be done first in case the sprite overlaps\r
-     itself */\r
-               x = sprite->x;\r
-               y = sprite->y;\r
-               col = 0;\r
-               for(count = 0; count < 256; count++)\r
-               {\r
-                    if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))\r
-                    {\r
-                         z = sprite->save[count];\r
-                         asm mov al , z;\r
-                         asm mov bh , 00;\r
-                         asm mov cx , x;\r
-                         asm mov dx , y;\r
-                         asm mov ah , 0Ch;\r
-                         asm int 10h;\r
-                    }\r
-                    col++;\r
-                    if (col == 16)\r
-                    {\r
-                         y++;\r
-                         x = sprite->x;\r
-                         col = 0;\r
-                    }\r
-                    else\r
-                         x++;\r
-               }\r
-          }\r
-     \r
-          x = origx;\r
-          y = origy;\r
-          col = 0;\r
-     \r
-          for(count = 0; count < 256; count++)\r
-          {\r
-               if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))\r
-               {\r
-                    asm mov cx , x;\r
-                    asm mov dx , y;\r
-                    asm mov ah , 0Dh;\r
-                    asm int 10h;\r
-                    asm mov z ,al;\r
-                    sprite->save[count] = z;\r
-                    z = sprite->data[count];\r
-     \r
-                    if (z != 255)\r
-                    {\r
-                         asm mov al , z;\r
-                         asm mov bh , 0;\r
-                         asm mov cx , x;\r
-                         asm mov dx , y;\r
-                         asm mov ah , 0Ch;\r
-                         asm int 10h;\r
-                    }\r
-               }\r
-               col++;\r
-               if (col == 16)\r
-               {\r
-                    y++;\r
-                    x = origx;\r
-                    col = 0;\r
-               }\r
-               else\r
-                    x++;\r
-          }\r
-          sprite->x = origx;\r
-          sprite->y = origy;\r
-          return;\r
-     }\r
-     \r
-     \r
-\r
-Displaying A PCX File\r
-\r
-The following program is offered as a practical example of graphics with\r
-the IBM PC. It reads a file of the `PCX' format and displays the image on\r
-the screen.\r
-\r
-     \r
-     /* Read a PCX file and display image */\r
-     \r
-     #include <dos.h>\r
-     #include <io.h>\r
-     #include <fcntl.h>\r
-     \r
-     typedef struct\r
-     {\r
-          unsigned char man;\r
-          unsigned char version;\r
-          unsigned char encoding;\r
-          unsigned char bpp;\r
-          int xmin;\r
-          int ymin;\r
-          int xmax;\r
-          int ymax;\r
-          int hdpi;\r
-          int vdpi;\r
-          int colormap[24];\r
-          char reserved;\r
-          unsigned char planes;\r
-          int bpl;\r
-          int palette;\r
-          int hss;\r
-          int vsize;\r
-          char pad[54];\r
-     }\r
-     PCX_HEADER;\r
-     \r
-     PCX_HEADER header;\r
-     \r
-     int x;\r
-     int y;\r
-     \r
-     union REGS inreg,outreg;\r
-     \r
-     void setvideo(unsigned char mode)\r
-     {\r
-          /* Sets the video display mode     and clears the screen */\r
-     \r
-          inreg.h.al = mode;\r
-          inreg.h.ah = 0x00;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     void plot(int x, int y, unsigned char colour)\r
-     {\r
-     \r
-             if (x < header.xmax && y < header.ymax)\r
-             {\r
-     \r
-             /* Direct video plot in modes 16 & 18 only! */\r
-             asm mov   ax,y;\r
-             asm mov   dx,80;\r
-             asm mul   dx;\r
-             asm mov   bx,x;\r
-             asm mov   cl,bl;\r
-     \r
-             asm shr   bx,1;\r
-             asm shr   bx,1;\r
-             asm shr   bx,1;\r
-             asm add   bx,ax;\r
-     \r
-             asm and   cl,7;\r
-             asm xor   cl,7;\r
-             asm mov   ah,1;\r
-             asm shl   ah,cl;\r
-     \r
-             asm mov   dx,3ceh;\r
-             asm mov   al,8;\r
-             asm out   dx,ax;\r
-     \r
-             asm mov   ax,(02h shl 8) + 5;\r
-             asm out   dx,ax;\r
-     \r
-             asm mov   ax,0A000h;\r
-             asm mov   es,ax;\r
-     \r
-             asm mov   al,es:[bx];\r
-             asm mov   al,byte ptr colour;\r
-             asm mov   es:[bx],al;\r
-     \r
-             asm mov   ax,(0FFh shl 8 ) + 8;\r
-             asm out   dx,ax;\r
-     \r
-             asm mov   ax,(00h shl 8) + 5;\r
-             asm out   dx,ax;\r
-          }\r
-     }\r
-     \r
-     void DISPLAY(unsigned char data)\r
-     {\r
-          int n;\r
-          int bit;\r
-     \r
-          bit = 32;\r
-     \r
-          for (n = 0; n < 6; n++)\r
-          {\r
-               if (data & bit)\r
-                    plot(x,y,1);\r
-               else\r
-                    plot(x,y,0);\r
-               bit >>= 1;\r
-               x++;\r
-          }\r
-     }\r
-     \r
-     main(int argc, char *argv[])\r
-     {\r
-          int fp;\r
-          int total_bytes;\r
-          int n;\r
-          unsigned char data;\r
-          int count;\r
-          int scan;\r
-     \r
-          if (argc != 2)\r
-          {\r
-               puts("USAGE IS getpcx <filename>");\r
-               exit(0);\r
-          }\r
-     \r
-          setvideo(16);\r
-     \r
-          x = 0;\r
-          y = 0;\r
-     \r
-          fp = open(argv[1],O_RDONLY|O_BINARY);\r
-     \r
-          _read(fp,&header,128);\r
-     \r
-          total_bytes = header.planes * header.bpl;\r
-     \r
-          for(scan = 0; scan <= header.ymax; scan++)\r
-          {\r
-               x = 0;\r
-     \r
-               /* First scan line */\r
-     \r
-               for(n = 0; n < total_bytes; n++)\r
-               {\r
-                    /* Read byte */\r
-                    _read(fp,&data,1);\r
-     \r
-                    count = data & 192;\r
-     \r
-                    if (count == 192)\r
-                    {\r
-                         count = data & 63;\r
-                         n += count - 1;\r
-                         _read(fp,&data,1);\r
-                         while(count)\r
-                         {\r
-                              DISPLAY(data);\r
-                              count--;\r
-                         }\r
-                    }\r
-                    else\r
-                         DISPLAY(data);\r
-     \r
-               }\r
-               x = 0;\r
-               y++;\r
-          }\r
-     }\r
-          \r
-\r
-Drawing Circles\r
-\r
-\r
-What has drawing circles got to do with advanced C programming? Well\r
-quite a lot, it is a task which is often desired by modern programmers,\r
-and it is a task which can be attacked from a number of angles. This\r
-example illustrates some of the ideas already discussed for replacing\r
-floating point numbers with integers, and using lookup tables rather than\r
-repeat calls to maths functions.\r
-\r
-A circle may be drawn by plotting each point on its circumference. The\r
-location of any point is given by;\r
-\r
-\r
-     Xp = Xo + Sine(Angle) * Radius\r
-     Yp = Yo + Cosine(Angle) * Radius\r
-\r
-Where Xp,Yp is the point to be plotted, and Xo,Yo is the centre of the\r
-circle.\r
-\r
-Thus, the simplest way to draw a circle is to calculate Xp and Yp for\r
-each angle between 1 and 360 degrees and to plot these points. There is\r
-however one fundamental error with this. As the radius of the circle\r
-increases, so also does the length of the arc between each angular point.\r
-Thus a circle of sufficient radius will be drawn with a dotted line\r
-rather than a solid line.\r
-\r
-The problem of the distance between the angular points may be tackled in\r
-two ways;\r
-\r
-  1)The number of points to be plotted can be increased, to say every 30\r
-     minutes.\r
-  2)A straight line may be drawn between each angular point.\r
-\r
-The simplest circle drawing pseudo-code may then look like this;\r
-\r
-\r
-     FOR angle = 1 TO 360\r
-          PLOT Xo + SINE(angle) * radius, Yo + COSINE(angle) * radius\r
-     NEXT angle\r
-\r
-This code has two major disadvantages;\r
-\r
-     1) It uses REAL numbers for the sine and cosine figures\r
-     2) It makes numerous calculations of sine and cosine values\r
-\r
-Both of these disadvantages result in a very slow piece of code. Since a\r
-circle is a regular figure with two axis of symmetry, one in both the X\r
-and Y axis, one only needs to calculate the relative offsets of points in\r
-one quadrant of the circle and then these offsets may be applied to the\r
-other three quadrants to produce a faster piece of code. Faster because\r
-the slow sine and cosine calculations are only done 90 times instead of\r
-360 times;\r
-\r
-\r
-     FOR angle = 1 TO 90\r
-          Xa = SINE(angle) * radius\r
-          Ya = COSINE(angle) * radius\r
-          PLOT Xo + Xa, Yo + Ya\r
-          PLOT Xo + Xa, Yo - Ya\r
-          PLOT Xo - Xa, Yo + Ya\r
-          PLOT Xo - Xa, Yo - Ya\r
-     NEXT angle\r
-\r
-A further enhancement may be made by making use of sine and cosine lookup\r
-tables instead of calculating them. This means calculating the sine and\r
-cosine values for each required angle and storing them in a table. Then,\r
-instead of calculating the values for each angle the circle drawing code\r
-need only retrieve the values from a table;\r
-\r
-\r
-     FOR angle = 1 TO 90\r
-          Xa = SINE[angle] * radius\r
-          Ya = COSINE[angle] * radius\r
-          PLOT Xo + Xa, Yo + Ya\r
-          PLOT Xo + Xa, Yo - Ya\r
-          PLOT Xo - Xa, Yo + Ya\r
-          PLOT Xo - Xa, Yo - Ya\r
-     NEXT angle\r
-\r
-\r
-Most computer languages work in RADIANS rather than DEGREES. There being\r
-approximately 57 degrees in one radian, 2 * PI radians in one circle.\r
-This implies that to calculate sine and cosine values of sufficient\r
-points to draw a reasonable circle using radians one must again use real\r
-numbers, that is numbers which have figures following a decimal point.\r
-Real number arithmetic, also known as floating point arithmetic, is\r
-horrendously slow to calculate. Integer arithmetic on the other hand is\r
-very quick to calculate, but less precise.\r
-\r
-To use integer arithmetic in circle drawing code requires ingenuity. If\r
-one agrees to use sine and cosine lookup tables for degrees, rather than\r
-radians. Then the sine value of an angle of 1 degree is;\r
-\r
-       0.0175\r
-\r
-Which, truncated to an integer becomes zero! To overcome this the sine\r
-and cosine values held in the table should be multiplied by some factor,\r
-say 10000. Then, the integer value of the sine of an angle of 1 degree\r
-becomes;\r
-\r
-       175\r
-\r
-If the sine and cosine values have been multiplied by a factor, then when\r
-the calculation of the point's offset is carried out one must remember to\r
-divide the result by the same factor. Thus the calculation becomes;\r
-\r
-          Xa = SINE[angle] * radius / factor\r
-          Ya = COSINE[angle] * radius / factor\r
-\r
-The final obstacle to drawing circles on a computer is the relationship\r
-between the width of the display screen and its height. This ratio\r
-between width and height is known as the "aspect ratio" and varies upon\r
-video display mode. The IBM VGA 256 colour mode for example can display\r
-320 pixels across and 200 up the screen. This equates to an aspect ratio\r
-of 1:1.3. If the circle drawing code ignores the aspect ratio, then the\r
-shape displayed will often be ovalar to a greater or lesser degree due to\r
-the rectangular shape of the display pixels. Thus in order to display a\r
-true circle, the formulae to calculate each point on the circumference\r
-must be amended to calculate a slight ellipse in compensation of the\r
-distorting factor of the display.\r
-\r
-The offset formulae then become;\r
-\r
-          Xa = SINE[angle] * radius / factor\r
-          Ya = COSINE[angle] * radius / (factor * aspect ratio)\r
-\r
-The following short C program illustrates a practical circle drawing code\r
-segment, in a demonstrable  form;\r
-\r
-\r
-     /* Circles.c   A demonstration circle drawing program for the IBM PC\r
-     */\r
-     \r
-     \r
-     #include <stdlib.h>\r
-     \r
-     int sintable[91] = {0,175,349,523,698,\r
-               872,1045,1219,1392,\r
-               1564,1736,1908,2079,\r
-               2250,2419,2588,2756,\r
-               2924,3090,3256,3420,\r
-               3584,3746,3907,4067,\r
-               4226,4384,4540,4695,\r
-               4848,5000,5150,5299,\r
-               5446,5592,5736,5878,\r
-               6018,6157,6293,6428,\r
-               6561,6691,6820,6947,\r
-               7071,7193,7314,7431,\r
-               7547,7660,7771,7880,\r
-               7986,8090,8192,8290,\r
-               8387,8480,8572,8660,\r
-               8746,8829,8910,8988,\r
-               9063,9135,9205,9272,\r
-               9336,9397,9455,9511,\r
-               9563,9613,9659,9703,\r
-               9744,9781,9816,9848,\r
-               9877,9903,9925,9945,\r
-               9962,9976,9986,9994,\r
-               9998,10000\r
-     };\r
-     \r
-     int costable[91] = { 10000,9998,9994,9986,9976,\r
-               9962,9945,9925,9903,\r
-               9877,9848,9816,9781,\r
-               9744,9703,9659,9613,\r
-               9563,9511,9455,9397,\r
-               9336,9272,9205,9135,\r
-               9063,8988,8910,8829,\r
-               8746,8660,8572,8480,\r
-               8387,8290,8192,8090,\r
-               7986,7880,7771,7660,\r
-               7547,7431,7314,7193,\r
-               7071,6947,6820,6691,\r
-               6561,6428,6293,6157,\r
-               6018,5878,5736,5592,\r
-               5446,5299,5150,5000,\r
-               4848,4695,4540,4384,\r
-               4226,4067,3907,3746,\r
-               3584,3420,3256,3090,\r
-               2924,2756,2588,2419,\r
-               2250,2079,1908,1736,\r
-               1564,1392,1219,1045,\r
-               872,698,523,349,\r
-               175,0\r
-     };\r
-     \r
-     void setvideo(unsigned char mode)\r
-     {\r
-          /* Sets the video display mode for an IBM PC */\r
-     \r
-          asm mov al , mode;\r
-          asm mov ah , 00;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void plot(int x, int y, unsigned char colour)\r
-     {\r
-          /* Code for IBM PC BIOS ROM */\r
-          /* Sets a pixel at the specified coordinates to a specified\r
-     colour */\r
-     \r
-          /* Return if out of range */\r
-          if (x < 0 || y < 0 || x > 320 || y > 200)\r
-               return;\r
-     \r
-          asm mov al , colour;\r
-          asm mov bh , 0;\r
-          asm mov cx , x;\r
-          asm mov dx , y;\r
-          asm mov ah, 0Ch;\r
-          asm int 10h;\r
-     }\r
-     \r
-     void Line(int a, int b, int c, int d, int col)\r
-     {\r
-          /* Draws a straight line from point a,b to point c,d in colour\r
-     col */\r
-     \r
-          int u;\r
-          int v;\r
-          int d1x;\r
-          int d1y;\r
-          int d2x;\r
-          int d2y;\r
-          int m;\r
-          int n;\r
-          double s; /* The only real number variable, but it's essential\r
-     */\r
-          int i;\r
-     \r
-          u = c - a;\r
-          v = d - b;\r
-          if (u == 0)\r
-          {\r
-               d1x = 0;\r
-               m = 0;\r
-          }\r
-          else\r
-          {\r
-               m = abs(u);\r
-               if (u < 0)\r
-                    d1x = -1;\r
-               else\r
-               if (u > 0)\r
-                    d1x = 1;\r
-          }\r
-     \r
-          if ( v == 0)\r
-          {\r
-               d1y = 0;\r
-               n = 0;\r
-          }\r
-          else\r
-          {\r
-               n = abs(v);\r
-               if (v < 0)\r
-                    d1y = -1;\r
-               else\r
-               if (v > 0)\r
-                    d1y = 1;\r
-          }\r
-          if (m > n)\r
-          {\r
-               d2x = d1x;\r
-               d2y = 0;\r
-          }\r
-          else\r
-          {\r
-               d2x = 0;\r
-               d2y = d1y;\r
-               m = n;\r
-               n = abs(u);\r
-          }\r
-          s = m / 2;\r
-     \r
-          for (i = 0; i <= m; i++)\r
-          {\r
-               plot(a,b,col);\r
-               s += n;\r
-               if (s >= m)\r
-               {\r
-                    s -= m;\r
-                    a += d1x;\r
-                    b += d1y;\r
-               }\r
-               else\r
-               {\r
-                    a += d2x;\r
-                    b += d2y;\r
-               }\r
-          }\r
-     }\r
-     \r
-     \r
-     void Circle(int x, int y, int rad, int col)\r
-     {\r
-          /* Draws a circle about origin x,y */\r
-          /* With a radius of rad */\r
-          /* The col parameter defines the colour for plotting */\r
-     \r
-          int f;\r
-          long xa;\r
-          long ya;\r
-          int a1;\r
-          int b1;\r
-          int a2;\r
-          int b2;\r
-          int a3;\r
-          int b3;\r
-          int a4;\r
-          int b4;\r
-     \r
-     \r
-          /* Calculate first point in each segment */\r
-     \r
-          a1 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
-          b1 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
-     \r
-          a2 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
-          b2 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
-     \r
-          a3 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
-          b3 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
-     \r
-          a4 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
-          b4 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
-     \r
-          /* Start loop at second point */\r
-          for(f = 1; f <= 90; f++)\r
-          {\r
-               /* Calculate offset to new point */\r
-               xa = ((long)(costable[f]) * (long)(rad) + 5000) / 10000;\r
-               ya = ((long)(sintable[f]) * (long)(rad) + 5000) / 13000;\r
-     \r
-               /* Draw a line from the previous point to the new point in\r
-                  each segment */\r
-               Line(a1,b1,x + xa, y + ya,col);\r
-               Line(a2,b2,x - xa, y + ya,col);\r
-               Line(a3,b3,x - xa, y - ya,col);\r
-               Line(a4,b4,x + xa, y - ya,col);\r
-     \r
-               /* Update the previous point in each segment */\r
-               a1 = x + xa;\r
-               b1 = y + ya;\r
-               a2 = x - xa;\r
-               b2 = y + ya;\r
-               a3 = x - xa;\r
-               b3 = y - ya;\r
-               a4 = x + xa;\r
-               b4 = y - ya;\r
-          }\r
-     }\r
-     \r
-     main()\r
-     {\r
-          int n;\r
-     \r
-          /* Select VGA 256 colour 320 x 200 video mode */\r
-          setvideo(19);\r
-     \r
-          /* Draw some circles */\r
-          for(n = 0; n < 100; n++)\r
-               Circle(160,100,n,n + 20);\r
-     }\r
-     \r
-     \r
-\r
-Vesa Mode\r
-\r
-The VESA BIOS provides a number of new, and exciting video modes not\r
-supported by the standard BIOS. These modes vary from one video card to\r
-another, but most support the following modes:\r
-\r
-Mode               Display\r
-                   \r
-0x54               Text 16 colours 132 x 43\r
-0x55               Text 16 colours 132 x 25\r
-0x58               Graphics 16 colours 800 x 600\r
-0x5C               Graphics 256 colours 800 x 600\r
-0x5D               Graphics 16 colours 1024 x 768\r
-0x5F               Graphics 256 colours 640 x 480\r
-0x60               Graphics 256 colours 1024 x 768\r
-0x64               Graphics 64k colours 640 x 480\r
-0x65               Graphics 64k colours 800 x 600\r
-0x6A               Graphics 16 colours 800 x 600\r
-0x6C               Graphics 16 colours 1280 x 1024\r
-0x70               Graphics 16m colours 320 x 200\r
-0x71               Graphics 16m colours 640 x 480\r
-\r
-These modes are in addition to the standard BIOS video modes described\r
-earlier.\r
-\r
-Setting a VESA video mode requires a call to a different BIOS function\r
-than the standard BIOS, as illustrated in the following example which\r
-enables any VESA or standard display mode to be selected from the DOS\r
-command line.\r
-\r
-     #include <dos.h>\r
-     #include <ctype.h>\r
-     \r
-     void setvideo(int mode)\r
-     {\r
-          /* Sets the video display to a VESA or normal mode and clears\r
-     the screen */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 0x4f;\r
-          inreg.h.al = 0x02;\r
-          inreg.x.bx = mode;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     main(int argc, char *argv[])\r
-     {\r
-          setvideo(atoi(argv[1]));\r
-     }\r
-\r
-\r
-Plotting pixels in a VESA mode graphics display can be acgieved with the\r
-standard BIOS plot functiona call, as illustrated here;\r
-\r
-     \r
-     void plot(int x, int y, unsigned char colour)\r
-     {\r
-          asm mov al , colour;\r
-          asm mov bh , 00;\r
-          asm mov cx , x;\r
-          asm mov dx , y;\r
-          asm mov ah , 0Ch;\r
-          asm int 10h;\r
-     }\r
-\r
-\r
-Or, in a 800 x 600 resolution mode you can use this fast direct video\r
-access plot function;\r
-\r
-     void plot( int x, int y, unsigned char pcolor)\r
-     {\r
-          /*\r
-               Fast 800 x 600 mode (0x58 or 0x102) plotting\r
-          */\r
-     \r
-          asm mov   ax,y;\r
-          asm mov   dx,800/8;\r
-          asm mul   dx;\r
-          asm mov   bx,x;\r
-          asm mov   cl,bl;\r
-     \r
-          asm shr   bx,1;\r
-          asm shr   bx,1;\r
-          asm shr   bx,1;\r
-          asm add   bx,ax;\r
-     \r
-          asm and   cl,7;\r
-          asm xor   cl,7;\r
-          asm mov   ah,1;\r
-          asm shl   ah,cl;\r
-     \r
-          asm mov   dx,03CEh;\r
-          asm mov   al,8;\r
-          asm out   dx,ax;\r
-     \r
-          asm mov   ax,(02h shl 8) + 5;\r
-          asm out   dx,ax;\r
-     \r
-          asm mov   ax,0A000h;\r
-          asm mov   es,ax;\r
-     \r
-          asm mov   al,es:[bx];\r
-          asm mov   al,byte ptr pcolor;\r
-          asm mov   es:[bx],al;\r
-     \r
-          asm mov   ax,(0FFh shl 8 ) + 8;\r
-          asm out   dx,ax;\r
-     \r
-          asm mov   ax,(00h shl 8) + 5;\r
-          asm out   dx,ax;\r
-     }\r
-\r
-There are lots more functions supported by the VESA BIOS, but this will\r
-get you going with the basic operations. Remember though that when using\r
-VESA display modes, that the direct console I/O functions in the C\r
-compiler library may not function correctly.\r
-                                    \r
-               DIRECTORY SEARCHING WITH THE IBM PC AND DOS\r
-\r
-Amongst the many functions provided by DOS for programmers, are a pair of\r
-functions; "Find first" and "Find next" which are used to search a DOS\r
-directory for a specified file name or names. The first function, "Find\r
-first" is accessed via DOS interrupt 21, function 4E. It takes an ascii\r
-string file specification, which can include wildcards, and the required\r
-attribute for files to match. Upon return the function fills the disk\r
-transfer area (DTA) with details of the located file and returns with the\r
-carry flag clear. If an error occurs, such as no matching files are\r
-located, the function returns with the carry flag set.\r
-\r
-Following a successful call to "Find first", a program can call "Find\r
-next", DOS interrupt 21, function 4F, to locate the next file matching\r
-the specifications provided by the initial call to "Find first". If this\r
-function succeeds, then the DTA is filled in with details of the next\r
-matching file, and the function returns with the carry flag clear.\r
-Otherwise a return is made with the carry flag set.\r
-\r
-Most C compilers for the IBM PC provide non standard library functions\r
-for accessing these two functions. Turbo C provides "findfirst()" and\r
-"findnext()". Making use of the supplied library functions shields the\r
-programmer from the messy task of worrying about the DTA. Microsoft C\r
-programmers should substitue findfirst() with _dos_findfirst() and\r
-findnext() with _dos_findnext().\r
-\r
-The following Turbo C example imitates the DOS directory command, in a\r
-basic form;\r
-\r
-     #include <stdio.h>\r
-     #include <dir.h>\r
-     #include <dos.h>\r
-     \r
-     void main(void)\r
-     {\r
-          /* Display directory listing of current directory */\r
-     \r
-          int done;\r
-          int day;\r
-          int month;\r
-          int year;\r
-          int hour;\r
-          int min;\r
-          char amflag;\r
-          struct ffblk ffblk;\r
-          struct fcb fcb;\r
-     \r
-          /* First display sub directory entries */\r
-          done = findfirst("*.",&ffblk,16);\r
-     \r
-          while (!done)\r
-          {\r
-               year = (ffblk.ff_fdate >> 9) + 80;\r
-               month = (ffblk.ff_fdate >> 5) & 0x0f;\r
-               day = ffblk.ff_fdate & 0x1f;\r
-               hour = (ffblk.ff_ftime >> 11);\r
-               min = (ffblk.ff_ftime >> 5) & 63;\r
-     \r
-               amflag = 'a';\r
-     \r
-               if (hour > 12)\r
-               {\r
-                    hour -= 12;\r
-                    amflag = 'p';\r
-               }\r
-     \r
-               printf("%-11.11s  <DIR>   %02d-%02d-%02d  %2d:%02d%c\n",\r
-                       ffblk.ff_name,day,month,year,hour,min,amflag);\r
-               done = findnext(&ffblk);\r
-          }\r
-     \r
-          /* Now all files except directories */\r
-          done = findfirst("*.*",&ffblk,231);\r
-     \r
-          while (!done)\r
-          {\r
-               year = (ffblk.ff_fdate >> 9) + 80;\r
-               month = (ffblk.ff_fdate >> 5) & 0x0f;\r
-               day = ffblk.ff_fdate & 0x1f;\r
-               hour = (ffblk.ff_ftime >> 11);\r
-               min = (ffblk.ff_ftime >> 5) & 63;\r
-     \r
-               amflag = 'a';\r
-     \r
-               if (hour > 12)\r
-               {\r
-                    hour -= 12;\r
-                    amflag = 'p';\r
-               }\r
-     \r
-               parsfnm(ffblk.ff_name,&fcb,1);\r
-     \r
-               printf("%-8.8s %-3.3s %8ld  %02d-%02d-%02d  %2d:%02d%c\n",\r
-                         fcb.fcb_name,fcb.fcb_ext,ffblk.ff_fsize,\r
-                         day,month,year,hour,min,amflag);\r
-               done = findnext(&ffblk);\r
-          }\r
-     }\r
-     \r
-\r
-The function "parsfnm()" is a Turbo C library command which makes use of\r
-the DOS function for parsing an ascii string containing a file name, into\r
-its component parts. These component parts are then put into a DOS file\r
-control block (fcb), from where they may be easily retrieved for\r
-displaying by printf().\r
-\r
-\r
-The DOS DTA is comprised as follows;\r
-\r
-Offset                   Length                   Contents\r
-                                                  \r
-00                       15                       Reserved\r
-15                       Byte                     Attribute of matched\r
-                                                  file\r
-16                       Word                     File time\r
-18                       Word                     File date\r
-1A                       04                       File size\r
-1E                       0D                       File name and extension\r
-                                                  as ascii string\r
-\r
-The file time word contains the time at which the file was last written\r
-to disc and is comprised as follows;\r
-\r
-Bits      Contents\r
-\r
- 0 -  4             Seconds divided by 2\r
- 5 - 10        Minutes\r
-11 - 15   Hours\r
-\r
-The file date word holds the date on which the file was last written to\r
-disc and is comprised of;\r
-\r
-Bits      Contents\r
-\r
- 0 -  4             Day\r
- 5 -  8             Month\r
- 9 - 15        Years since 1980\r
-\r
-To extract these details from the DTA requires a little manipulation, as\r
-illustrated in the above example.\r
-\r
-The DTA attribute flag is comprised of the following bits being set or\r
-not;\r
-\r
-Bit       Attribute\r
-\r
-0         Read only\r
-1         Hidden\r
-2         System\r
-3         Volume label\r
-4         Directory\r
-5         Archive\r
-                                    \r
-                        ACCESSING EXPANDED MEMORY\r
-\r
-Memory (RAM) in an IBM PC comes in three flavours; Conventional, Expanded\r
-and Extended. Conventional memory is the 640K of RAM which the operating\r
-system DOS can access. This memory is normally used. However, it is often\r
-insufficient for todays RAM hungry systems. Expanded memory is RAM which\r
-is addressed outside of the area of conventional RAM not by DOS but by a\r
-second program called a LIM EMS driver. Access to this device driver is\r
-made through interrupt 67h.\r
-\r
-The main problem with accessing expanded memory is that no matter how\r
-much expanded memory is fitted to the computer, it can only be accessed\r
-through 16K blocks refered to as pages. So for example. If you have 2mB\r
-of expanded RAM fitted to your PC then that is comprised of 128 pages\r
-(128 * 16K = 2mB).\r
-\r
-A program can determine whether a LIM EMS driver is installed by\r
-attempting to open the file `EMMXXXX0' which is guarranteed by the LIM\r
-standard to be present as an IOCTL device when the device driver is\r
-active.\r
-\r
-The following source code illustrates some basic functions for testing\r
-for and accessing expanded memory.\r
-\r
-     /*\r
-     Various functions for using Expanded memory\r
-     */\r
-     \r
-     #include <dos.h>\r
-     #define   EMM  0x67\r
-     \r
-     char far *emmbase;\r
-     emmtest()\r
-     {\r
-          /*\r
-          Tests for the presence of expnaded memory by attempting to\r
-          open the file EMMXXXX0.\r
-          */\r
-     \r
-          union REGS regs;\r
-          struct SREGS sregs;\r
-          int error;\r
-          long handle;\r
-     \r
-          /* Attempt to open the file device EMMXXXX0 */\r
-          regs.x.ax = 0x3d00;\r
-          regs.x.dx = (int)"EMMXXXX0";\r
-          sregs.ds = _DS;\r
-          intdosx(&regs,&regs,&sregs);\r
-          handle = regs.x.ax;\r
-          error = regs.x.cflag;\r
-     \r
-          if (!error)\r
-          {\r
-               regs.h.ah = 0x3e;\r
-               regs.x.bx = handle;\r
-               intdos(&regs,&regs);\r
-          }\r
-          return error;\r
-     }\r
-     \r
-     emmok()\r
-     {\r
-          /*\r
-          Checks whether the expanded memory manager responds correctly\r
-          */\r
-     \r
-          union REGS regs;\r
-     \r
-          regs.h.ah = 0x40;\r
-          int86(EMM,&regs,&regs);\r
-     \r
-          if (regs.h.ah)\r
-               return 0;\r
-     \r
-          regs.h.ah = 0x41;\r
-          int86(EMM,&regs,&regs);\r
-     \r
-          if (regs.h.ah)\r
-               return 0;\r
-     \r
-          emmbase = MK_FP(regs.x.bx,0);\r
-          return 1;\r
-     }\r
-     \r
-     long emmavail()\r
-     {\r
-        /*\r
-        Returns the number of available (free) 16K pages of expanded\r
-     memory\r
-        or -1 if an error occurs.\r
-        */\r
-     \r
-             union REGS regs;\r
-     \r
-          regs.h.ah = 0x42;\r
-          int86(EMM,&regs,&regs);\r
-          if (!regs.h.ah)\r
-               return regs.x.bx;\r
-          return -1;\r
-     }\r
-     \r
-     long emmalloc(int n)\r
-     {\r
-          /*\r
-          Requests 'n' pages of expanded memory and returns the file\r
-     handle\r
-          assigned to the pages or -1 if there is an error\r
-          */\r
-     \r
-          union REGS regs;\r
-     \r
-          regs.h.ah = 0x43;\r
-          regs.x.bx = n;\r
-          int86(EMM,&regs,&regs);\r
-          if (regs.h.ah)\r
-               return -1;\r
-          return regs.x.dx;\r
-     }\r
-     \r
-     emmmap(long handle, int phys, int page)\r
-     {\r
-          /*\r
-          Maps a physical page from expanded memory into the page frame\r
-     in the\r
-          conventional memory 16K window so that data can be transfered\r
-     between\r
-          the expanded memory and conventional memory.\r
-          */\r
-     \r
-          union REGS regs;\r
-     \r
-          regs.h.ah = 0x44;\r
-          regs.h.al = page;\r
-          regs.x.bx = phys;\r
-          regs.x.dx = handle;\r
-          int86(EMM,&regs,&regs);\r
-          return (regs.h.ah == 0);\r
-     }\r
-     \r
-     void emmmove(int page, char *str, int n)\r
-     {\r
-          /*\r
-          Move 'n' bytes from conventional memory to the specified\r
-     expanded memory\r
-          page\r
-          */\r
-     \r
-          char far *ptr;\r
-     \r
-          ptr = emmbase + page * 16384;\r
-          while(n-- > 0)\r
-               *ptr++ = *str++;\r
-     }\r
-     \r
-     void emmget(int page, char *str, int n)\r
-     {\r
-          /*\r
-          Move 'n' bytes from the specified expanded memory page into\r
-     conventional\r
-          memory\r
-          */\r
-     \r
-          char far *ptr;\r
-     \r
-          ptr = emmbase + page * 16384;\r
-          while(n-- > 0)\r
-               *str++ = *ptr++;\r
-     }\r
-     \r
-     emmclose(long handle)\r
-     {\r
-          /*\r
-          Release control of the expanded memory pages allocated to\r
-     'handle'\r
-          */\r
-     \r
-          union REGS regs;\r
-     \r
-          regs.h.ah = 0x45;\r
-          regs.x.dx = handle;\r
-          int86(EMM,&regs,&regs);\r
-          return (regs.h.ah == 0);\r
-     }\r
-     \r
-     /*\r
-     Test function for the EMM routines\r
-     */\r
-     \r
-     void main()\r
-     {\r
-          long emmhandle;\r
-          long avail;\r
-          char teststr[80];\r
-          int i;\r
-     \r
-          if(!emmtest())\r
-          {\r
-               printf("Expanded memory is not present\n");\r
-               exit(0);\r
-          }\r
-     \r
-          if(!emmok())\r
-          {\r
-               printf("Expanded memory manager is not present\n");\r
-               exit(0);\r
-          }\r
-     \r
-          avail = emmavail();\r
-          if (avail == -1)\r
-          {\r
-               printf("Expanded memory manager error\n");\r
-               exit(0);\r
-          }\r
-          printf("There are %ld pages available\n",avail);\r
-     \r
-          /* Request 10 pages of expanded memory */\r
-          if((emmhandle = emmalloc(10)) < 0)\r
-          {\r
-               printf("Insufficient pages available\n");\r
-               exit(0);\r
-          }\r
-     \r
-          for (i = 0; i < 10; i++)\r
-          {\r
-               sprintf(teststr,"%02d This is a test string\n",i);\r
-               emmmap(emmhandle,i,0);\r
-               emmmove(0,teststr,strlen(teststr) + 1);\r
-          }\r
-     \r
-          for (i = 0; i < 10; i++)\r
-          {\r
-               emmmap(emmhandle,i,0);\r
-               emmget(0,teststr,strlen(teststr) + 1);\r
-               printf("READING BLOCK %d: %s\n",i,teststr);\r
-          }\r
-     \r
-          emmclose(emmhandle);\r
-     }\r
-                                    \r
-                        ACCESSING EXTENDED MEMORY\r
-\r
-\r
-Extended memory has all but taken over from Expanded Memory now (1996).\r
-It is faster and more useable than expanded memory. As with Expanded\r
-memory, Extended memory cannot be directly accessed through the standard\r
-DOS mode, and so a transfer buffer in conventional or "real-mode" memory\r
-needs to be used. The process to write data to Extended memory then\r
-involves copying the data to the transfer buffer in conventional memory,\r
-and from there copying it to Extended memory.\r
-\r
-Before any use may be made of Extended memory, a program should test to\r
-see if Extended memory is available. The following function, XMS_init(),\r
-tests for the presence of Extended memory, and if available calls another\r
-function, GetXMSEntry()  to initialise the program for using Extended\r
-Memory. The function also allocates a conventional memory transfer\r
-buffer.\r
-\r
-\r
-     \r
-     /*\r
-          BLOCKSIZE will be the size of our real-memory buffer that\r
-          we'll swap XMS through (must be a multiple of 1024, since\r
-          XMS is allocated in 1K chunks.)\r
-     */\r
-     \r
-     #ifdef __SMALL__\r
-     #define BLOCKSIZE (16L * 1024L)\r
-     #endif\r
-     \r
-     \r
-     #ifdef __MEDIUM__\r
-     #define BLOCKSIZE (16L * 1024L)\r
-     #endif\r
-     \r
-     \r
-     #ifdef __COMPACT__\r
-     #define BLOCKSIZE (64L * 1024L)\r
-     #endif\r
-     \r
-     #ifdef __LARGE__\r
-     #define BLOCKSIZE (64L * 1024L)\r
-     #endif\r
-     \r
-     \r
-     char XMS_init()\r
-     {\r
-          /*\r
-               returns 0 if XMS present,\r
-                    1 if XMS absent\r
-                    2 if unable to allocate conventional memory transfer\r
-     buffer\r
-          */\r
-          unsigned char status;\r
-          _AX=0x4300;\r
-          geninterrupt(0x2F);\r
-          status = _AL;\r
-          if(status==0x80)\r
-          {\r
-               GetXMSEntry();\r
-               XMSBuf = (char far *) farmalloc(BLOCKSIZE);\r
-               if (XMSBuf == NULL)\r
-                    return 2;\r
-               return 0;\r
-          }\r
-          return 1;\r
-     }\r
-     \r
-     void GetXMSEntry(void)\r
-     {\r
-          /*\r
-               GetXMSEntry sets XMSFunc to the XMS Manager entry point\r
-               so we can call it later\r
-          */\r
-     \r
-          _AX=0x4310;\r
-          geninterrupt(0x2F);\r
-          XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);\r
-     }\r
-\r
-\r
-Once the presence of Extended memory has been confirmed, a program can\r
-find out how much Extended memory is available;\r
-\r
-     void XMSSize(int *kbAvail, int *largestAvail)\r
-     {\r
-          /*\r
-               XMSSize returns the total kilobytes available, and the\r
-     size\r
-               in kilobytes of the largest available block\r
-          */\r
-     \r
-          _AH=8;\r
-          (*XMSFunc)();\r
-          *largestAvail=_DX;\r
-          *kbAvail=_AX;\r
-     }\r
-\r
-\r
-The following function may be called to allocate a block of Extended\r
-memory, like you would allocate a block of conventional memory.\r
-\r
-     char AllocXMS(unsigned long numberBytes)\r
-     {\r
-          /*\r
-               Allocate a block of XMS memory numberBytes long\r
-               Returns 1 on success\r
-                    0 on failure\r
-          */\r
-     \r
-          _DX = (int)(numberBytes / 1024);\r
-          _AH = 9;\r
-          (*XMSFunc)();\r
-          if (_AX==0)\r
-          {\r
-               return 0;\r
-          }\r
-          XMSHandle=_DX;\r
-          return 1;\r
-     }\r
-     \r
-\r
-Allocated Extended memory is not freed by DOS. A program using Extended\r
-memory must release it before terminating. This function frees a block of\r
-extended memory previously allocated by AllocXMS. Note, XMSHandle is a\r
-global variable of type int.\r
-\r
-     void XMS_free(void)\r
-     {\r
-          /*\r
-               Free used XMS\r
-          */\r
-          _DX=XMSHandle;\r
-          _AH=0x0A;\r
-          (*XMSFunc)();\r
-     }\r
-\r
-\r
-Two functions are now given. One for writing data to Extended memory, and\r
-one for reading data from Extended memory into conventional memory.\r
-\r
-     /*\r
-          XMSParms is a structure for copying information to and from\r
-          real-mode memory to XMS memory\r
-     */\r
-     \r
-     struct parmstruct\r
-     {\r
-          /*\r
-               blocklength is the size in bytes of block to copy\r
-          */\r
-          unsigned long blockLength;\r
-     \r
-          /*\r
-               sourceHandle is the XMS handle of source; 0 means that\r
-               sourcePtr will be a 16:16 real-mode pointer, otherwise\r
-               sourcePtr is a 32-bit offset from the beginning of the\r
-               XMS area that sourceHandle points to\r
-          */\r
-     \r
-          unsigned int sourceHandle;\r
-          far void *sourcePtr;\r
-     \r
-          /*\r
-               destHandle is the XMS handle of destination; 0 means that\r
-               destPtr will be a 16:16 real-mode pointer, otherwise\r
-               destPtr is a 32-bit offset from the beginning of the XMS\r
-               area that destHandle points to\r
-          */\r
-     \r
-          unsigned int destHandle;\r
-          far void *destPtr;\r
-     }\r
-     XMSParms;\r
-     \r
-     \r
-     char XMS_write(unsigned long loc, char far *val, unsigned length)\r
-     {\r
-          /*\r
-               Round length up to next even value\r
-          */\r
-          length += length % 2;\r
-     \r
-          XMSParms.sourceHandle=0;\r
-          XMSParms.sourcePtr=val;\r
-          XMSParms.destHandle=XMSHandle;\r
-          XMSParms.destPtr=(void far *) (loc);\r
-          XMSParms.blockLength=length;  /* Must be an even number! */\r
-          _SI = FP_OFF(&XMSParms);\r
-          _AH=0x0B;\r
-          (*XMSFunc)();\r
-          if (_AX==0)\r
-          {\r
-               return 0;\r
-          }\r
-          return 1;\r
-     }\r
-     \r
-     \r
-     void *XMS_read(unsigned long loc,unsigned length)\r
-     {\r
-          /*\r
-               Returns pointer to data\r
-               or NULL on error\r
-          */\r
-     \r
-          /*\r
-               Round length up to next even value\r
-          */\r
-          length += length % 2;\r
-     \r
-          XMSParms.sourceHandle=XMSHandle;\r
-          XMSParms.sourcePtr=(void far *) (loc);\r
-          XMSParms.destHandle=0;\r
-          XMSParms.destPtr=XMSBuf;\r
-          XMSParms.blockLength=length;       /* Must be an even number */\r
-          _SI=FP_OFF(&XMSParms);\r
-          _AH=0x0B;\r
-          (*XMSFunc)();\r
-          if (_AX==0)\r
-          {\r
-               return NULL;\r
-          }\r
-          return XMSBuf;\r
-     }\r
-     \r
-     \r
-And now putting it all together is a demonstration program.\r
-\r
-     /* A sequential table of variable length records in XMS */\r
-     \r
-     #include <dos.h>\r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     #include <alloc.h>\r
-     #include <string.h>\r
-     \r
-     #define TRUE 1\r
-     #define FALSE 0\r
-     \r
-     /*\r
-          BLOCKSIZE will be the size of our real-memory buffer that\r
-          we'll swap XMS through (must be a multiple of 1024, since\r
-          XMS is allocated in 1K chunks.)\r
-     */\r
-     \r
-     #ifdef __SMALL__\r
-     #define BLOCKSIZE (16L * 1024L)\r
-     #endif\r
-     \r
-     \r
-     #ifdef __MEDIUM__\r
-     #define BLOCKSIZE (16L * 1024L)\r
-     #endif\r
-     \r
-     \r
-     #ifdef __COMPACT__\r
-     #define BLOCKSIZE (64L * 1024L)\r
-     #endif\r
-     \r
-     #ifdef __LARGE__\r
-     #define BLOCKSIZE (64L * 1024L)\r
-     #endif\r
-     \r
-     \r
-     /*\r
-          XMSParms is a structure for copying information to and from\r
-          real-mode memory to XMS memory\r
-     */\r
-     \r
-     struct parmstruct\r
-     {\r
-          /*\r
-               blocklength is the size in bytes of block to copy\r
-          */\r
-          unsigned long blockLength;\r
-     \r
-          /*\r
-               sourceHandle is the XMS handle of source; 0 means that\r
-               sourcePtr will be a 16:16 real-mode pointer, otherwise\r
-               sourcePtr is a 32-bit offset from the beginning of the\r
-               XMS area that sourceHandle points to\r
-          */\r
-     \r
-          unsigned int sourceHandle;\r
-          far void *sourcePtr;\r
-     \r
-          /*\r
-               destHandle is the XMS handle of destination; 0 means that\r
-               destPtr will be a 16:16 real-mode pointer, otherwise\r
-               destPtr is a 32-bit offset from the beginning of the XMS\r
-               area that destHandle points to\r
-          */\r
-     \r
-          unsigned int destHandle;\r
-          far void *destPtr;\r
-     }\r
-     XMSParms;\r
-     \r
-     void far (*XMSFunc) (void);   /* Used to call XMS manager\r
-     (himem.sys) */\r
-     char GetBuf(void);\r
-     void GetXMSEntry(void);\r
-     \r
-     char *XMSBuf;  /* Conventional memory buffer for transfers */\r
-     \r
-     unsigned int XMSHandle;  /* handle to allocated XMS block */\r
-     \r
-     \r
-     char XMS_init()\r
-     {\r
-          /*\r
-               returns 0 if XMS present,\r
-                    1 if XMS absent\r
-                    2 if unable to allocate transfer buffer\r
-          */\r
-          unsigned char status;\r
-          _AX=0x4300;\r
-          geninterrupt(0x2F);\r
-          status = _AL;\r
-          if(status==0x80)\r
-          {\r
-               GetXMSEntry();\r
-               XMSBuf = (char far *) farmalloc(BLOCKSIZE);\r
-               if (XMSBuf == NULL)\r
-                    return 2;\r
-               return 0;\r
-          }\r
-          return 1;\r
-     }\r
-     \r
-     void GetXMSEntry(void)\r
-     {\r
-          /*\r
-               GetXMSEntry sets XMSFunc to the XMS Manager entry point\r
-               so we can call it later\r
-          */\r
-     \r
-          _AX=0x4310;\r
-          geninterrupt(0x2F);\r
-          XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);\r
-     }\r
-     \r
-     \r
-     void XMSSize(int *kbAvail, int *largestAvail)\r
-     {\r
-          /*\r
-               XMSSize returns the total kilobytes available, and the\r
-     size\r
-               in kilobytes of the largest available block\r
-          */\r
-     \r
-          _AH=8;\r
-          (*XMSFunc)();\r
-          *largestAvail=_DX;\r
-          *kbAvail=_AX;\r
-     }\r
-     \r
-     char AllocXMS(unsigned long numberBytes)\r
-     {\r
-          /*\r
-               Allocate a block of XMS memory numberBytes long\r
-          */\r
-     \r
-          _DX = (int)(numberBytes / 1024);\r
-          _AH = 9;\r
-          (*XMSFunc)();\r
-          if (_AX==0)\r
-          {\r
-               return FALSE;\r
-          }\r
-          XMSHandle=_DX;\r
-          return TRUE;\r
-     }\r
-     \r
-     void XMS_free(void)\r
-     {\r
-          /*\r
-               Free used XMS\r
-          */\r
-          _DX=XMSHandle;\r
-          _AH=0x0A;\r
-          (*XMSFunc)();\r
-     }\r
-     \r
-     char XMS_write(unsigned long loc, char far *val, unsigned length)\r
-     {\r
-          /*\r
-               Round length up to next even value\r
-          */\r
-          length += length % 2;\r
-     \r
-          XMSParms.sourceHandle=0;\r
-          XMSParms.sourcePtr=val;\r
-          XMSParms.destHandle=XMSHandle;\r
-          XMSParms.destPtr=(void far *) (loc);\r
-          XMSParms.blockLength=length;  /* Must be an even number! */\r
-          _SI = FP_OFF(&XMSParms);\r
-          _AH=0x0B;\r
-          (*XMSFunc)();\r
-          if (_AX==0)\r
-          {\r
-               return FALSE;\r
-          }\r
-          return TRUE;\r
-     }\r
-     \r
-     \r
-     void *XMS_read(unsigned long loc,unsigned length)\r
-     {\r
-          /*\r
-               Returns pointer to data\r
-               or NULL on error\r
-          */\r
-     \r
-          /*\r
-               Round length up to next even value\r
-          */\r
-          length += length % 2;\r
-     \r
-          XMSParms.sourceHandle=XMSHandle;\r
-          XMSParms.sourcePtr=(void far *) (loc);\r
-          XMSParms.destHandle=0;\r
-          XMSParms.destPtr=XMSBuf;\r
-          XMSParms.blockLength=length;  /* Must be an even number */\r
-          _SI=FP_OFF(&XMSParms);\r
-          _AH=0x0B;\r
-          (*XMSFunc)();\r
-          if (_AX==0)\r
-          {\r
-               return NULL;\r
-          }\r
-          return XMSBuf;\r
-     }\r
-     \r
-     \r
-     /*\r
-          Demonstration code\r
-          Read various length strings into a single XMS block (EMB)\r
-          and write them out again\r
-     */\r
-     \r
-     int main()\r
-     {\r
-          int kbAvail,largestAvail;\r
-          char buffer[80];\r
-          char *p;\r
-          long pos;\r
-          long end;\r
-     \r
-          if (XMS_init() == 0)\r
-               printf("XMS Available ...\n");\r
-          else\r
-          {\r
-               printf("XMS Not Available\n");\r
-               return(1);\r
-          }\r
-     \r
-          XMSSize(&kbAvail,&largestAvail);\r
-          printf("Kilobytes Available: %d; Largest block:\r
-     %dK\n",kbAvail,largestAvail);\r
-     \r
-          if (!AllocXMS(2000 * 1024L))\r
-               return(1);\r
-     \r
-     \r
-          pos = 0;\r
-     \r
-          do\r
-          {\r
-               p = fgets(buffer,1000,stdin);\r
-               if (p != NULL)\r
-               {\r
-                    XMS_write(pos,buffer,strlen(buffer) + 1);\r
-                    pos += strlen(buffer) + 1;\r
-               }\r
-          }\r
-          while(p != NULL);\r
-     \r
-          end = pos;\r
-     \r
-          pos = 0;\r
-     \r
-          do\r
-          {\r
-               memcpy(buffer,XMS_read(pos,100),70);\r
-               printf("%s",buffer);\r
-               pos += strlen(buffer) + 1;\r
-          }\r
-          while(pos < end);\r
-     \r
-          /*\r
-               It is VERY important to free any XMS before exiting!\r
-          */\r
-          XMS_free();\r
-          return 0;\r
-     }\r
-\r
-                                    \r
-                                    \r
-                                    \r
-                             TSR PROGRAMMING\r
-\r
-\r
-Programs which remain running and resident in memory while other programs\r
-are running are the most exciting line of programming for many PC\r
-developers. This type of program is known as a "Terminate and Stay\r
-Resident" or "TSR" program and they are very difficult to program\r
-sucessfuly.\r
-\r
-The difficulties in programming TSRs comes from the limitations of DOS\r
-which is not a multi-tasking operating system, and does not react well to\r
-re-enterant code. That is it's own functions (interrupts) calling\r
-themselves.\r
-\r
-In theory a TSR is quite simple. It is an ordinary program which\r
-terminates not through the usual DOS terminate function, but through the\r
-DOS "keep" function - interrupt 27h. This function reserves an area of\r
-memory, used by the program so that no other programs will overwrite it.\r
-This in itself is not a very difficult task, excepting that the program\r
-needs to tell DOS how much memory to leave it!\r
-\r
-The problems stem mainly from not being able to use DOS function calls\r
-within the TSR program once it has "gone resident".\r
-\r
-There are a few basic rules which help to clarify the problems\r
-encountered in programming TSRs:\r
-\r
-  1.Avoid DOS function calls\r
-  2.Monitor the DOS busy flag, when this flag is nonzero, DOS is\r
-     executing an interrupt 21h function and MUST NOT be disturbed!\r
-  3.Monitor interrupt 28h. This reveals when DOS is busy waiting for\r
-     console input. At this time you can disturb DOS regardless of the\r
-     DOS busy flag setting.\r
-  4.Provide some way of checking whether the TSR is already loaded to\r
-     prevent multiple copies occuring in memory.\r
-  5.Remember that other TSR programs may be chained to interrupts, and\r
-     so you must chain any interrupt vectors that your program needs.\r
-  6.Your TSR program must use its own stack, and NOT that of the running\r
-     process.\r
-  7.TSR programs must be compiled in a small memory model with stack\r
-     checking turned off.\r
-  8.When control passes to your TSR program, it must tell DOS that the\r
-     active process has changed.\r
-\r
-\r
-The following three source code modules describe a complete TSR program.\r
-This is a useful pop-up address book database which can be activated\r
-while any other program is running by pressing the key combination `Alt'\r
-and `.'.  If the address book does not respond to the key press, it is\r
-probably because DOS cannot be disturbed, and you should try to pop-it-up\r
-again.\r
-\r
-\r
-     /*\r
-        A practical TSR program (a pop-up address book database)\r
-        Compile in small memory model with stack checking OFF\r
-     */\r
-     \r
-     #include <dos.h>\r
-     #include <stdio.h>\r
-     #include <string.h>\r
-     #include <dir.h>\r
-     \r
-     static union REGS rg;\r
-     \r
-     /*\r
-        Size of the program to remain resident\r
-        experimentation is required to make this as small as possible\r
-     */\r
-     unsigned sizeprogram = 28000/16;\r
-     \r
-     /* Activate with Alt . */\r
-     unsigned scancode = 52;  /* . */\r
-     unsigned keymask = 8;         /* ALT */\r
-     \r
-     char signature[]= "POPADDR";\r
-     char fpath[40];\r
-     \r
-     /*\r
-          Function prototypes\r
-     */\r
-     \r
-     void curr_cursor(int *x, int *y);\r
-     int resident(char *, void interrupt(*)());\r
-     void resinit(void);\r
-     void terminate(void);\r
-     void restart(void);\r
-     void wait(void);\r
-     void resident_psp(void);\r
-     void exec(void);\r
-     \r
-     /*\r
-        Entry point from DOS\r
-     */\r
-     \r
-     main(int argc, char *argv[])\r
-     {\r
-          void interrupt ifunc();\r
-          int ivec;\r
-     \r
-          /*\r
-             For simplicity, assume the data file is in the root\r
-     directory\r
-             of drive C:\r
-          */\r
-          strcpy(fpath,"C:\\ADDRESS.DAT");\r
-     \r
-          if ((ivec = resident(signature,ifunc)) != 0)\r
-          {\r
-               /* TSR is resident */\r
-               if (argc > 1)\r
-               {\r
-                    rg.x.ax = 0;\r
-                    if (strcmp(argv[1],"quit") == 0)\r
-                         rg.x.ax = 1;\r
-                    else if (strcmp(argv[1],"restart") == 0)\r
-                         rg.x.ax = 2;\r
-                    else if (strcmp(argv[1],"wait") == 0)\r
-                         rg.x.ax = 3;\r
-                    if (rg.x.ax)\r
-                    {\r
-                         int86(ivec,&rg,&rg);\r
-                         return;\r
-                    }\r
-               }\r
-               printf("\nPopup Address Book is already resident");\r
-          }\r
-          else\r
-          {\r
-               /* Initial load of TSR program */\r
-               printf("Popup Address Book Resident.\nPress Alt . To\r
-     Activate....\n");\r
-               resinit();\r
-          }\r
-     }\r
-     \r
-     void interrupt ifunc(bp,di,si,ds,es,dx,cx,bx,ax)\r
-     {\r
-          if(ax == 1)\r
-               terminate();\r
-          else if(ax == 2)\r
-               restart();\r
-          else if(ax == 3)\r
-               wait();\r
-     }\r
-     \r
-     popup()\r
-     {\r
-          int x,y;\r
-     \r
-          curr_cursor(&x,&y);\r
-     \r
-          /* Call the TSR C program here */\r
-          exec();\r
-          cursor(x,y);\r
-     }\r
-     \r
-     /*\r
-          Second source module\r
-     */\r
-     \r
-     #include <dos.h>\r
-     #include <stdio.h>\r
-     \r
-     static union REGS rg;\r
-     static struct SREGS seg;\r
-     static unsigned mcbseg;\r
-     static unsigned dosseg;\r
-     static unsigned dosbusy;\r
-     static unsigned enddos;\r
-     char far *intdta;\r
-     static unsigned intsp;\r
-     static unsigned intss;\r
-     static char far *mydta;\r
-     static unsigned myss;\r
-     static unsigned stack;\r
-     static unsigned ctrl_break;\r
-     static unsigned mypsp;\r
-     static unsigned intpsp;\r
-     static unsigned pids[2];\r
-     static int pidctr = 0;\r
-     static int pp;\r
-     static void interrupt (*oldtimer)();\r
-     static void interrupt (*old28)();\r
-     static void interrupt (*oldkb)();\r
-     static void interrupt (*olddisk)();\r
-     static void interrupt (*oldcrit)();\r
-     \r
-     void interrupt newtimer();\r
-     void interrupt new28();\r
-     void interrupt newkb();\r
-     void interrupt newdisk();\r
-     void interrupt newcrit();\r
-     \r
-     extern unsigned sizeprogram;\r
-     extern unsigned scancode;\r
-     extern unsigned keymask;\r
-     \r
-     static int resoff = 0;\r
-     static int running = 0;\r
-     static int popflg = 0;\r
-     static int diskflag = 0;\r
-     static int kbval;\r
-     static int cflag;\r
-     \r
-     void dores(void);\r
-     void pidaddr(void);\r
-     \r
-     void resinit()\r
-     {\r
-          segread(&seg);\r
-          myss = seg.ss;\r
-     \r
-          rg.h.ah = 0x34;\r
-          intdos(&rg,&rg);\r
-          dosseg = _ES;\r
-          dosbusy = rg.x.bx;\r
-     \r
-          mydta = getdta();\r
-          pidaddr();\r
-          oldtimer = getvect(0x1c);\r
-          old28 = getvect(0x28);\r
-          oldkb = getvect(9);\r
-          olddisk = getvect(0x13);\r
-     \r
-          setvect(0x1c,newtimer);\r
-          setvect(9,newkb);\r
-          setvect(0x28,new28);\r
-          setvect(0x13,newdisk);\r
-     \r
-          stack = (sizeprogram - (seg.ds - seg.cs)) * 16 - 300;\r
-          rg.x.ax = 0x3100;\r
-          rg.x.dx = sizeprogram;\r
-          intdos(&rg,&rg);\r
-     }\r
-     \r
-     void interrupt newdisk(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)\r
-     {\r
-          diskflag++;\r
-          (*olddisk)();\r
-          ax = _AX;\r
-          newcrit();\r
-          flgs = cflag;\r
-          --diskflag;\r
-     }\r
-     \r
-     void interrupt newcrit(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)\r
-     {\r
-          ax = 0;\r
-          cflag = flgs;\r
-     }\r
-     \r
-     void interrupt newkb()\r
-     {\r
-          if (inportb(0x60) == scancode)\r
-          {\r
-               kbval = peekb(0,0x417);\r
-               if (!resoff && ((kbval & keymask) ^ keymask) == 0)\r
-               {\r
-                    kbval = inportb(0x61);\r
-                    outportb(0x61,kbval | 0x80);\r
-                    outportb(0x61,kbval);\r
-                    disable();\r
-                    outportb(0x20,0x20);\r
-                    enable();\r
-                    if (!running)\r
-                         popflg = 1;\r
-                    return;\r
-               }\r
-          }\r
-          (*oldkb)();\r
-     }\r
-     \r
-     void interrupt newtimer()\r
-     {\r
-          (*oldtimer)();\r
-          if (popflg && peekb(dosseg,dosbusy) == 0)\r
-               if(diskflag == 0)\r
-               {\r
-                    outportb(0x20,0x20);\r
-                    popflg = 0;\r
-                    dores();\r
-               }\r
-     }\r
-     \r
-     void interrupt new28()\r
-     {\r
-          (*old28)();\r
-          if (popflg && peekb(dosseg,dosbusy) != 0)\r
-          {\r
-               popflg = 0;\r
-               dores();\r
-          }\r
-     }\r
-     \r
-     resident_psp()\r
-     {\r
-          intpsp = peek(dosseg,*pids);\r
-          for(pp = 0; pp < pidctr; pp++)\r
-               poke(dosseg,pids[pp],mypsp);\r
-     }\r
-     \r
-     interrupted_psp()\r
-     {\r
-          for(pp = 0; pp < pidctr; pp++)\r
-               poke(dosseg,pids[pp],intpsp);\r
-     }\r
-     \r
-     void dores()\r
-     {\r
-          running = 1;\r
-          disable();\r
-          intsp = _SP;\r
-          intss = _SS;\r
-          _SP = stack;\r
-          _SS = myss;\r
-          enable();\r
-          oldcrit = getvect(0x24);\r
-          setvect(0x24,newcrit);\r
-          rg.x.ax = 0x3300;\r
-          intdos(&rg,&rg);\r
-          ctrl_break = rg.h.dl;\r
-          rg.x.ax = 0x3301;\r
-          rg.h.dl = 0;\r
-          intdos(&rg,&rg);\r
-          intdta = getdta();\r
-          setdta(mydta);\r
-          resident_psp();\r
-          popup();\r
-          interrupted_psp();\r
-          setdta(intdta);\r
-          setvect(0x24,oldcrit);\r
-          rg.x.ax = 0x3301;\r
-          rg.h.dl = ctrl_break;\r
-          intdos(&rg,&rg);\r
-          disable();\r
-          _SP = intsp;\r
-          _SS = intss;\r
-          enable();\r
-          running = 0;\r
-     }\r
-     \r
-     static int avec = 0;\r
-     \r
-     unsigned resident(char *signature,void interrupt(*ifunc)())\r
-     {\r
-          char *sg;\r
-          unsigned df;\r
-          int vec;\r
-     \r
-          segread(&seg);\r
-          df = seg.ds-seg.cs;\r
-          for(vec = 0x60; vec < 0x68; vec++)\r
-          {\r
-               if (getvect(vec) == NULL)\r
-               {\r
-                    if (!avec)\r
-                         avec = vec;\r
-                    continue;\r
-               }\r
-               for(sg = signature; *sg; sg++)\r
-               if (*sg != peekb(peek(0,2+vec*4)+df,(unsigned)sg))\r
-                    break;\r
-               if (!*sg)\r
-                    return vec;\r
-          }\r
-          if (avec)\r
-               setvect(avec,ifunc);\r
-          return 0;\r
-     }\r
-     \r
-     static void pidaddr()\r
-     {\r
-          unsigned adr = 0;\r
-     \r
-          rg.h.ah = 0x51;\r
-          intdos(&rg,&rg);\r
-          mypsp = rg.x.bx;\r
-          rg.h.ah = 0x52;\r
-          intdos(&rg,&rg);\r
-          enddos = _ES;\r
-          enddos = peek(enddos,rg.x.bx-2);\r
-          while(pidctr < 2 && (unsigned)((dosseg<<4) + adr) < (enddos\r
-     <<4))\r
-          {\r
-               if (peek(dosseg,adr) == mypsp)\r
-               {\r
-                    rg.h.ah = 0x50;\r
-                    rg.x.bx = mypsp + 1;\r
-                    intdos(&rg,&rg);\r
-                    if (peek(dosseg,adr) == mypsp + 1)\r
-                         pids[pidctr++] = adr;\r
-                    rg.h.ah = 0x50;\r
-                    rg.x.bx = mypsp;\r
-                    intdos(&rg,&rg);\r
-               }\r
-               adr++;\r
-          }\r
-     }\r
-     \r
-     static resterm()\r
-     {\r
-          setvect(0x1c,oldtimer);\r
-          setvect(9,oldkb);\r
-          setvect(0x28,old28);\r
-          setvect(0x13,olddisk);\r
-          setvect(avec,(void interrupt (*)()) 0);\r
-          rg.h.ah = 0x52;\r
-          intdos(&rg,&rg);\r
-          mcbseg = _ES;\r
-          mcbseg = peek(mcbseg,rg.x.bx-2);\r
-          segread(&seg);\r
-          while(peekb(mcbseg,0) == 0x4d)\r
-          {\r
-               if(peek(mcbseg,1) == mypsp)\r
-               {\r
-                    rg.h.ah = 0x49;\r
-                    seg.es = mcbseg+1;\r
-                    intdosx(&rg,&rg,&seg);\r
-               }\r
-               mcbseg += peek(mcbseg,3) + 1;\r
-          }\r
-     }\r
-     \r
-     terminate()\r
-     {\r
-          if (getvect(0x13) == (void interrupt (*)()) newdisk)\r
-               if (getvect(9) == newkb)\r
-                    if(getvect(0x28) == new28)\r
-                         if(getvect(0x1c) == newtimer)\r
-                         {\r
-                              resterm();\r
-                              return;\r
-                         }\r
-          resoff = 1;\r
-     }\r
-     \r
-     restart()\r
-     {\r
-          resoff = 0;\r
-     }\r
-     \r
-     wait()\r
-     {\r
-          resoff = 1;\r
-     }\r
-     \r
-     void cursor(int y, int x)\r
-     {\r
-          rg.x.ax = 0x0200;\r
-          rg.x.bx = 0;\r
-          rg.x.dx = ((y << 8) & 0xff00) + x;\r
-          int86(16,&rg,&rg);\r
-     }\r
-     \r
-     void curr_cursor(int *y, int *x)\r
-     {\r
-          rg.x.ax = 0x0300;\r
-          rg.x.bx = 0;\r
-          int86(16,&rg,&rg);\r
-          *x = rg.h.dl;\r
-          *y = rg.h.dh;\r
-     }\r
-     \r
-     /*\r
-        Third module, the simple pop-up address book\r
-        with mouse support\r
-     */\r
-     \r
-     #include <stdio.h>\r
-     #include <stdlib.h>\r
-     #include <io.h>\r
-     #include <string.h>\r
-     #include <fcntl.h>\r
-     #include <sys\stat.h>\r
-     #include <dos.h>\r
-     #include <conio.h>\r
-     #include <graphics.h>\r
-     #include <bios.h>\r
-     \r
-     /* left cannot be less than 3 */\r
-     #define left   4\r
-     \r
-     /* Data structure for records */\r
-     typedef struct\r
-     {\r
-          char name[31];\r
-          char company[31];\r
-          char address[31];\r
-          char area[31];\r
-          char town[31];\r
-          char county[31];\r
-          char post[13];\r
-          char telephone[16];\r
-          char fax[16];\r
-     }\r
-     data;\r
-     \r
-     extern char fpath[];\r
-     \r
-     static char scr[4000];\r
-     \r
-     static char sbuff[2000];\r
-     char stext[30];\r
-     data rec;\r
-     int handle;\r
-     int recsize;\r
-     union REGS inreg,outreg;\r
-     \r
-     /*\r
-          Function prototypes\r
-     */\r
-     void FATAL(char *);\r
-     void OPENDATA(void);\r
-     void CONTINUE(void);\r
-     void EXPORT_MULTI(void);\r
-     void GETDATA(int);\r
-     int GETOPT(void);\r
-     void DISPDATA(void);\r
-     void ADD_REC(void);\r
-     void PRINT_MULTI(void);\r
-     void SEARCH(void);\r
-     void MENU(void);\r
-     \r
-     int GET_MOUSE(int *buttons)\r
-     {\r
-          inreg.x.ax = 0;\r
-          int86(0x33,&inreg,&outreg);\r
-          *buttons = outreg.x.bx;\r
-          return outreg.x.ax;\r
-     }\r
-     \r
-     void MOUSE_CURSOR(int status)\r
-     {\r
-          /* Status = 0 cursor off */\r
-          /*          1 cursor on */\r
-     \r
-          inreg.x.ax = 2 - status;\r
-          int86(0x33,&inreg,&outreg);\r
-     }\r
-     \r
-     int MOUSE_LOCATION(int *x, int *y)\r
-     {\r
-          inreg.x.ax = 3;\r
-          int86(0x33,&inreg,&outreg);\r
-     \r
-          *x = outreg.x.cx / 8;\r
-          *y = outreg.x.dx / 8;\r
-     \r
-          return outreg.x.bx;\r
-     }\r
-     \r
-     int GETOPT()\r
-     {\r
-          int result;\r
-          int x;\r
-          int y;\r
-     \r
-          do\r
-          {\r
-               do\r
-               {\r
-                    result = MOUSE_LOCATION(&x,&y);\r
-                    if (result & 1)\r
-                    {\r
-                         if (x >= 52 && x <= 53 && y >= 7 && y <= 15)\r
-                              return y - 7;\r
-                         if (x >= 4 && x <= 40 && y >= 7 && y <= 14)\r
-                              return y + 10;\r
-     \r
-                         if (x >= 4 && x <= 40 && y == 15)\r
-                              return y + 10;\r
-                    }\r
-               }\r
-               while(!bioskey(1));\r
-     \r
-               result = bioskey(0);\r
-               x = result & 0xff;\r
-               if (x == 0)\r
-               {\r
-                    result = result >> 8;\r
-                    result -= 60;\r
-               }\r
-          }\r
-          while(result < 0 || result > 8);\r
-          return result;\r
-     }\r
-     \r
-     void setvideo(unsigned char mode)\r
-     {\r
-          /* Sets the video display mode     and clears the screen */\r
-     \r
-          inreg.h.al = mode;\r
-          inreg.h.ah = 0x00;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     \r
-     int activepage(void)\r
-     {\r
-          /* Returns the currently selected video display page */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 0x0F;\r
-          int86(0x10, &inreg, &outreg);\r
-          return(outreg.h.bh);\r
-     }\r
-     \r
-     void print(char *str)\r
-     {\r
-          /*\r
-             Prints characters only directly to the current display page\r
-             starting at the current cursor position. The cursor is not\r
-             advanced.\r
-             This function assumes a colour display card. For use with a\r
-             monochrome display card change 0xB800 to read 0xB000\r
-          */\r
-     \r
-          int page;\r
-          int offset;\r
-          unsigned row;\r
-          unsigned col;\r
-          char far *ptr;\r
-     \r
-          page = activepage();\r
-          curr_cursor(&row,&col);\r
-     \r
-          offset = page * 4000 + row * 160 + col * 2;\r
-     \r
-          ptr = MK_FP(0xB800,offset);\r
-     \r
-          while(*str)\r
-          {\r
-               *ptr++= *str++;\r
-               ptr++;\r
-          }\r
-     }\r
-     \r
-     \r
-     void TRUESHADE(int lef, int top, int right, int bottom)\r
-     {\r
-          int n;\r
-     \r
-          /* True Shading of a screen block */\r
-     \r
-          gettext(lef,top,right,bottom,sbuff);\r
-          for(n = 1; n < 2000; n+= 2)\r
-               sbuff[n] = 7;\r
-          puttext(lef,top,right,bottom,sbuff);\r
-     }\r
-     \r
-     void DBOX(int l, int t, int r, int b)\r
-     {\r
-          /* Draws a double line box around the described area */\r
-     \r
-          int n;\r
-     \r
-          cursor(t,l);\r
-          print("\90");\r
-          for(n = 1; n < r - l; n++)\r
-          {\r
-               cursor(t,l + n);\r
-               print("I");\r
-          }\r
-          cursor(t,r);\r
-          print("¯");\r
-     \r
-          for (n = t + 1; n < b; n++)\r
-          {\r
-               cursor(n,l);\r
-               print("§");\r
-               cursor(n,r);\r
-               print("§");\r
-          }\r
-          cursor(b,l);\r
-          print("E");\r
-          for(n = 1; n < r - l; n++)\r
-          {\r
-               cursor(b,l+n);\r
-               print("I");\r
-          }\r
-          cursor(b,r);\r
-          print("¬");\r
-     }\r
-     \r
-     int INPUT(char *text,unsigned length)\r
-     {\r
-          /* Receive a string from the operator */\r
-     \r
-          unsigned key_pos;\r
-          int key;\r
-          unsigned start_row;\r
-          unsigned start_col;\r
-          unsigned end;\r
-          char temp[80];\r
-          char *p;\r
-     \r
-          curr_cursor(&start_row,&start_col);\r
-     \r
-          key_pos = 0;\r
-          end = strlen(text);\r
-          for(;;)\r
-          {\r
-               key = bioskey(0);\r
-               if ((key & 0xFF) == 0)\r
-               {\r
-                    key = key >> 8;\r
-                    if (key == 79)\r
-                    {\r
-                         while(key_pos < end)\r
-                              key_pos++;\r
-                         cursor(start_row,start_col + key_pos);\r
-                    }\r
-                    else\r
-                    if (key == 71)\r
-                    {\r
-                         key_pos = 0;\r
-                         cursor(start_row,start_col);\r
-                    }\r
-                    else\r
-                    if ((key == 75) && (key_pos > 0))\r
-                    {\r
-                         key_pos--;\r
-                         cursor(start_row,start_col + key_pos);\r
-                    }\r
-                    else\r
-                    if ((key == 77) && (key_pos < end))\r
-                    {\r
-                         key_pos++;\r
-                         cursor(start_row,start_col + key_pos);\r
-                    }\r
-                    else\r
-                    if (key == 83)\r
-                    {\r
-                         p = text + key_pos;\r
-                         while(*(p+1))\r
-                         {\r
-                              *p = *(p+1);\r
-                              p++;\r
-                         }\r
-                         *p = 32;\r
-                         if (end > 0)\r
-                              end--;\r
-                         cursor(start_row,start_col);\r
-                         cprintf(text);\r
-                         cprintf(" ");\r
-                         if ((key_pos > 0) && (key_pos == end))\r
-                              key_pos--;\r
-                         cursor(start_row,start_col + key_pos);\r
-                    }\r
-               }\r
-               else\r
-               {\r
-                    key = key & 0xFF;\r
-                    if (key == 13 || key == 27)\r
-                         break;\r
-                    else\r
-                    if ((key == 8) && (key_pos > 0))\r
-                    {\r
-                         end--;\r
-                         key_pos--;\r
-                         text[key_pos--] = '\0';\r
-                         strcpy(temp,text);\r
-                         p = text + key_pos + 2;\r
-                         strcat(temp,p);\r
-                         strcpy(text,temp);\r
-                         cursor(start_row,start_col);\r
-                         cprintf("%-*.*s",length,length,text);\r
-                         key_pos++;\r
-                         cursor(start_row,start_col + key_pos);\r
-                    }\r
-                    else\r
-                    if ((key > 31) && (key_pos < length)  &&\r
-                       (start_col + key_pos < 80))\r
-                    {\r
-                         if (key_pos <= end)\r
-                         {\r
-                              p = text + key_pos;\r
-                              memmove(p+1,p,end - key_pos);\r
-                              if (end < length)\r
-                                   end++;\r
-                              text[end] = '\0';\r
-                         }\r
-                         text[key_pos++] = (char)key;\r
-                         if (key_pos > end)\r
-                         {\r
-                              end++;\r
-                              text[end] = '\0';\r
-                         }\r
-                         cursor(start_row,start_col);\r
-                         cprintf("%-*.*s",length,length,text);\r
-                         cursor(start_row,start_col + key_pos);\r
-                    }\r
-               }\r
-          }\r
-          text[end] = '\0';\r
-          return key;\r
-     }\r
-     \r
-     void FATAL(char *error)\r
-     {\r
-          /* A fatal error has occured */\r
-     \r
-          printf("\nFATAL ERROR: %s",error);\r
-          exit(0);\r
-     }\r
-     \r
-     void OPENDATA()\r
-     {\r
-          /* Check for existence of data file and if not create it */\r
-          /* otherwise open it for reading/writing at end of file */\r
-     \r
-          handle = open(fpath,O_RDWR,S_IWRITE);\r
-     \r
-          if (handle == -1)\r
-          {\r
-               handle = open(fpath,O_RDWR|O_CREAT,S_IWRITE);\r
-               if (handle == -1)\r
-                    FATAL("Unable to create data file");\r
-          }\r
-          /* Read in first rec */\r
-          read(handle,&rec,recsize);\r
-     }\r
-     \r
-     void CLOSEDATA()\r
-     {\r
-          close(handle);\r
-     }\r
-     \r
-     void GETDATA(int start)\r
-     {\r
-          /* Get address data from operator */\r
-     \r
-          textcolor(BLACK);\r
-          textbackground(GREEN);\r
-          gotoxy(left,8);\r
-          print("Name ");\r
-          gotoxy(left,9);\r
-          print("Company ");\r
-          gotoxy(left,10);\r
-          print("Address ");\r
-          gotoxy(left,11);\r
-          print("Area ");\r
-          gotoxy(left,12);\r
-          print("Town ");\r
-          gotoxy(left,13);\r
-          print("County ");\r
-          gotoxy(left,14);\r
-          print("Post Code ");\r
-          gotoxy(left,15);\r
-          print("Telephone ");\r
-          gotoxy(left,16);\r
-          print("Fax ");\r
-     \r
-          switch(start)\r
-          {\r
-               case 0: gotoxy(left + 10,8);\r
-                    if(INPUT(rec.name,30) == 27)\r
-                         break;\r
-               case 1: gotoxy(left + 10,9);\r
-                    if(INPUT(rec.company,30) == 27)\r
-                         break;\r
-               case 2: gotoxy(left + 10,10);\r
-                    if(INPUT(rec.address,30) == 27)\r
-                         break;\r
-               case 3: gotoxy(left + 10,11);\r
-                    if(INPUT(rec.area,30) == 27)\r
-                         break;\r
-               case 4: gotoxy(left + 10,12);\r
-                    if(INPUT(rec.town,30) == 27)\r
-                         break;\r
-               case 5: gotoxy(left + 10,13);\r
-                    if(INPUT(rec.county,30) == 27)\r
-                         break;\r
-               case 6: gotoxy(left + 10,14);\r
-                    if(INPUT(rec.post,12) == 27)\r
-                         break;\r
-               case 7: gotoxy(left + 10,15);\r
-                    if(INPUT(rec.telephone,15) == 27)\r
-                         break;\r
-               case 8: gotoxy(left + 10,16);\r
-                    INPUT(rec.fax,15);\r
-                    break;\r
-          }\r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          gotoxy(left + 23,21);\r
-          print("                                                 ");\r
-     }\r
-     \r
-     void DISPDATA()\r
-     {\r
-          /* Display address data */\r
-          textcolor(BLACK);\r
-          textbackground(GREEN);\r
-          cursor(7,3);\r
-          cprintf("Name    %-30.30s",rec.name);\r
-          cursor(8,3);\r
-          cprintf("Company   %-30.30s",rec.company);\r
-          cursor(9,3);\r
-          cprintf("Address   %-30.30s",rec.address);\r
-          cursor(10,3);\r
-          cprintf("Area    %-30.30s",rec.area);\r
-          cursor(11,3);\r
-          cprintf("Town    %-30.30s",rec.town);\r
-          cursor(12,3);\r
-          cprintf("County     %-30.30s",rec.county);\r
-          cursor(13,3);\r
-          cprintf("Post Code %-30.30s",rec.post);\r
-          cursor(14,3);\r
-          cprintf("Telephone %-30.30s",rec.telephone);\r
-          cursor(15,3);\r
-          cprintf("Fax      %-30.30s",rec.fax);\r
-     }\r
-     \r
-     int LOCATE(char *text)\r
-     {\r
-          int result;\r
-     \r
-          do\r
-          {\r
-               /* Read rec into memory */\r
-               result = read(handle,&rec,recsize);\r
-               if (result > 0)\r
-               {\r
-                    /* Scan rec for matching data */\r
-                    if (strstr(strupr(rec.name),text) != NULL)\r
-                         return(1);\r
-                    if (strstr(strupr(rec.company),text) != NULL)\r
-                         return(1);\r
-                    if (strstr(strupr(rec.address),text) != NULL)\r
-                         return(1);\r
-                    if (strstr(strupr(rec.area),text) != NULL)\r
-                         return(1);\r
-                    if (strstr(strupr(rec.town),text) != NULL)\r
-                         return(1);\r
-                    if (strstr(strupr(rec.county),text) != NULL)\r
-                         return(1);\r
-                    if (strstr(strupr(rec.post),text) != NULL)\r
-                         return(1);\r
-                    if (strstr(strupr(rec.telephone),text) != NULL)\r
-                         return(1);\r
-                    if (strstr(strupr(rec.fax),text) != NULL)\r
-                         return(1);\r
-               }\r
-          }\r
-          while(result > 0);\r
-          return(0);\r
-     }\r
-     \r
-     void SEARCH()\r
-     {\r
-          int result;\r
-     \r
-          gotoxy(left,21);\r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          cprintf("Enter data to search for ");\r
-          strcpy(stext,"");\r
-          INPUT(stext,30);\r
-          if (*stext == 0)\r
-          {\r
-               gotoxy(left,21);\r
-               cprintf("%70c",32);\r
-               return;\r
-          }\r
-          gotoxy(left,21);\r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          cprintf("Searching for %s Please Wait....",stext);\r
-          strupr(stext);\r
-          /* Locate start of file */\r
-          lseek(handle,0,SEEK_SET);\r
-          result = LOCATE(stext);\r
-          if (result == 0)\r
-          {\r
-               gotoxy(left,21);\r
-               cprintf("%70c",32);\r
-               gotoxy(left + 27,21);\r
-               cprintf("NO MATCHING RECORDS");\r
-               gotoxy(left + 24,22);\r
-               cprintf("Press RETURN to Continue");\r
-               bioskey(0);\r
-               gotoxy(left,21);\r
-               cprintf("%70c",32);\r
-               gotoxy(left,22);\r
-               cprintf("%70c",32);\r
-          }\r
-          else\r
-          {\r
-               lseek(handle,0 - recsize,SEEK_CUR);\r
-               read(handle,&rec,recsize);\r
-               DISPDATA();\r
-          }\r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          gotoxy(left,21);\r
-          cprintf("%70c",32);\r
-          textcolor(BLACK);\r
-          textbackground(GREEN);\r
-     }\r
-     \r
-     void CONTINUE()\r
-     {\r
-          int result;\r
-          long curpos;\r
-     \r
-          curpos = tell(handle) - recsize;\r
-     \r
-          result = LOCATE(stext);\r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          if (result == 0)\r
-          {\r
-               gotoxy(left + 24,21);\r
-               cprintf("NO MORE MATCHING RECORDS");\r
-               gotoxy(left + 24,22);\r
-               cprintf("Press RETURN to Continue");\r
-               bioskey(0);\r
-               gotoxy(left,21);\r
-               cprintf("%70c",32);\r
-               gotoxy(left,22);\r
-               cprintf("%70c",32);\r
-               lseek(handle,curpos,SEEK_SET);\r
-               read(handle,&rec,recsize);\r
-               DISPDATA();\r
-          }\r
-          else\r
-          {\r
-               lseek(handle,0 - recsize,SEEK_CUR);\r
-               read(handle,&rec,recsize);\r
-               DISPDATA();\r
-          }\r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          gotoxy(left,21);\r
-          cprintf("%70c",32);\r
-          gotoxy(left,22);\r
-          cprintf("                                           ");\r
-          textcolor(BLACK);\r
-          textbackground(GREEN);\r
-     }\r
-     \r
-     void PRINT_MULTI()\r
-     {\r
-          data buffer;\r
-          char destination[60];\r
-          char text[5];\r
-          int result;\r
-          int ok;\r
-          int ok2;\r
-          int blanks;\r
-          int total_lines;\r
-          char *p;\r
-          FILE *fp;\r
-     \r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          gotoxy(left + 23,21);\r
-          cprintf("Enter selection criteria");\r
-     \r
-          /* Clear existing rec details */\r
-          memset(&rec,0,recsize);\r
-     \r
-          DISPDATA();\r
-          GETDATA(0);\r
-     \r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          gotoxy(left,21);\r
-          cprintf("Enter report destination PRN");\r
-          strcpy(destination,"PRN");\r
-          gotoxy(left,22);\r
-          cprintf("Enter Address length in lines 18");\r
-          strcpy(text,"18");\r
-          gotoxy(left + 25,21);\r
-          INPUT(destination,40);\r
-          gotoxy(left +30,22);\r
-          INPUT(text,2);\r
-          gotoxy(left,21);\r
-          cprintf("%72c",32);\r
-          gotoxy(left,22);\r
-          cprintf("%72c",32);\r
-     \r
-          total_lines = atoi(text) - 6;\r
-          if (total_lines < 0)\r
-               total_lines = 0;\r
-     \r
-          fp = fopen(destination,"w+");\r
-          if (fp == NULL)\r
-          {\r
-               gotoxy(left,21);\r
-               cprintf("Unable to print to %s",destination);\r
-               gotoxy(left,22);\r
-               cprintf("Press RETURN to Continue");\r
-               bioskey(0);\r
-               gotoxy(left,21);\r
-               cprintf("%78c",32);\r
-               gotoxy(left,22);\r
-               cprintf("                          ");\r
-          }\r
-     \r
-          /* Locate start of file */\r
-          lseek(handle,0,SEEK_SET);\r
-     \r
-          do\r
-          {\r
-               /* Read rec into memory */\r
-               result = read(handle,&buffer,recsize);\r
-               if (result > 0)\r
-               {\r
-                    ok = 1;\r
-                    /* Scan rec for matching data */\r
-                    if (*rec.name)\r
-                         if (stricmp(buffer.name,rec.name))\r
-                              ok = 0;\r
-                    if (*rec.company)\r
-                         if (stricmp(buffer.company,rec.company))\r
-                              ok = 0;\r
-                    if (*rec.address)\r
-                         if (stricmp(buffer.address,rec.address))\r
-                              ok = 0;\r
-                    if (*rec.area)\r
-                         if (stricmp(buffer.area,rec.area))\r
-                              ok = 0;\r
-                    if (*rec.town)\r
-                         if (stricmp(buffer.town,rec.town))\r
-                              ok = 0;\r
-                    if (*rec.county)\r
-                         if (stricmp(buffer.county,rec.county))\r
-                              ok = 0;\r
-                    if (*rec.post)\r
-                         if (stricmp(buffer.post,rec.post))\r
-                         ok = 0;\r
-                    if (*rec.telephone)\r
-                         if (stricmp(buffer.telephone,rec.telephone))\r
-                              ok = 0;\r
-                    if (*rec.fax)\r
-                         if (stricmp(buffer.fax,rec.fax))\r
-                              ok = 0;\r
-                    if (ok)\r
-                    {\r
-                         blanks = total_lines;\r
-                         p = buffer.name;\r
-                         ok2 = 0;\r
-                         while(*p)\r
-                         {\r
-                              if (*p != 32)\r
-                              {\r
-                                   ok2 = 1;\r
-                                   break;\r
-                              }\r
-                              p++;\r
-                         }\r
-                         if (!ok2)\r
-                              blanks++;\r
-                         else\r
-                              fprintf(fp,"%s\n",buffer.name);\r
-                         p = buffer.company;\r
-                         ok2 = 0;\r
-                         while(*p)\r
-                         {\r
-                              if (*p != 32)\r
-                              {\r
-                                   ok2 = 1;\r
-                                   break;\r
-                              }\r
-                              p++;\r
-                         }\r
-                         if (!ok2)\r
-                              blanks++;\r
-                         else\r
-                              fprintf(fp,"%s\n",buffer.company);\r
-                         p = buffer.address;\r
-                         ok2 = 0;\r
-     \r
-                         while(*p)\r
-                         {\r
-                              if (*p != 32)\r
-                              {\r
-                                   ok2 = 1;\r
-                                   break;\r
-                              }\r
-                              p++;\r
-                         }\r
-                         if (!ok2)\r
-                              blanks++;\r
-                         else\r
-                              fprintf(fp,"%s\n",buffer.address);\r
-                         p = buffer.area;\r
-                         ok2 = 0;\r
-                         while(*p)\r
-                         {\r
-                              if (*p != 32)\r
-                              {\r
-                                   ok2 = 1;\r
-                                   break;\r
-                              }\r
-                              p++;\r
-                         }\r
-                         if (!ok2)\r
-                              blanks++;\r
-                         else\r
-                              fprintf(fp,"%s\n",buffer.area);\r
-                         p = buffer.town;\r
-                         ok2 = 0;\r
-                         while(*p)\r
-                         {\r
-                              if (*p != 32)\r
-                              {\r
-                                   ok2 = 1;\r
-                                   break;\r
-                              }\r
-                              p++;\r
-                         }\r
-                         if (!ok2)\r
-                              blanks++;\r
-                         else\r
-                              fprintf(fp,"%s\n",buffer.town);\r
-                         p = buffer.county;\r
-                         ok2 = 0;\r
-     \r
-                         while(*p)\r
-                         {\r
-                              if (*p != 32)\r
-                              {\r
-                                   ok2 = 1;\r
-                                   break;\r
-                              }\r
-                              p++;\r
-                         }\r
-                         if (!ok2)\r
-                              blanks++;\r
-                         else\r
-                              fprintf(fp,"%s\n",buffer.county);\r
-                         p = buffer.post;\r
-                         ok2 = 0;\r
-                         while(*p)\r
-                         {\r
-                              if (*p != 32)\r
-                              {\r
-                                   ok2 = 1;\r
-                                   break;\r
-                              }\r
-                              p++;\r
-                         }\r
-                         if (!ok2)\r
-                              blanks++;\r
-                         else\r
-                              fprintf(fp,"%s\n",buffer.post);\r
-                         while(blanks)\r
-                         {\r
-                              fprintf(fp,"\n");\r
-                              blanks--;\r
-                         }\r
-                    }\r
-               }\r
-          }\r
-          while(result > 0);\r
-          fclose(fp);\r
-          lseek(handle,0,SEEK_SET);\r
-          read(handle,&rec,recsize);\r
-          DISPDATA();\r
-     }\r
-     \r
-     void EXPORT_MULTI()\r
-     {\r
-          data buffer;\r
-          char destination[60];\r
-          int result;\r
-          int ok;\r
-          FILE *fp;\r
-     \r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          gotoxy(left + 23,21);\r
-          cprintf("Enter selection criteria");\r
-     \r
-          /* Clear existing rec details */\r
-          memset(&rec,0,recsize);\r
-     \r
-          DISPDATA();\r
-          GETDATA(0);\r
-     \r
-          textcolor(WHITE);\r
-          textbackground(RED);\r
-          gotoxy(left,21);\r
-          cprintf("Enter export file address.txt");\r
-          strcpy(destination,"address.txt");\r
-          gotoxy(left + 18,21);\r
-          INPUT(destination,59);\r
-          gotoxy(left,21);\r
-          cprintf("%70c",32);\r
-     \r
-          fp = fopen(destination,"w+");\r
-          if (fp == NULL)\r
-          {\r
-               gotoxy(left,21);\r
-               cprintf("Unable to print to %s",destination);\r
-               gotoxy(left,22);\r
-               cprintf("Press RETURN to Continue");\r
-               bioskey(0);\r
-               gotoxy(left,21);\r
-               cprintf("%78c",32);\r
-               gotoxy(left,22);\r
-               cprintf("                          ");\r
-          }\r
-          /* Locate start of file */\r
-          lseek(handle,0,SEEK_SET);\r
-     \r
-          do\r
-          {\r
-               /* Read rec into memory */\r
-               result = read(handle,&buffer,recsize);\r
-               if (result > 0)\r
-               {\r
-                    ok = 1;\r
-                    /* Scan rec for matching data */\r
-                    if (*rec.name)\r
-                         if (stricmp(buffer.name,rec.name))\r
-                              ok = 0;\r
-                    if (*rec.company)\r
-                         if (stricmp(buffer.company,rec.company))\r
-                              ok = 0;\r
-                    if (*rec.address)\r
-                         if (stricmp(buffer.address,rec.address))\r
-                              ok = 0;\r
-                    if (*rec.area)\r
-                         if (stricmp(buffer.area,rec.area))\r
-                              ok = 0;\r
-                    if (*rec.town)\r
-                         if (stricmp(buffer.town,rec.town))\r
-                              ok = 0;\r
-                    if (*rec.county)\r
-                         if (stricmp(buffer.county,rec.county))\r
-                              ok = 0;\r
-                    if (*rec.post)\r
-                         if (stricmp(buffer.post,rec.post))\r
-                         ok = 0;\r
-                    if (*rec.telephone)\r
-                         if (stricmp(buffer.telephone,rec.telephone))\r
-                              ok = 0;\r
-                    if (*rec.fax)\r
-                         if (stricmp(buffer.fax,rec.fax))\r
-                              ok = 0;\r
-                    if (ok)\r
-                    {\r
-                         fprintf(fp,"\"%s\",",buffer.name);\r
-                         fprintf(fp,"\"%s\",",buffer.company);\r
-                         fprintf(fp,"\"%s\",",buffer.address);\r
-                         fprintf(fp,"\"%s\",",buffer.area);\r
-                         fprintf(fp,"\"%s\",",buffer.town);\r
-                         fprintf(fp,"\"%s\",",buffer.county);\r
-                         fprintf(fp,"\"%s\",",buffer.post);\r
-                         fprintf(fp,"\"%s\",",buffer.telephone);\r
-                         fprintf(fp,"\"%s\"\n",buffer.fax);\r
-     \r
-                    }\r
-               }\r
-          }\r
-     \r
-          while(result > 0);\r
-          fclose(fp);\r
-          lseek(handle,0,SEEK_SET);\r
-          read(handle,&rec,recsize);\r
-          DISPDATA();\r
-     }\r
-     \r
-     void MENU()\r
-     {\r
-          int option;\r
-          long result;\r
-          long end;\r
-          int new;\r
-     \r
-          do\r
-          {\r
-               cursor(21,26);\r
-               print("Select option (F2 - F10)");\r
-               cursor(7,52);\r
-               print("F2 Next record");\r
-               cursor(8,52);\r
-               print("F3 Previous record");\r
-               cursor(9,52);\r
-               print("F4 Amend record");\r
-               cursor(10,52);\r
-               print("F5 Add new record");\r
-               cursor(11,52);\r
-               print("F6 Search");\r
-               cursor(12,52);\r
-               print("F7 Continue search");\r
-               cursor(13,52);\r
-               print("F8 Print address labels");\r
-               cursor(14,52);\r
-               print("F9 Export records");\r
-               cursor(15,52);\r
-               print("F10 Exit");\r
-               MOUSE_CURSOR(1);\r
-               option = GETOPT();\r
-               MOUSE_CURSOR(0);\r
-     \r
-               switch(option)\r
-               {\r
-                    case 0 : /* Next rec */\r
-                          result = read(handle,&rec,recsize);\r
-                          if (!result)\r
-                          {\r
-                              lseek(handle,0,SEEK_SET);\r
-                               result = read(handle,&rec,recsize);\r
-                          }\r
-                          DISPDATA();\r
-                          break;\r
-     \r
-                    case 1 : /* Previous rec */\r
-                         result = lseek(handle,0 - recsize * 2,SEEK_CUR);\r
-                         if (result <= -1)\r
-                              lseek(handle,0 - recsize,SEEK_END);\r
-                         result = read(handle,&rec,recsize);\r
-                         DISPDATA();\r
-                         break;\r
-\r
-                    case 3 : /* Add rec */\r
-                          lseek(handle,0,SEEK_END);\r
-                          memset(&rec,0,recsize);\r
-                          DISPDATA();\r
-     \r
-                    case 2 : /* Amend current rec */\r
-                          new = 1;\r
-                          if (*rec.name)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.company)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.address)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.area)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.town)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.county)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.post)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.telephone)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.fax)\r
-                              new = 0;\r
-                          result = tell(handle);\r
-                          lseek(handle,0,SEEK_END);\r
-                          end = tell(handle);\r
-     \r
-                          /* Back to original position */\r
-                          lseek(handle,result,SEEK_SET);\r
-     \r
-                          /* If not at end of file, && !new rewind one\r
-rec */\r
-                          if (result != end || ! new)\r
-                              result = lseek(handle,0 -\r
-recsize,SEEK_CUR);\r
-                          result = tell(handle);\r
-                          gotoxy(left + 22,21);\r
-                          print(" Enter address details  ");\r
-                          GETDATA(0);\r
-                          if (*rec.name || *rec.company)\r
-                              result =  write(handle,&rec,recsize);\r
-                          break;\r
-\r
-                    case 4 : /* Search */\r
-                          gotoxy(left + 22,21);\r
-                          print("                           ");\r
-                          SEARCH();\r
-                          break;\r
-     \r
-                    case 5 : /* Continue */\r
-                          gotoxy(left + 22,21);\r
-                          print("                           ");\r
-                          CONTINUE();\r
-                          break;\r
-     \r
-                    case 6 : /* Print */\r
-                          gotoxy(left + 22,21);\r
-                          print("                           ");\r
-                          PRINT_MULTI();\r
-                          break;\r
-\r
-                    case 7 : /* Export */\r
-                          gotoxy(left + 22,21);\r
-                          print("                           ");\r
-                          EXPORT_MULTI();\r
-                          break;\r
-     \r
-                    case 8 : /* Exit */\r
-                          break;\r
-     \r
-                    default: /* Amend current rec */\r
-                          new = 1;\r
-                          if (*rec.name)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.company)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.address)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.area)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.town)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.county)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.post)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.telephone)\r
-                              new = 0;\r
-                          else\r
-                          if (*rec.fax)\r
-                              new = 0;\r
-                          result = tell(handle);\r
-                          lseek(handle,0,SEEK_END);\r
-                          end = tell(handle);\r
-\r
-                          /* Back to original position */\r
-                          lseek(handle,result,SEEK_SET);\r
-\r
-                          /* If not at end of file, && !new rewind one\r
-rec */\r
-                          if (result != end || ! new)\r
-                              result = lseek(handle,0 -\r
-recsize,SEEK_CUR);\r
-                          result = tell(handle);\r
-                          gotoxy(left + 22,21);\r
-                          print(" Enter address details  ");\r
-                          GETDATA(option - 17);\r
-                          if (*rec.name || *rec.company)\r
-                              result = write(handle,&rec,recsize);\r
-                          option = -1;\r
-                          break;\r
-\r
-               }\r
-          }\r
-     \r
-          while(option != 8);\r
-     }\r
-     \r
-     void exec()\r
-     {\r
-          gettext(1,1,80,25,scr);\r
-          setvideo(3);\r
-          textbackground(WHITE);\r
-          textcolor(BLACK);\r
-          clrscr();\r
-          recsize = sizeof(data);\r
-     \r
-          OPENDATA();\r
-     \r
-          TRUESHADE(left,3,79,5);\r
-          window(left - 2,2 ,78, 4);\r
-          textcolor(YELLOW);\r
-          textbackground(MAGENTA);\r
-          clrscr();\r
-          DBOX(left - 3, 1, 77, 3);\r
-          gotoxy(3,2);\r
-          print("Servile Software             PC ADDRESS BOOK 5.2\r
-     (c) 1994");\r
-     \r
-          TRUESHADE(left,8,left + 43,18);\r
-          window(left - 2,7 , left + 42, 17);\r
-          textcolor(BLACK);\r
-          textbackground(GREEN);\r
-          clrscr();\r
-          DBOX(left - 3, 6, left + 41, 16);\r
-     \r
-          TRUESHADE(left + 48,8,79,18);\r
-          window(left + 46, 7 , 78, 17);\r
-          textbackground(BLUE);\r
-          textcolor(YELLOW);\r
-          clrscr();\r
-          DBOX(left + 45,6,77,16);\r
-     \r
-          TRUESHADE(left ,21,79,24);\r
-          window(left - 2, 20 , 78, 23);\r
-          textbackground(RED);\r
-          textcolor(WHITE);\r
-          clrscr();\r
-          DBOX(left - 3,19,77,22);\r
-     \r
-          window(1,1,80,25);\r
-          textcolor(BLACK);\r
-          textbackground(GREEN);\r
-          DISPDATA();\r
-     \r
-          MENU();\r
-     \r
-          CLOSEDATA();\r
-          puttext(1,1,80,25,scr);\r
-          return;\r
-     }\r
-     \r
-                                    \r
-                       INTERFACING C WITH CLIPPER\r
-\r
-The Clipper programming language is a popular xBase environment for the\r
-PC.  However, it lacks many of the facilities available to programmers of\r
-other languages, and it is quite slow compared to C. Because of this\r
-there are a large number of third party add-on libraries available for\r
-Clipper which provide the facilities lacked.\r
-\r
-As a programmer you probably want to write your own library for Clipper,\r
-or perhaps individual functions to cater for circumstances which Clipper\r
-cannot handle, such as high resolution graphics.\r
-\r
-Throughout this section, Clipper refers to the Summer `87 Clipper\r
-compiler, although initial tests show that the functions described here\r
-work perfectly well with the new Clipper 5 compiler also, we are not in a\r
-position to guarrantee success!\r
-\r
-\r
-COMPILING AND LINKING\r
-The  Clipper extend functions allow user defined functions to be written\r
-in C, linked with and used by the Clipper application. The  first\r
-problem a programmer must address when writing functions in C  to link\r
-with  a  Clipper  application is that of the  C  compiler's  run  time\r
-libraries.\r
-\r
-If one is writing functions with Microsoft C,  then most of the required\r
-run time  library  functions  will be found in the  Clipper.lib  and\r
-Extend.lib libraries which are part of Clipper.\r
-\r
-If,  however, one is using a different C compiler, such as Borland's\r
-Turbo C then the run time library routines must be supplied on the link\r
-line.\r
-\r
-All C functions must be compiled using the large memory model the\r
-following line is used with Microsoft C\r
-\r
-\r
-     cl /c /AL /Zl /Oalt /FPa /Gs <program.c>\r
-\r
-and this compile line may be used with Turbo C\r
-\r
-     tcc -c -ml <program>\r
-\r
-simply substitute <program> for the program name to be compiled.\r
-\r
-Having compiled a C function it must be linked in with the application.\r
-If the C function was compiled with Microsoft C then the link line will\r
-look a little like this;\r
-\r
-\r
-     LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend\r
-\r
-If  the C function was linked with another C compiler you will also need\r
-to link in the C run time libraries,  for example to link in the Turbo C\r
-large memory mode library use the following link line;\r
-\r
-\r
-     LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend cl\r
-\r
-If one is using a number of separately compiled C functions it is a good\r
-idea to  collect  them in a library.  If you are using Microsoft C then\r
-you  can simply  create  the  library by using Microsoft Lib.exe with\r
-the  following command line;\r
-\r
-\r
-      LIB mylib +prog1 +prog2, NUL, NUL\r
-\r
-This tells the librarian to add prog1.obj and prog2.obj to a library\r
-called mylib.lib,   creating  it  if it does not exist.  The NUL\r
-parameter  is  for supressing the listing file.\r
-\r
-\r
-If you have been using another C compiler you should copy the C large\r
-memory model run time library before adding your functions to it for\r
-example;\r
-\r
-\r
-      COPY C:\TURBOC\LIB\cl.lib mylib.lib\r
-      LIB mylib +prog1 +prog2, NUL, NUL\r
-\r
-Then when you link your Clipper application you will use a link line\r
-similar to;\r
-\r
-     LINK /SE:500 /NOE myprog,,,Clipper Extend Mylib\r
-\r
-Often  when  linking C functions with  Clipper applications link errors\r
-will occur such as those shown below;\r
-\r
-     \r
-     Microsoft (R) Overlay Linker  Version 3.65\r
-     Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.\r
-     \r
-     \r
-     LINK : error L2029: Unresolved externals:\r
-     \r
-     \r
-     FIWRQQ in file(s):\r
-      M:SLIB.LIB(TEST)\r
-     FIDRQQ in file(s):\r
-      M:SLIB.LIB(TEST)\r
-     \r
-     There were 2 errors detected\r
-     \r
-\r
-Example Link Errors\r
-\r
-The  errors  shown  here  are  `Unresolved externals',   that  is  they\r
-are references to functions which are not found in any of the object\r
-modules  or libraries  specified on the link line.  These occur because\r
-the C  compilers often  scatter  functions and variables through a number\r
-of  libraries.   In tracking  these  functions down  use may be made of\r
-the Microsoft  librarian list file option.  If you run Lib.Exe on the\r
-Turbo C `emu.lib'  library file and specify a listing file as follows;\r
-\r
-\r
-     LIB emu,emu.lst\r
-\r
-The  librarian  will create an ascii file which contains the names  of\r
-each object module contained in the specified library file, and the names\r
-of each function and public variable declared in each object module, as\r
-shown in this listing of Borland's EMU.LIB library.\r
-\r
-\r
-     e086_Entry........EMU086      e086_Shortcut.....EMU086\r
-     e087_Entry........EMU087      e087_Shortcut.....EMU087\r
-     FIARQQ............EMUINIT          FICRQQ............EMUINIT\r
-     FIDRQQ............EMUINIT          FIERQQ............EMUINIT\r
-     FISRQQ............EMUINIT          FIWRQQ............EMUINIT\r
-     FJARQQ............EMUINIT          FJCRQQ............EMUINIT\r
-     FJSRQQ............EMUINIT          __EMURESET........EMUINIT\r
-     \r
-     \r
-     EMUINIT        Offset: 00000010H  Code and data size: 1a2H\r
-     FIARQQ         FICRQQ              FIDRQQ              FIERQQ\r
-     FISRQQ         FIWRQQ              FJARQQ              FJCRQQ\r
-     FJSRQQ         __EMURESET\r
-     \r
-     EMU086         Offset: 00000470H  Code and data size: 2630H\r
-       e086_Entry        e086_Shortcut\r
-     \r
-     EMU087         Offset: 00003200H  Code and data size: 417H\r
-       e087_Entry        e087_Shortcut\r
-     \r
-\r
-\r
-Receiving Parameters\r
-\r
-Clipper provides six different functions for receiving parameters in a C\r
-function. These functions are;\r
-\r
-\r
-  Receive a string      char * _parc(int,[int])\r
-  Receive a Date string char * _pards(int,[int])\r
-  Receive a logical     int _parl(int,[int])\r
-  Receive an integer         int _parni(int,[int])\r
-  Receive a long        long _parnl(int,[int])\r
-  Receive a double      double _parnd(int,[int])\r
-\r
-\r
-\r
-To  illustrate  simple  parameter  receiving in a C  function  I  offer\r
-the following  simple C function which receives two numeric parameters\r
-from  the calling  Clipper program,  and uses these two numeric\r
-parameters to set  the size of the cursor.\r
-\r
-\r
-     #include <nandef.h>            /* Clipper header files */\r
-     #include <extend.h>\r
-     #include <dos.h>                   /* Header file to define REGS */\r
-     \r
-     CLIPPER s_curset()\r
-     {\r
-          /* Demonstration function to set cursor shape */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 0x01;\r
-          inreg.h.ch = _parni(1);   /* Get integer parameter 1 */\r
-          inreg.h.cl = _parni(2);   /* Get integer parameter 2 */\r
-          int86(0x10,&inreg,&outreg);\r
-          _ret();                        /* Return to Clipper */\r
-     }\r
-\r
-Clipper  provides four more functions for dealing with received\r
-parameters;\r
-\r
-_parclen(int,[int])  which returns the length of a string including\r
-imbedded `\0's,   _parcsiz(int[int])  which returns the length of a\r
-character  string passed by reference from Clipper, _parinfa(int,[int])\r
-which returns the type of  a  specified  array  element  or the length of\r
-an  array,   and  finally _parinfo(int) whic returns the type of a\r
-parameter.\r
-\r
-The following example function uses _parinfa()  to determine both the\r
-length of an array passed from a Clipper program,  and the type of each\r
-element  in the array.  The function then returns to Clipper an integer\r
-representing the number of defined elements in the array.\r
-\r
-\r
-\r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     \r
-     CLIPPER s_alen()\r
-     {\r
-          int total;\r
-          int n;\r
-          int defined;\r
-          int type;\r
-     \r
-          /* Return the number of defined elements in an array */\r
-          /* From Clipper use defined = s_alen(arr) */\r
-     \r
-          total = _parinfa(1,0); /* Get declared number of elements in\r
-     array */\r
-     \r
-          defined = 0;\r
-     \r
-          for (n = 1; n <= total; n++){\r
-               type = _parinfa(1,n);   /* Get array parameter type */\r
-               if (type)\r
-                    defined++;\r
-          }\r
-          _retni(defined);              /* Return an integer to Clipper\r
-     */\r
-     }\r
-     \r
-\r
-This  function  goes  one step further to return the  mean  average  of\r
-all numeric  values  in an array.  Notice the use of _parnd()  to\r
-retrieve  the numeric  values as doubles.  You may find that because of\r
-the floating point arithmetic  in  this  function  that it will  only\r
-work  if  compiled  with Microsoft C.\r
-\r
-\r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     \r
-     CLIPPER s_aave()\r
-     {\r
-          int total;\r
-          int defined;\r
-          int n;\r
-          int type;\r
-          double sum;\r
-     \r
-          /* Return the mean average value of numbers in array */\r
-          /* From Clipper use mean = s_aave(arr)\r
-     \r
-     \r
-          total = _parinfa(1,0);               /* Get declared number of\r
-     elements */\r
-     \r
-          defined = 0;\r
-     \r
-          for (n = 1; n <= total; n++){    /* Determine number of defined\r
-     */\r
-               type = _parinfa(1,n);            /* elements */\r
-               if (type == 2)\r
-                    defined++;\r
-          }\r
-     \r
-          sum = 0;\r
-     \r
-          for (n = 1; n <= total; n++){\r
-               type = _parinfa(1,n);\r
-               if (type == 2)                  /* Only sum numeric values\r
-     */\r
-                    sum += _parnd(1,n);\r
-          }\r
-          _retnd(sum / defined);               /* Return a double to\r
-     Clipper */\r
-     }\r
-     \r
-     \r
-\r
-Returning Values\r
-\r
-The  Clipper  manual  lists seven functions for returning  from  a\r
-function written in another language. These return functions for C are as\r
-follows;\r
-\r
-  character        _retc(char *)\r
-  date        _retds(char *)\r
-  logical     _retl(int)\r
-  numeric (int)    _retni(int)\r
-  numeric (long)   _retnl(long)\r
-  numeric  (double)     _retnd(double)\r
-  nothing     _ret(void)\r
-\r
-Omitted  from  the  Clipper manual is the information that  you  may\r
-return different types of value back from a function! For example,  you\r
-may wish to return a character string under normal circumstances,  fine\r
-use _retc().  On error occurences however you can return a logical using\r
-_retl(). The Clipper program  will  assign the received value to the\r
-receiving  variable  in  the correct manner.\r
-\r
-The following simple C function returns a random number.  Notice the use\r
-of integers  which  limits  the range of the function to +-32767.   For\r
-larger values you should use longs instead of integers.\r
-\r
-\r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <dos.h>\r
-     \r
-     CLIPPER s_random()\r
-     {\r
-          /* Returns a random number between 0 and param1 - 1 */\r
-          /* From Clipper use x = s_random(param1) */\r
-     \r
-          int param1;\r
-          int x;\r
-     \r
-          param1 = _parni(1);\r
-     \r
-          x = rand() % param1;\r
-          _retni(x);\r
-     }\r
-     \r
-\r
-This function receives a string from Clipper,  and passes back an upper\r
-case copy of the string,  leaving the original unchanged.  The maximum\r
-length  of the  string  which can be processed is determined by the size\r
-of  target[], here set to 5000 characters.\r
-\r
-\r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     \r
-     CLIPPER s_upper()\r
-     {\r
-          /* Returns an upper case copy of string */\r
-          /* From Clipper use ? s_upper("this is a string")\r
-     \r
-          char *p;\r
-          char *q;\r
-          char *string;\r
-          char target[5000];\r
-          int n;\r
-     \r
-          string = _parc(1);\r
-     \r
-          p = string;\r
-          q = target;\r
-     \r
-          while(*string){\r
-               *q++ = toupper(*string);\r
-               string++;\r
-          }\r
-          *q = '\0';\r
-          string = p;\r
-          _retc(target);\r
-     }\r
-     \r
-     \r
-This  function  may be used to change the current DOS directory.  If  it\r
-is successful  it  returns .T.  to the calling Clipper program,\r
-otherwise  it returns .F.\r
-\r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <dos.h>\r
-     \r
-     CLIPPER s_chdir()\r
-     {\r
-          /* Attempts to change the current DOS directory */\r
-          /* From Clipper use result = s_chdir(path) */\r
-     \r
-          union REGS inreg,outreg;\r
-          struct SREGS segreg;\r
-     \r
-          char *path;\r
-          int x;\r
-     \r
-          path = _parc(1);              /* Retrieve string from Clipper\r
-     */\r
-     \r
-          inreg.h.ah = 0x3b;\r
-          segreg.ds = FP_SEG(path);\r
-          inreg.x.dx = FP_OFF(path);\r
-          intdosx(&inreg,&outreg,&segreg);\r
-     \r
-          x = outreg.x.ax;\r
-     \r
-          if (x == 3)\r
-               _retl(0);    /* Return logical .F. back to Clipper */\r
-          else\r
-               _retl(1);    /* Return logical .T. back to Clipper */\r
-     }\r
-     \r
-\r
-\r
-Avoiding Unresolved Externals\r
-\r
-As we have already seen, a common problem plaguing the programmer\r
-interfacing C functions with Clipper programs is Unresolved Externals.\r
-\r
-The  following  example  C function called s_print()   will  not  link\r
-with Clipper.\r
-\r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <stdio.h>\r
-     \r
-     CLIPPER s_print()\r
-     {\r
-          char *x;\r
-     \r
-          x = _parc(1);\r
-     \r
-          printf("\nI received %s from Clipper.\n",x);\r
-     \r
-          _ret();\r
-     }\r
-     \r
-\r
-The linker gives you the following reply;\r
-\r
-     Microsoft (R) Overlay Linker  Version 3.65\r
-     Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.\r
-     \r
-     M:SLIB.LIB(IOERROR) : error L2025: __doserrno : symbol defined more\r
-     than once\r
-      pos: 16C6F Record type: 53C6\r
-     \r
-     LINK : error L2029: Unresolved externals:\r
-     \r
-     \r
-\r
-     __RealCvtVector in file(s):\r
-      M:SLIB.LIB(REALCVT)\r
-     _abort in file(s):\r
-      M:SLIB.LIB(CVTFAK)\r
-     \r
-     There were 3 errors detected\r
-     \r
-\r
-The error L2025 `symbol defined more than once' can in this case be\r
-ignored.  However,   the unresolved externals `RealCvtVector'  and\r
-`abort'  cannot  be ignored.  These two functions are referenced by the\r
-function printf()  which has  been  included in the C function.  The\r
-answer is to use as few  of  the compiler's  run time library functions\r
-as possible,  use ROM  calls  instead with INT86() and INTDOSX() etc.\r
-\r
-\r
-Adding High Resolution Graphics To Clipper With C\r
-\r
-The most annoying omission from Clipper, in my opinion, is the lack of\r
-high resolution graphics facilities. The following functions, written in\r
-Turbo C, provide high resolution graphics to Clipper.\r
-\r
-First we require a means to change the video display mode to a high\r
-resolution graphics mode, and back to text mode. The IBM PC BIOS provides\r
-the means for this and can be called from C as follows;\r
-\r
-\r
-\r
-\r
-     /*         Servile Software Library For Clipper              */\r
-     \r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <dos.h>\r
-     \r
-     CLIPPER s_smode()\r
-     {\r
-          /* Set Video Mode */\r
-          /* From Clipper use s_smode(mode) */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.al = _parni(1);\r
-          inreg.h.ah = 0x00;\r
-          int86 (0x10, &inreg, &outreg);\r
-     \r
-     \r
-     /*  1 40x25 colour text\r
-          2 40x25 bw text\r
-          3 80x25 colour text\r
-          4 320x200 4 colour graphics\r
-          5 320x200 4 colour graphics colour burst off\r
-          6 640x200 2 colour graphics\r
-          etc\r
-     */\r
-          _ret();\r
-     }\r
-\r
-Having set the computer into graphics mode, how about setting pixels to a\r
-specified colour?\r
-\r
-     \r
-     /*         Servile Software Library For Clipper              */\r
-     \r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <dos.h>\r
-     \r
-     CLIPPER s_plot()\r
-     {\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          /* Sets a pixel at the specified coordinates to the specified\r
-     colour. */\r
-     \r
-          inreg.h.bh = 0x00;\r
-          inreg.x.cx = _parni(1);\r
-          inreg.x.dx = _parni(2);\r
-          inreg.h.al = _parni(3);\r
-          inreg.h.ah = 0x0C;\r
-          int86(0x10, &inreg, &outreg);\r
-     }\r
-     \r
-Line drawing and circles are handled by these two functions;\r
-\r
-     \r
-     /*         Servile Software Library For Clipper              */\r
-     \r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <dos.h>\r
-     \r
-     CLIPPER s_line()\r
-     {\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          /* Draws a straight line from (a,b) to (c,d) in colour col */\r
-     \r
-          int a;\r
-          int b;\r
-          int c;\r
-          int d;\r
-          int col;\r
-          int u;\r
-          int v;\r
-          int d1x;\r
-          int d1y;\r
-          int d2x;\r
-          int d2y;\r
-          int m;\r
-          int n;\r
-          int s;\r
-          int i;\r
-     \r
-          a = _parni(1);\r
-          b = _parni(2);\r
-          c = _parni(3);\r
-          d = _parni(4);\r
-          col = _parni(5);\r
-     \r
-          u = c - a;\r
-          v = d - b;\r
-          if (u == 0)\r
-          {\r
-               d1x = 0;\r
-               m = 0;\r
-          }\r
-          else\r
-          {\r
-               m = abs(u);\r
-               if (u < 0)\r
-                    d1x = -1;\r
-               else\r
-                    if (u > 0)\r
-                         d1x = 1;\r
-          }\r
-          if ( v == 0)\r
-          {\r
-               d1y = 0;\r
-               n = 0;\r
-          }\r
-          else\r
-          {\r
-               n = abs(v);\r
-               if (v < 0)\r
-                    d1y = -1;\r
-               else\r
-                    if (v > 0)\r
-                         d1y = 1;\r
-          }\r
-          if (m > n)\r
-          {\r
-               d2x = d1x;\r
-               d2y = 0;\r
-          }\r
-          else\r
-          {\r
-               d2x = 0;\r
-               d2y = d1y;\r
-               m = n;\r
-               n = abs(u);\r
-          }\r
-          s = (m / 2);\r
-     \r
-          inreg.h.al = (unsigned char)col;\r
-          inreg.h.bh = 0x00;\r
-          inreg.h.ah = 0x0C;\r
-          for (i = 0; i <= m; i++)\r
-          {\r
-               inreg.x.cx = (unsigned int)(a);\r
-               inreg.x.dx = (unsigned int)(b);\r
-               int86(0x10, &inreg, &outreg);\r
-               s += n;\r
-               if (s >= m)\r
-               {\r
-                    s -= m;\r
-                    a += d1x;\r
-                    b += d1y;\r
-               }\r
-               else\r
-               {\r
-                    a += d2x;\r
-                    b += d2y;\r
-               }\r
-          }\r
-     }\r
-     \r
-     \r
-\r
-This circle drawing function uses in-line assembler to speed up the\r
-drawing process. It can easily be replaced with inreg and outreg\r
-parameters as in the other functions, or the other functions can be\r
-changed to in-line assembler.  Both methods are shown to illustrate\r
-different ways of achieving the same result.\r
-\r
-\r
-     /*         Servile Software Library For Clipper              */\r
-     \r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <dos.h>\r
-     \r
-     \r
-     void plot(int x, int y, unsigned char colour)\r
-     {\r
-          asm mov al , colour;\r
-          asm mov bh , 00;\r
-          asm mov cx , x;\r
-          asm mov dx , y;\r
-          asm mov ah , 0Ch;\r
-          asm int 10h;\r
-     }\r
-     \r
-     int getmode()\r
-     {\r
-          /* Returns current video mode  and number of columns in ncols\r
-     */\r
-     \r
-          asm mov ah , 0Fh;\r
-          asm int 10h;\r
-          return(_AL);\r
-     }\r
-     \r
-     \r
-     CLIPPER s_circle()\r
-     {\r
-          int x_centre;\r
-          int y_centre;\r
-          int radius;\r
-          int colour;\r
-          int x,y,delta;\r
-          int startx,endx,x1,starty,endy,y1;\r
-          int asp_ratio;\r
-     \r
-          x_centre = _parni(1);\r
-          y_centre = _parni(2);\r
-          radius = _parni(3);\r
-          colour = _parni(4);\r
-     \r
-     \r
-     \r
-     \r
-          if (getmode() == 6)\r
-               asp_ratio = 22;\r
-          else\r
-               asp_ratio = 13;\r
-     \r
-          y = radius;\r
-          delta = 3 - 2 * radius;\r
-     \r
-          for(x = 0; x < y; )\r
-          {\r
-               starty = y * asp_ratio / 10;\r
-               endy = (y + 1) * asp_ratio / 10;\r
-               startx = x * asp_ratio / 10;\r
-               endx = (x + 1) * asp_ratio / 10;\r
-     \r
-               for(x1 = startx; x1 < endx; ++x1)\r
-               {\r
-                    plot(x1+x_centre,y+y_centre,colour);\r
-                    plot(x1+x_centre,y_centre - y,colour);\r
-                    plot(x_centre - x1,y_centre - y,colour);\r
-                    plot(x_centre - x1,y + y_centre,colour);\r
-               }\r
-     \r
-               for(y1 = starty; y1 < endy; ++y1)\r
-               {\r
-                    plot(y1+x_centre,x+y_centre,colour);\r
-                    plot(y1+x_centre,y_centre - x,colour);\r
-                    plot(x_centre - y1,y_centre - x,colour);\r
-                    plot(x_centre - y1,x + y_centre,colour);\r
-               }\r
-     \r
-               if (delta < 0)\r
-                    delta += 4 * x + 6;\r
-               else\r
-               {\r
-                    delta += 4*(x-y)+10;\r
-                    y--;\r
-               }\r
-               x++;\r
-          }\r
-     \r
-     \r
-     \r
-     \r
-          if(y)\r
-          {\r
-               starty = y * asp_ratio / 10;\r
-               endy = (y + 1) * asp_ratio / 10;\r
-               startx = x * asp_ratio / 10;\r
-               endx = (x + 1) * asp_ratio / 10;\r
-               for(x1 = startx; x1 < endx; ++x1)\r
-               {\r
-                    plot(x1+x_centre,y+y_centre,colour);\r
-                    plot(x1+x_centre,y_centre - y,colour);\r
-                    plot(x_centre - x1,y_centre - y,colour);\r
-                    plot(x_centre - x1,y + y_centre,colour);\r
-               }\r
-     \r
-               for(y1 = starty; y1 < endy; ++y1)\r
-               {\r
-                    plot(y1+x_centre,x+y_centre,colour);\r
-                    plot(y1+x_centre,y_centre - x,colour);\r
-                    plot(x_centre - y1,y_centre - x,colour);\r
-                    plot(x_centre - y1,x + y_centre,colour);\r
-               }\r
-          }\r
-     }\r
-     \r
-\r
-The Clipper facilities for displaying text on the screen, @....SAY and ?\r
-do not work when the monitor is in graphics mode. You then need the\r
-following function to allow text to be displayed in a graphics mode;\r
-\r
-     \r
-     /*         Servile Software Library For Clipper              */\r
-     \r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <dos.h>\r
-     \r
-     int sgetmode(int *ncols)\r
-     {\r
-          /* Returns current video mode  and number of columns in ncols\r
-     */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 0x0F;\r
-          int86(0x10, &inreg, &outreg);\r
-          *ncols = outreg.h.ah;\r
-          return(outreg.h.al);\r
-     }\r
-     \r
-     void at(int row, int col)\r
-     {\r
-          asm mov bh , 0;\r
-          asm mov dh , row;\r
-          asm mov dl , col;\r
-          asm mov ah , 02h;\r
-          asm int 10h;\r
-     }\r
-     \r
-     \r
-     \r
-     \r
-     CLIPPER s_say()\r
-     {\r
-          char *output;\r
-          int p = 0;\r
-          unsigned char page;\r
-          unsigned char text;\r
-          int n;\r
-          int r;\r
-          int c;\r
-          int attribute;\r
-     \r
-          output = _parc(1);\r
-          r = _parni(2);\r
-          c = _parni(3);\r
-          attribute = _parni(4);\r
-     \r
-          asm mov ah , 0Fh;\r
-          asm int 10h;\r
-          asm mov page, bh;\r
-     \r
-          sgetmode(&n);\r
-     \r
-          at(r,c);\r
-     \r
-          while (output[p])\r
-          {\r
-               text = output[p++];\r
-               asm mov bh , page;\r
-               asm mov bl , attribute;\r
-               asm mov cx , 01h;\r
-               asm mov ah , 09h;\r
-               asm mov al , text;\r
-               asm int 10h;\r
-               c++;\r
-               if (c < (n-1))\r
-                    at( r, c);\r
-               else\r
-               {\r
-                    c = 0;\r
-                    at(++r,0);\r
-               }\r
-          }\r
-     }\r
-     \r
-\r
-\r
-\r
-When drawing graphs, it is often required to fill in areas of the graph\r
-in different patterns. This is a graphics function to fill boundered\r
-shapes with a specified hatching pattern providing a means to achieve\r
-more usable graphs;\r
-\r
-\r
-     \r
-     /*         Servile Software Library For Clipper              */\r
-     \r
-     #include <nandef.h>\r
-     #include <extend.h>\r
-     #include <dos.h>\r
-     \r
-     int pixset(int x, int y)\r
-     {\r
-          /* Returns the colour of the specified pixel */\r
-     \r
-          asm mov cx ,x;\r
-          asm mov dx ,y;\r
-          asm mov ah ,0Dh;\r
-          asm int 10h;\r
-          return(_AL);\r
-     }\r
-     \r
-     CLIPPER s_fill()\r
-     {\r
-          /* Fill a boundered shape using a hatch pattern */\r
-     \r
-          int mode;\r
-          int xa;\r
-          int ya;\r
-          int bn;\r
-          int byn;\r
-          int x;\r
-          int y;\r
-          int col;\r
-          int pattern;\r
-          int maxx;\r
-          int maxy;\r
-          int hatch[10][8] = { 255,255,255,255,255,255,255,255,\r
-                                   128,64,32,16,8,4,2,1,\r
-                                   1,2,4,8,16,32,64,128,\r
-                                   1,2,4,8,8,4,2,1,\r
-                                   238,238,238,238,238,238,238,238,\r
-                                   170,85,170,85,170,85,170,85,\r
-                                   192,96,48,24,12,6,3,1,\r
-                                   62,62,62,0,227,227,227,0,\r
-                                   129,66,36,24,24,36,66,129,\r
-                                   146,36,146,36,146,36,146,36};\r
-     \r
-          /* Patterns for fill, each integer describes a row of dots */\r
-     \r
-     \r
-     \r
-     \r
-          x = _parni(1);\r
-          y = _parni(2);\r
-          col = _parni(3);\r
-          pattern = _parni(4);\r
-     \r
-          mode = getmode();\r
-     \r
-          switch(mode)\r
-          {\r
-               case 0:\r
-               case 1:\r
-               case 2:\r
-               case 3: break;\r
-               case 4:\r
-               case 9:\r
-               case 13:\r
-               case 19:\r
-               case 5: maxx = 320;\r
-                         maxy = 200;\r
-                         break;\r
-               case 14:\r
-               case 10:\r
-               case 6: maxx = 640;\r
-                         maxy = 200;\r
-                         break;\r
-               case 7: maxx = 720;\r
-                         maxy = 400;\r
-                         break;\r
-               case 8: maxx = 160;\r
-                         maxy = 200;\r
-                         break;\r
-               case 15:\r
-               case 16: maxx = 640;\r
-                          maxy = 350;\r
-                          break;\r
-               case 17:\r
-               case 18: maxx = 640;\r
-                          maxy = 480;\r
-                          break;\r
-     \r
-          }\r
-     \r
-          xa = x;\r
-          ya = y;  /* Save Origin */\r
-     \r
-          if(pixset(x,y))\r
-               return;\r
-     \r
-          bn = 1;\r
-          byn = 0;\r
-     \r
-     \r
-     \r
-     \r
-          do\r
-          {\r
-               if (hatch[pattern][byn] != 0)\r
-               {  /* If blank ignore */\r
-                    do\r
-                    {\r
-                         if ((bn & hatch[pattern][byn]) == bn)\r
-                         {\r
-                              asm mov al , col;\r
-                              asm mov bh , 00;\r
-                              asm mov cx , x;\r
-                              asm mov dx , y;\r
-                              asm mov ah , 0Ch;\r
-                              asm int 10h;\r
-                         }\r
-                         x--;\r
-                         bn <<= 1;\r
-                         if (bn > 128)\r
-                              bn = 1;\r
-                    }\r
-                    while(!pixset(x,y) && (x > -1));\r
-     \r
-                    x = xa + 1;\r
-                    bn = 128;\r
-     \r
-                    do\r
-                    {\r
-                         if ((bn & hatch[pattern][byn]) == bn)\r
-                         {\r
-                              asm mov al , col;\r
-                              asm mov bh , 00;\r
-                              asm mov cx , x;\r
-                              asm mov dx , y;\r
-                              asm mov ah , 0Ch;\r
-                              asm int 10h;\r
-                         }\r
-                         x++;\r
-                         bn >>=1;\r
-                         if (bn <1)\r
-                              bn = 128;\r
-                    }\r
-                    while((!pixset(x,y)) && (x <= maxx));\r
-               }\r
-               x = xa;\r
-               y--;\r
-               bn = 1;\r
-               byn++;\r
-               if (byn > 7)\r
-                    byn = 0;\r
-     \r
-     \r
-     \r
-     \r
-          }\r
-          while(!pixset(x,y) && ( y > -1));\r
-     \r
-          /* Now travel downwards */\r
-     \r
-          y = ya + 1;\r
-     \r
-          byn = 7;\r
-          bn = 1;\r
-          do\r
-          {\r
-               /* Travel left */\r
-               if (hatch[pattern][byn] !=0)\r
-               {\r
-                    do\r
-                    {\r
-                         if ((bn & hatch[pattern][byn]) == bn)\r
-                         {\r
-                              asm mov al , col;\r
-                              asm mov bh , 00;\r
-                              asm mov cx , x;\r
-                              asm mov dx , y;\r
-                              asm mov ah , 0Ch;\r
-                              asm int 10h;\r
-                         }\r
-                         x--;\r
-                         bn <<= 1;\r
-                         if (bn > 128)\r
-                              bn = 1;\r
-                    }\r
-                    while(!pixset(x,y) && (x > -1));\r
-     \r
-                    /* Back to x origin */\r
-                    x = xa + 1 ;\r
-                    bn = 128;\r
-     \r
-                    /* Travel right */\r
-                    do\r
-                    {\r
-                         if ((bn & hatch[pattern][byn]) == bn)\r
-                         {\r
-                              asm mov al , col;\r
-                              asm mov bh , 00;\r
-                              asm mov cx , x;\r
-                              asm mov dx , y;\r
-                              asm mov ah , 0Ch;\r
-                              asm int 10h;\r
-                         }\r
-                         x++;\r
-                         bn >>=1;\r
-     \r
-     \r
-     \r
-     \r
-                         if (bn <1)\r
-                              bn = 128;\r
-                    }\r
-                    while((!pixset(x,y)) && (x <= maxx));\r
-               }\r
-               x = xa;\r
-               bn = 1;\r
-               y++;\r
-               byn--;\r
-               if (byn < 0)\r
-                    byn = 7;\r
-          }\r
-          while((!pixset(x,y)) && (y <= maxy));\r
-     }\r
-     \r
-     \r
-     \r
-                                    \r
-                       SPELL - AN EXAMPLE PROGRAM\r
-\r
-It has been said that example programs provide a good way of learning a\r
-new computer language. On that basis the following simple program is\r
-offered as an example of making use of the dynamic memory allocation\r
-provided by DOS.\r
-\r
-This is a spell checker for ASCII (text) documents, written in, and\r
-making use of Borland's Turbo C text graphics facilities for displaying\r
-windows of text.\r
-\r
-\r
-     /* Spell checker for ascii documents */\r
-     /* Compile with -mc (compact memory model) and unsigned characters\r
-     */\r
-     \r
-     \r
-     #include <stdio.h>\r
-     #include <conio.h>\r
-     #include <fcntl.h>\r
-     #include <io.h>\r
-     #include <dos.h>\r
-     #include <string.h>\r
-     #include <alloc.h>\r
-     #include <ctype.h>\r
-     #include <stdlib.h>\r
-     #include <bios.h>\r
-     #include <dir.h>\r
-     #include <stat.h>\r
-     \r
-     #define  ON           0x06\r
-     #define  OFF         0x20\r
-     #define  MAXIMUM     15000\r
-     #define  WORDLEN     20\r
-     #define  LEFTMARGIN  1\r
-     \r
-     union REGS inreg,outreg;\r
-     \r
-     char *dicname;\r
-     char *dic[MAXIMUM];      /* Array of text lines */\r
-     char word[31];\r
-     char comp[31];\r
-     char fname[160];\r
-     int lastelem;\r
-     char changed;\r
-     char *ignore[100];\r
-     int lastign;\r
-     int insert;\r
-     int n;\r
-     int bp;\r
-     int mp;\r
-     int tp;\r
-     int result;\r
-     \r
-     \r
-     \r
-     \r
-     char *text;\r
-     char *textsav;\r
-     \r
-     void AT(int, int);\r
-     void BANNER(void);\r
-     int COMPARE(void);\r
-     void CORRECT(void);\r
-     void FATAL(char *);\r
-     void FILERR(char *);\r
-     void GETDIC(void);\r
-     void IGNORE(void);\r
-     void INSERT(void);\r
-     int MATCHSTR(char *, char *);\r
-     void SPELL(void);\r
-     void UPDATE(void);\r
-     \r
-     void CURSOR(char status)\r
-     {\r
-          /* Toggle cursor display on and off */\r
-     \r
-          union REGS inreg,outreg;\r
-     \r
-          inreg.h.ah = 1;\r
-          inreg.h.ch = (unsigned char)status;\r
-          inreg.h.cl = 7;\r
-          int86(0x10,&inreg,&outreg);\r
-     }\r
-     \r
-     void DISPLAY(char *text)\r
-     {\r
-          /* Display 'text' expanding tabs and newline characters */\r
-     \r
-          while(*text)\r
-          {\r
-               switch(*text)\r
-               {\r
-                    case '\n':  cputs("\r\n");\r
-                                   break;\r
-                    case '\t':  cputs("      ");\r
-                                   break;\r
-                    default:  putch(*text);\r
-               }\r
-               text++;\r
-          }\r
-     }\r
-     \r
-     \r
-     \r
-     \r
-     void GETDIC()\r
-     {\r
-          /* Read dictionary into memory */\r
-     \r
-          FILE *fp;\r
-          char *p;\r
-          int poscr;\r
-          int handle;\r
-     \r
-          window(1,22,80,24);\r
-          clrscr();\r
-          gotoxy(28,2);\r
-          cprintf("Reading Dictionary....");\r
-     \r
-          changed = 0;\r
-          lastelem = 0;\r
-     \r
-          dicname = searchpath("spell.dic");\r
-          handle = open(dicname,O_RDWR);\r
-          if (handle < 0)\r
-               FILERR("spell.dic");\r
-     \r
-          fp = fdopen(handle,"r");\r
-          if (fp == NULL)\r
-               FILERR("spell.dic");\r
-     \r
-          do\r
-          {\r
-               dic[lastelem] = calloc(WORDLEN,1);\r
-               if (dic[lastelem])\r
-               {\r
-                    p = fgets(dic[lastelem],79,fp);\r
-                    /* Remove carriage return from end of text line */\r
-                    poscr = (int)strlen(dic[lastelem]) - 1;\r
-                    if (dic[lastelem][poscr] == '\n')\r
-                         dic[lastelem][poscr] = 0;\r
-               }\r
-               else\r
-                    FATAL("Unable To Allocate Memory");\r
-          }\r
-          while((p != NULL) && (lastelem++ < MAXIMUM));\r
-     \r
-          lastelem--;\r
-     \r
-          fclose(fp);\r
-     }\r
-     \r
-     \r
-     \r
-     \r
-     void UPDATE()\r
-     {\r
-          FILE *fp;\r
-          int n;\r
-     \r
-          if (changed)\r
-          {\r
-               window(1,22,80,24);\r
-               clrscr();\r
-               gotoxy(27,2);\r
-               cprintf("Updating Dictionary....");\r
-     \r
-               fp = fopen(dicname,"w+");\r
-               if (fp == NULL)\r
-                    FILERR("spell.dic");\r
-     \r
-               for(n = 0; n <= lastelem; n++)\r
-                    fprintf(fp,"%s\n",dic[n]);\r
-     \r
-               fclose(fp);\r
-          }\r
-     }\r
-     \r
-     void IGNORE()\r
-     {\r
-          /* Add a word to the ignore table */\r
-     \r
-          if (lastign < 100)\r
-          {\r
-               ignore[lastign] = calloc(strlen(word) + 1,1);\r
-               if (ignore[lastign])\r
-                    strcpy(ignore[lastign++],comp);\r
-               else\r
-               {\r
-                    clrscr();\r
-                    cprintf("No available memory for new words!\r\nPress\r
-     A key....");\r
-                    bioskey(0);\r
-               }\r
-           }\r
-           else\r
-           {\r
-               clrscr();\r
-               cprintf("No available memory for new words!\r\nPress A\r
-     key....");\r
-               bioskey(0);\r
-           }\r
-     }\r
-     \r
-     \r
-     \r
-     \r
-     void FATAL(char *text)\r
-     {\r
-          /* Fatal error drop out */\r
-     \r
-          textcolor(LIGHTGRAY);\r
-          textbackground(BLACK);\r
-          window(1,1,80,25);\r
-          clrscr();\r
-          printf("SERVILE SOFTWARE\n\nSPELL V1.7\nFATAL ERROR:\r
-     %s\n\n",text);\r
-          CURSOR(ON);\r
-          exit(0);\r
-     }\r
-     \r
-     void FILERR(char *fname)\r
-     {\r
-          char text[60];\r
-     \r
-          strcpy(text,"Unable To Access: ");\r
-          strcat(text,fname);\r
-          FATAL(text);\r
-     }\r
-     \r
-     int COMPARE()\r
-     {\r
-          char **p;\r
-     \r
-          /* Check Ignore table */\r
-          for(p = ignore; p <= &ignore[lastign]; p++)\r
-               if (strcmp(comp,*p) == 0)\r
-                    return(1);\r
-     \r
-          /* Binary search of dictionary file */\r
-          bp = 0;\r
-          tp = lastelem;\r
-          mp = (tp + bp) / 2;\r
-     \r
-     \r
-     \r
-     \r
-          while((result = strcmp(dic[mp],comp)) != 0)\r
-          {\r
-               if (mp >= tp)\r
-               {\r
-                    /* Not found! */\r
-                    insert = mp;\r
-                    if (result > 0)\r
-                         insert--;\r
-                    return(0);\r
-               }\r
-               if (result < 0)\r
-                    bp = mp + 1;\r
-               else\r
-                    tp = mp - 1;\r
-     \r
-               mp = (bp + tp) / 2;\r
-          }\r
-          return(1);\r
-     }\r
-     \r
-     void INSERT()\r
-     {\r
-          int n;\r
-     \r
-          changed = 1;\r
-          lastelem++;\r
-          n = lastelem;\r
-     \r
-          dic[n] = calloc(WORDLEN,1);\r
-     \r
-          if (dic[n] == NULL)\r
-          {\r
-               clrscr();\r
-               cprintf("No available memory for new words!\r\nPress A\r
-     key....");\r
-               bioskey(0);\r
-               free(dic[n]);\r
-               lastelem--;\r
-               return;\r
-          }\r
-     \r
-          while(n > (insert + 1))\r
-          {\r
-               strcpy(dic[n],dic[n-1]);\r
-               n--;\r
-          };\r
-     \r
-          strcpy(dic[insert + 1],comp);\r
-     }\r
-     \r
-     \r
-     \r
-     \r
-     void SPELL()\r
-     {\r
-          FILE *target;\r
-          FILE *source;\r
-          char *p;\r
-          char *x;\r
-          char temp[256];\r
-          char dat1[1250];\r
-          char dat2[1250];\r
-          int c;\r
-          int m;\r
-          int found;\r
-          int curpos;\r
-          int key;\r
-          int row;\r
-          int col;\r
-          int srow;\r
-          int scol;\r
-     \r
-          window(1,1,80,20);\r
-          textcolor(BLACK);\r
-          textbackground(WHITE);\r
-     \r
-          /* Open temporary file to take spell checked copy */\r
-          target = fopen("spell.$$$","w+");\r
-     \r
-          source = fopen(fname,"r");\r
-     \r
-          if (source == NULL)\r
-               FILERR(fname);\r
-     \r
-          lastign = 0;\r
-     \r
-          do\r
-          {\r
-               clrscr();\r
-     \r
-               text = dat1;\r
-     \r
-               p = text;\r
-     \r
-               textsav = dat2;\r
-     \r
-               strcpy(text,"");\r
-     \r
-               /* Display read text */\r
-               row = wherey();\r
-               col = wherex();\r
-     \r
-     \r
-     \r
-     \r
-               for(m = 0; m < 15; m++)\r
-               {\r
-                    x = fgets(temp,200,source);\r
-                    if (x)\r
-                    {\r
-                         strcat(text,temp);\r
-                         DISPLAY(temp);\r
-                    }\r
-                    if (wherey() > 18)\r
-                         break;\r
-               }\r
-     \r
-               /* return cursor to start position */\r
-               gotoxy(col,row);\r
-     \r
-               do\r
-               {\r
-                    memset(word,32,30);\r
-                    curpos = 0;\r
-                    do\r
-                    {\r
-                         c = *text++;\r
-                         if ((isalpha(c)) || (c == '-') && (curpos != 0))\r
-                              word[curpos++] = c;\r
-                    }\r
-                    while(((isalpha(c)) || (c == '-') && (curpos != 0))\r
-                            && (curpos < 30));\r
-                    word[curpos] = 0;\r
-                    strcpy(comp,word);\r
-                    strupr(comp);\r
-     \r
-                    if (*comp != 0)\r
-                    {\r
-                         found = COMPARE();\r
-                         if (!found){\r
-                              textbackground(RED);\r
-                              textcolor(WHITE);\r
-                         }\r
-                    }\r
-                    else\r
-                         found = 1;\r
-     \r
-                    srow = wherey();\r
-                    scol = wherex();\r
-     \r
-                    cputs(word);\r
-                    textbackground(WHITE);\r
-                    textcolor(BLACK);\r
-     \r
-     \r
-     \r
-     \r
-     \r
-                    switch(c)\r
-                    {\r
-                         case '\n': cputs("\r\n");\r
-                                      break;\r
-                         case '\t': cputs("       ");\r
-                                      break;\r
-                         default: putch(c);\r
-                    }\r
-     \r
-                    row = wherey();\r
-                    col = wherex();\r
-     \r
-                    if (!found)\r
-                    {\r
-                         window(1,22,80,24);\r
-                         clrscr();\r
-                         cputs("Unknown word ");\r
-                         textcolor(BLUE);\r
-                         cprintf("%s ",word);\r
-                         textcolor(BLACK);\r
-                         cputs("[A]dd  [I]gnore  [C]orrect  [S]kip");\r
-                         do\r
-                         {\r
-                              key = toupper(getch());\r
-                              if (key == 27)\r
-                                   key = 'Q';\r
-                         }\r
-                         while(strchr("AICSQ",key) == NULL);\r
-     \r
-                         switch(key)\r
-                         {\r
-                              case 'A':INSERT();\r
-                                         break;\r
-     \r
-                              case 'C':CORRECT();\r
-                                         break;\r
-     \r
-                              case 'I':IGNORE();\r
-                                         break;\r
-                         }\r
-     \r
-     \r
-     \r
-     \r
-                         if (key == 'C')\r
-                         {\r
-                              clrscr();\r
-                              gotoxy(1,1);\r
-                              strcpy(textsav,--text);\r
-                              /* Delete old word */\r
-                              text -= strlen(comp);\r
-                              *text = 0;\r
-                              /* Insert new word */\r
-                              strcat(text,word);\r
-                              /* Append remainder of text */\r
-                              strcat(text,textsav);\r
-                              text += strlen(word);\r
-                              text++;\r
-                              /* Length of text may have changed ! */\r
-                              if (strlen(word) < strlen(comp))\r
-                                   col -= (strlen(comp) - strlen(word));\r
-                              window(1,1,80,20);\r
-                              clrscr();\r
-                              DISPLAY(p);\r
-                         }\r
-                         else\r
-                         {\r
-                              clrscr();\r
-                              gotoxy(29,2);\r
-                              cputs("Checking Spelling....");\r
-                              window(1,1,80,20);\r
-                              gotoxy(scol,srow);\r
-                              cputs(word);\r
-                         }\r
-                         window(1,1,80,20);\r
-                         gotoxy(col,row);\r
-                    }\r
-               }\r
-               while((*text) && (key != 'Q'));\r
-               fprintf(target,"%s",p);\r
-          }\r
-          while((x != NULL) && (key != 'Q'));\r
-     \r
-          window(1,22,80,24);\r
-          clrscr();\r
-          gotoxy(27,2);\r
-          cprintf("Writing Updated File....");\r
-     \r
-     \r
-     \r
-     \r
-          do\r
-          {\r
-               p = fgets(temp,200,source);\r
-               if (p)\r
-                    fprintf(target,"%s",temp);\r
-          }\r
-          while(p);\r
-     \r
-          fclose(target);\r
-          fclose(source);\r
-     \r
-          /* Now transfer spell.$$$ to fname */\r
-          unlink(fname);\r
-          rename("SPELL.$$$",fname);\r
-     }\r
-     \r
-     void CORRECT()\r
-     {\r
-          /* Locate a good match and return word */\r
-     \r
-          char text[51];\r
-          int m;\r
-          int n;\r
-          int key;\r
-     \r
-          window(1,22,80,24);\r
-          clrscr();\r
-          gotoxy(25,2);\r
-          cprintf("Searching For Alternatives....");\r
-     \r
-          /* Remove any pending key strokes from keyboard buffer */\r
-          while(kbhit())\r
-               getch();\r
-     \r
-          for(n = 0; n <= lastelem; n++)\r
-          {\r
-               if (MATCHSTR(dic[n],comp))\r
-               {\r
-                    strcpy(text,dic[n]);\r
-                    if (strlen(word) <= strlen(text))\r
-                    {\r
-                         for (m = 0; m < strlen(word); m++)\r
-                         {\r
-                              if (isupper(word[m]))\r
-                                   text[m] = toupper(text[m]);\r
-                              else\r
-                                   text[m] = tolower(text[m]);\r
-                         }\r
-     \r
-     \r
-     \r
-     \r
-                         for(m = strlen(word); m < strlen(text); m++)\r
-                              if (isupper(word[strlen(word)]))\r
-                                   text[m] = toupper(text[m]);\r
-                              else\r
-                                   text[m] = tolower(text[m]);\r
-                    }\r
-                    else\r
-                    {\r
-                         for (m = 0; m < strlen(text); m++)\r
-                         {\r
-                              if (isupper(word[m]))\r
-                                   text[m] = toupper(text[m]);\r
-                              else\r
-                                   text[m] = tolower(text[m]);\r
-                         }\r
-                    }\r
-                    clrscr();\r
-                    cprintf("Replace ");\r
-                    textcolor(BLUE);\r
-                    cprintf("%s ",word);\r
-                    textcolor(BLACK);\r
-                    cprintf("With ");\r
-                    textcolor(BLUE);\r
-                    cprintf("%s",text);\r
-                    textcolor(BLACK);\r
-                    cprintf(" Yes No Continue");\r
-                    do\r
-                    {\r
-                         key = toupper(getch());\r
-                    }\r
-                    while(strchr("YNC",key) == NULL);\r
-                    if (key == 'Y')\r
-                    {\r
-                         strcpy(word,text);\r
-                         return;\r
-                    }\r
-                    clrscr();\r
-                    gotoxy(25,2);\r
-                    cprintf("Searching For Alternatives....");\r
-     \r
-                    /* Remove any pending key strokes from keyboard\r
-     buffer */\r
-                    while(kbhit())\r
-                         getch();\r
-     \r
-                    if (key == 'C')\r
-                         return;\r
-               }\r
-          }\r
-          clrscr();\r
-          gotoxy(23,2);\r
-     \r
-     \r
-     \r
-     \r
-          cprintf("NO ALTERNATIVES FOUND! (Press a key)");\r
-          bioskey(0);\r
-          return;\r
-     }\r
-     \r
-     \r
-     int MATCHSTR(char *src, char *tgt)\r
-     {\r
-          /* Compare two words and return non zero if they are similar */\r
-     \r
-          int match;\r
-          int result;\r
-          int strsrc;\r
-          int strtgt;\r
-          int longest;\r
-     \r
-          strtgt = strlen(strupr(tgt));\r
-          strsrc = strlen(strupr(src));\r
-     \r
-          longest = max(strtgt,strsrc);\r
-     \r
-          match = 0;\r
-     \r
-          if(strtgt > strsrc)\r
-          {\r
-               for(; *src ; match += (*src++ == *tgt++))\r
-                    ;\r
-          }\r
-          else\r
-          {\r
-               for(; *tgt ; match += (*src++ == *tgt++))\r
-                    ;\r
-          }\r
-     \r
-          result = (match * 100 / longest);\r
-     \r
-          /* result holds percentage similarity */\r
-     \r
-          if (result > 50)\r
-               return(1);\r
-          return(0);\r
-     }\r
-     \r
-     void AT(int row, int col)\r
-     {\r
-          /* Position the text cursor */\r
-          inreg.h.bh = 0;\r
-          inreg.h.dh = row;\r
-          inreg.h.dl = col;\r
-          inreg.h.ah = 0x02;\r
-          int86 (0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     void WRTCHA (unsigned char ch, unsigned char attrib,  int num)\r
-     {\r
-          /* Display a character num times in colour attrib */\r
-          /* via the BIOS */\r
-     \r
-          inreg.h.al = ch;\r
-          inreg.h.bh = 0;\r
-          inreg.h.bl = attrib;\r
-          inreg.x.cx = num;\r
-          inreg.h.ah = 0x09;\r
-          int86 (0x10, &inreg, &outreg);\r
-     }\r
-     \r
-     void SHADE_BLOCK(int left,int top,int right,int bottom)\r
-     {\r
-          int c;\r
-     \r
-          AT(bottom,right);\r
-          WRTCHA(223,56,1);\r
-          AT(top,right);\r
-          WRTCHA('á',7,1);\r
-          for (c = top+1; c < bottom; c++)\r
-          {\r
-               AT(c,right);\r
-               WRTCHA(' ',7,1);\r
-          }\r
-          AT(bottom,left+1);\r
-          WRTCHA('\9a',7,right-left);\r
-     }\r
-     \r
-     \r
-     \r
-     \r
-     void BOX(int l, int t, int r, int b)\r
-     {\r
-          /* Draws a single line box around a described area */\r
-     \r
-          int n;\r
-          char top[81];\r
-          char bottom[81];\r
-          char tolc[5];\r
-          char torc[5];\r
-          char bolc[5];\r
-          char borc[5];\r
-          char hoor[5];\r
-     \r
-          sprintf(tolc,"%c",218);\r
-          sprintf(bolc,"%c",192);\r
-          sprintf(hoor,"%c",196);\r
-          sprintf(torc,"%c",191);\r
-          sprintf(borc,"%c",217);\r
-     \r
-               strcpy(top,tolc);\r
-          strcpy(bottom,bolc);\r
-          for(n = l + 1; n < r; n++)\r
-          {\r
-               strcat(top,hoor);\r
-               strcat(bottom,hoor);\r
-          }\r
-          strcat(top,torc);\r
-          strcat(bottom,borc);\r
-     \r
-          window(1,1,80,25);\r
-               gotoxy(l,t);\r
-          cputs(top);\r
-          for (n = t + 1; n < b; n++)\r
-          {\r
-               gotoxy(l,n);\r
-               putch(179);\r
-               gotoxy(r,n);\r
-               putch(179);\r
-          }\r
-          gotoxy(l,b);\r
-          cputs(bottom);\r
-     }\r
-     \r
-     \r
-     \r
-     \r
-     void BANNER()\r
-     {\r
-          window (2,2,78,4);\r
-          textcolor(BLACK);\r
-          textbackground(GREEN);\r
-          clrscr();\r
-          SHADE_BLOCK(1,1,78,4);\r
-          BOX(2,2,78,4);\r
-          gotoxy(4,3);\r
-          cprintf("Servile Software                SPELL CHECKER V1.7\r
-                     (c)1992");\r
-     }\r
-     \r
-     \r
-     void main(int argc, char *argv[])\r
-     {\r
-          char *p;\r
-          char tmp_name[160];\r
-          char tmp_fname[160];\r
-     \r
-          if (argc != 2)\r
-          {\r
-               puts("\nERROR: Usage is SPELL document");\r
-               exit(1);\r
-          }\r
-          else\r
-               strcpy(fname,argv[1]);\r
-     \r
-          CURSOR(OFF);\r
-     \r
-          GETDIC();\r
-     \r
-          window(1,22,80,24);\r
-          clrscr();\r
-          gotoxy(28,2);\r
-          cprintf("Making Backup File....");\r
-     \r
-          strcpy(tmp_fname,argv[1]);\r
-     \r
-          /* Remove extension from tmp_fname */\r
-          p = strchr(tmp_fname,'.');\r
-          if(p)\r
-               *p = 0;\r
-     \r
-          /* Create backup file name using DOS */\r
-          sprintf(tmp_name,"copy %s %s.!s! > NUL",argv[1],tmp_fname);\r
-     \r
-          system(tmp_name);\r
-     \r
-          window(1,1,80,25);\r
-     \r
-     \r
-     \r
-     \r
-          textcolor(WHITE);\r
-          textbackground(BLACK);\r
-          clrscr();\r
-          gotoxy(29,2);\r
-          cprintf("Checking Spelling....");\r
-     \r
-          SPELL();\r
-     \r
-          UPDATE();\r
-          window(1,1,80,25);\r
-          textcolor(LIGHTGRAY);\r
-          textbackground(BLACK);\r
-          clrscr();\r
-          CURSOR(ON);\r
-     }\r
-     \r
-                                    \r
-                         APPENDIX A - USING LINK\r
-\r
-\r
-\r
-General Syntax:\r
-\r
-     LINK [options] obj[,[exe][,[map][,[lib]]]][;]\r
-\r
-`obj' is a list of object files to be linked. Each obj file name must be\r
-separated by a + or a space. If you do not specify an extension, LINK\r
-will assume .OBJ. `exe' allows you to specify a name for the executable\r
-file. If this file name is ommited, LINK will use the first obj file name\r
-and suffix it with .EXE. `map' is an optional map file name. If you\r
-specify the name `NUL', no map file is produced. `lib' is a list of\r
-library files to link. LINK searches each library file and only links in\r
-modules which are referenced.\r
-\r
-\r
-eg:\r
-\r
-     LINK filea+fileb,myfile,NUL;\r
-\r
-Links .obj files `filea.obj' and `fileb.obj' into .exe file `myfile.exe'\r
-with no map file produced. The ; at the end of the line tells LINK that\r
-there are no more parameters.\r
-\r
-\r
-\r
-Using Overlays\r
-\r
-Overlay .obj modules are specified by encasing the .obj name in\r
-parenthesis in the link line.\r
-\r
-eg:\r
-\r
-     LINK filea + (fileb) + (filec),myfile,NUL;\r
-\r
-Will link filea.obj fileb.obj and filec.obj with modules fileb.obj and\r
-filec.obj as overlay code.\r
-\r
-\r
-Overlay modules must use FAR call/return instructions.\r
-\r
-\r
-Linker Options\r
-\r
-All LINK options commence with a forward slash `/'. Options which accept\r
-a number can accept a decimal number or a hex number prefixed 0X. eg:\r
-0X10 is interpreted as 10h, decimal 16.\r
-\r
-\r
-\r
-Pause during Linking (/PAU)\r
-\r
-     Tells LINK to wait before writing the .exe file to disk. LINK\r
-displays a\r
-     message and waits for you to press enter.\r
-\r
-Display Linker Process Information (/I)\r
-\r
-     Tells LINK to display information about the link process.\r
-\r
-Pack Executable File (/E)\r
-\r
-     Tells LINK to remove sequences of repeated bytes and to optimise the\r
-load-time\r
-     relocation table before creating the executable file. Symbolic debug\r
-     information is stripped out of the file.\r
-\r
-List Public Symbols (/M)\r
-\r
-     Tells LINK to create a list of all public symbols defined in the\r
-     object files\r
-     in the MAP file.\r
-\r
-Include Line Numbers In Map File (/LI)\r
-\r
-     Tells LINK to include line numbers and associated addresses of the\r
-source\r
-     program in the MAP file.\r
-\r
-Preserve Case Sensitivity (/NOI)\r
-\r
-     By default LINK treats uppercase and lowercase letters as the same.\r
-This\r
-     option tells LINK that they are different.\r
-\r
-Ignore Default Libraries (/NOD)\r
-\r
-     Tells LINK not to search any library specified in the object files\r
-to resolve\r
-     external references.\r
-\r
-Controlling Stack Size (/ST:n)\r
-\r
-     Specifies the size of the stack segment where 'n' is the number of\r
-bytes.\r
-\r
-Setting Maximum Allocation Space (/CP:n)\r
-\r
-     Tells LINK to write the parameter 'n' into the exe file header. When\r
-the exe\r
-     file is executed by DOS, 'n' 16 byte paragraphs of memory are\r
-reserved. If 'n'\r
-     is less than the minimum required, it will be set to the minimum.\r
-This option\r
-     is ESSENTIAL to free memory from the program. C programs free memory\r
-     automatically on start-up, assembly language programs which want to\r
-use\r
-     dynamic memory allocation must be linked with this option set to a\r
-minimum.\r
-\r
-Setting Maximum Number Of Segments (/SE:n)\r
-\r
-     Tells LINK how many segments a program is allowed to have. The\r
-default is 128\r
-     but 'n' can be any number between 1 and 3072.\r
-\r
-\r
-Setting Overlay Interrupt (/O:n)\r
-\r
-     Tells LINK which interrupt number will be used for passing control\r
-to\r
-     overlays. The default is 63. Valid values for 'n' are 0 through 255.\r
-\r
-Ordering Segments (/DO)\r
-\r
-     Tells LINK to use DOS segment ordering. This option is also enabled\r
-by the\r
-     MASM directive .DOSSEG.\r
-\r
-Controlling Data Loading (/DS)\r
-\r
-     By default LINK loads all data starting at the low end of the data\r
-segment. At\r
-     run time the DS register is set to the lowest possible address to\r
-allow the\r
-     entire data segment to be used. This option tells LINK to load all\r
-data\r
-     starting at the high end of the data segment.\r
-\r
-Control Exe File Loading (/HI)\r
-\r
-     Tells LINK to place the exe file as high as possible in memory.\r
-\r
-Prepare for Debugging (/CO)\r
-\r
-     Tells LINK to include symbolic debug information for use by\r
-codeview.\r
-\r
-Optimising Far Calls (/F)\r
-\r
-     Tells LINK to translate FAR calls to NEAR calls where possible. This\r
-results\r
-     in faster code.\r
-\r
-Disabling Far Call Optimisation (/NOF)\r
-\r
-     Tells LINK not to translate FAR calls. This option is specified by\r
-default.\r
-\r
-Packing Contiguous Segments (/PAC:n)\r
-\r
-     Tells LINK to group together neighbouring code segments, providing\r
-more\r
-     oportunities for FAR call translation. 'n' specifies the maximum\r
-size of a\r
-     segment. By default 'n' is 65530. This option is only relevant to\r
-obj files\r
-     created using FAR calls.\r
-\r
-Disabling Segment Packing (/NOP)\r
-\r
-     Disables segment packing. This option is specified by default.\r
-\r
-\r
-\r
-Using Response Files\r
-\r
-Linker options and file names may be specified in a response file. Each\r
-file list starting on a new line instead of being separated by a comma.\r
-\r
-eg:\r
-\r
-     filea.obj fileb.obj\r
-     myfile.exe\r
-     NUL\r
-     liba.lib libb.lib\r
-     \r
-A response file is specified to LINK by prefixing the response file name\r
-with '@'.\r
-eg:\r
-\r
-     LINK @response\r
-\r