├── .gitignore ├── ch1 ├── as_i_read │ ├── vars.c │ ├── liftoff.c │ ├── double.c │ ├── ints.c │ ├── digitsum.c │ ├── isvowel.c │ ├── addlist.c │ ├── even_or_odd.c │ ├── simple_io.c │ └── powertab.c ├── exercises │ ├── ex3.c │ ├── ex1.c │ ├── ex4.c │ ├── ex10.c │ ├── ex6.c │ ├── ex7.c │ ├── ex9.c │ ├── ex5.c │ ├── ex8.c │ ├── ex2.c │ ├── ex11.c │ └── ex12.c └── review_questions.md ├── roberts_clang ├── ch2 └── as_i_read │ └── enums.c ├── book_code ├── VERSION ├── unix-xwindows │ ├── glibrary.c │ ├── config.csh │ ├── glibrary.h │ ├── xcompat.h │ ├── random.c │ ├── random.h │ ├── gcalloc.h │ ├── simpio.h │ ├── xcompat.c │ ├── xmanager.h │ ├── README │ ├── Makefile │ ├── exception.c │ ├── genlib.c │ ├── simpio.c │ ├── graphics.h │ ├── strlib.c │ ├── genlib.h │ ├── xdisplay.h │ ├── strlib.h │ ├── exception.h │ ├── xmanager.c │ ├── extgraph.h │ ├── graphics.c │ └── xdisplay.c └── README └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.out* 2 | *.o 3 | *.a 4 | *.swp 5 | -------------------------------------------------------------------------------- /ch1/as_i_read/vars.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | int _stk_depth = 2; 5 | 6 | printf("%d\n", _stk_depth); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /ch1/as_i_read/liftoff.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int i; 5 | 6 | for(i=10;i>0;i--) { 7 | printf("%d\n", i); 8 | } 9 | printf("Liftoff!\n"); 10 | 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /roberts_clang: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | INCLUDE=/home/matt/dev/c/roberts_abstractions/book_code/unix-xwindows 3 | CSLIB=$INCLUDE/cslib.a 4 | LIBRARIES="$CSLIB -lX11 -lm" 5 | clang -I$INCLUDE $* $LIBRARIES 6 | -------------------------------------------------------------------------------- /ch1/as_i_read/double.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | double x; 5 | printf("Please enter a double: "); 6 | scanf("%lf", &x); 7 | printf("You entered %lf\n", x); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /ch1/as_i_read/ints.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) { 5 | printf("SHRT_MAX = %d\n", SHRT_MAX); 6 | printf("INT_MAX = %d\n", INT_MAX); 7 | printf("LONG_MAX = %ld\n", LONG_MAX); 8 | 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /ch1/exercises/ex3.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | int i, sum = 0; 5 | 6 | for (i=1;i<=100;i++) { 7 | sum += i; 8 | } 9 | 10 | printf("The sum of the numbers between 1 and 100 is %d\n", sum); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /ch1/exercises/ex1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | float cels; 5 | float fahr; 6 | 7 | printf("Please enter a temperature in degrees Celsius: "); 8 | scanf("%f", &cels); 9 | 10 | fahr = 9.0 / 5 * cels + 32; 11 | printf("%f degrees Celsius is %f degrees Fahrenheit.\n", cels, fahr); 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /ch2/as_i_read/enums.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef enum { 4 | Red, Green, Blue, Yellow, Cyan, Magenta 5 | } colorT; 6 | 7 | typedef int colorIntT; 8 | 9 | colorT favorite = Blue; 10 | colorIntT favoriteInt = 2; 11 | 12 | int main(void) { 13 | printf("My favorite color is %d, also known as %d\n", favorite, favoriteInt); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /ch1/exercises/ex4.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | int i, num, max, sum = 0; 5 | 6 | printf("Please enter an integer: "); 7 | scanf("%d", &num); 8 | 9 | max = num * 2 - 1; 10 | 11 | for(i=1; i<=max; i+=2) { 12 | sum += i; 13 | } 14 | 15 | printf("The sum of the first %d odd integers is %d.\n", num, sum); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /ch1/exercises/ex10.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TERMS 10000 4 | 5 | int main(void) { 6 | int i, counter = 1; 7 | double sum = 0; 8 | 9 | for(i=1; i <= TERMS; i += 2) { 10 | if(counter % 2 == 1) { 11 | sum += 1.0 / i; 12 | } 13 | else { 14 | sum -= 1.0 / i; 15 | } 16 | 17 | counter++; 18 | } 19 | 20 | printf("%.20lf\n", sum*4); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /book_code/VERSION: -------------------------------------------------------------------------------- 1 | cslib -- Version 3.1 2 | Last modified on Mon Mar 6 08:10:24 1995 by eroberts 3 | ----------------------------------------------------------------- 4 | This directory contains version 3.1 of the cslib libraries, which 5 | consists of minor bug fixes from version 3.0. For details, see 6 | the changes.txt file in the documents directory. 7 | 8 | Old versions may be found in the oldfiles directory. 9 | -------------------------------------------------------------------------------- /ch1/as_i_read/digitsum.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int DigitSum(int n) { 4 | int sum = 0; 5 | 6 | while (n > 0) { 7 | sum += n % 10; 8 | n /= 10; 9 | } 10 | 11 | return (sum); 12 | } 13 | 14 | int main(void) { 15 | int num; 16 | 17 | printf("Please enter an integer: "); 18 | scanf("%d", &num); 19 | printf("The sum of the digits in %d is %d.\n", num, DigitSum(num)); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /ch1/exercises/ex6.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | long int input, reversed = 0; 5 | 6 | printf("Please enter a positive integer: "); 7 | scanf("%ld", &input); 8 | 9 | printf("The reverse of %ld is ", input); 10 | 11 | while (input > 0) { 12 | reversed *= 10; 13 | reversed += input % 10; 14 | input /= 10; 15 | } 16 | 17 | printf("%ld.\n", reversed); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /ch1/as_i_read/isvowel.c: -------------------------------------------------------------------------------- 1 | //As it turns out, C99 has a boolean type. 2 | #include 3 | 4 | _Bool IsVowel(char ch) { 5 | switch(ch) { 6 | case 'A': case 'E': case 'I': case 'O': case 'U': 7 | case 'a': case 'e': case 'i': case 'o': case 'u': 8 | return 1; 9 | default: 10 | return 0; 11 | } 12 | } 13 | 14 | int main() { 15 | 16 | char c = 'O'; 17 | 18 | printf("%d", IsVowel(c)); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /ch1/exercises/ex7.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int IsPerfect(int n) { 4 | int i, sum = 0; 5 | for (i=1; i < n; i++) { 6 | if (n % i == 0) { 7 | sum += i; 8 | } 9 | } 10 | if (sum == n) { 11 | return 1; 12 | } else { 13 | return 0; 14 | } 15 | } 16 | 17 | int main(void) { 18 | int i; 19 | for (i=1; i <= 9999; i++) { 20 | if(IsPerfect(i)) { 21 | printf("%d\n", i); 22 | } 23 | } 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /ch1/as_i_read/addlist.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define SENTINEL 0 4 | 5 | int main() { 6 | int value, total; 7 | 8 | printf("This program adds a list of numbers.\n"); 9 | printf("Use %d to signal the end of the list.\n", SENTINEL); 10 | total = 0; 11 | while (1) { 12 | printf(" ? "); 13 | scanf("%d", &value); 14 | if(value == SENTINEL) break; 15 | total += value; 16 | } 17 | printf("The total is %d.\n", total); 18 | } 19 | -------------------------------------------------------------------------------- /ch1/exercises/ex9.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int Round(float input) { 4 | if(input > 0) { 5 | input += 0.5; 6 | } 7 | 8 | if(input < 0) { 9 | input -= 0.5; 10 | } 11 | 12 | return (int)input; 13 | } 14 | 15 | int main(void) { 16 | float input; 17 | 18 | printf("Please enter a floating-point number: "); 19 | scanf("%f", &input); 20 | printf("Here's the rounded version: %d\n", Round(input)); 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /ch1/as_i_read/even_or_odd.c: -------------------------------------------------------------------------------- 1 | #include 2 | //#include "../book_code/unix-xwindows/genlib.h" 3 | //#include "../book_code/unix-xwindows/simpio.h" 4 | 5 | int main() { 6 | int n; 7 | 8 | printf("This program labels a number as even or odd.\n"); 9 | printf("Enter a number: "); 10 | scanf("%d", &n); 11 | if (n % 2 == 0) { 12 | printf("That number is even.\n"); 13 | } else { 14 | printf("That number is odd.\n"); 15 | } 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /ch1/exercises/ex5.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define SENTINEL 0 4 | 5 | int main(void) { 6 | int i, max = 0; 7 | 8 | printf("This program finds the largest integer in a list.\n"); 9 | printf("Enter 0 to signal the end of the list.\n"); 10 | 11 | while(1) { 12 | printf(" ? "); 13 | scanf("%d", &i); 14 | if (i == SENTINEL) { 15 | break; 16 | } 17 | 18 | if (i > max) { 19 | max = i; 20 | } 21 | } 22 | 23 | printf("The largest value is %d.\n", max); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /ch1/exercises/ex8.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | int input, i = 0; 5 | 6 | printf("Please enter a positive integer: "); 7 | fflush(stdout); 8 | scanf("%d", &input); 9 | 10 | 11 | while (input > 1) { 12 | for(i=2; i <= input; i++) { 13 | if(input % i == 0) { 14 | if (input / i == 1) { 15 | printf("%d\n", i); 16 | } else { 17 | printf("%d * ", i); 18 | } 19 | input /= i; 20 | break; 21 | } 22 | } 23 | } 24 | 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /ch1/exercises/ex2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | float meters, inches, part_inches; 5 | int feet, whole_inches; 6 | 7 | printf("Please enter a distance in meters: "); 8 | scanf("%f", &meters); 9 | inches = meters / 0.0254; 10 | feet = inches / 12; 11 | whole_inches = (int)inches % 12; 12 | part_inches = inches - (feet * 12 + whole_inches); 13 | inches = whole_inches + part_inches; 14 | 15 | printf("%f meters is %d feet and %f inches.\n", meters, feet, inches); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /ch1/as_i_read/simple_io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../book_code/unix-xwindows/genlib.h" 3 | #include "../book_code/unix-xwindows/simpio.h" 4 | 5 | int main() { 6 | double n1, n2, n3, average; 7 | 8 | printf("This program averages three numbers.\n"); 9 | printf("1st number: "); 10 | n1 = GetReal(); 11 | printf("2nd number: "); 12 | n2 = GetReal(); 13 | printf("3rd number: "); 14 | n3 = GetReal(); 15 | average = (n1 + n2 + n3) / 3; 16 | printf("The average is %g\n", average); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /ch1/exercises/ex11.c: -------------------------------------------------------------------------------- 1 | // Way to tell us about sqrt and pow. 2 | // You'll need to invoke the compiler like so: clang -g -lm ex11.c 3 | // I hope not all libraries need compiler flags... 4 | 5 | #include 6 | #include 7 | 8 | #define RECTANGLES 100 9 | 10 | int main(void) { 11 | 12 | int i, radius = 2; 13 | double height, area = 0, width = (float)radius / RECTANGLES; 14 | 15 | double x = width / 2; // The first x value is the midpoint of the first rectangle. 16 | 17 | for(i=0; i < RECTANGLES; i++) { 18 | height = sqrt(pow(radius, 2) - pow(x, 2)); 19 | area += height * width; 20 | x += width; 21 | } 22 | 23 | printf("The approximation of Π with %d rectangles is %.20f.\n", RECTANGLES, area); 24 | 25 | return 0; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/glibrary.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: glibrary.c 3 | * Version: 3.0 4 | * Last modified on Sat Oct 1 12:28:01 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file implements the simple functions in the glibrary.h 7 | * interface. 8 | */ 9 | 10 | #include 11 | #include 12 | #include "genlib.h" 13 | 14 | /* Constants */ 15 | 16 | #define Pi 3.1415926535 17 | 18 | /* Exported entries */ 19 | 20 | double GLRadians(double degrees) 21 | { 22 | return (degrees * Pi / 180); 23 | } 24 | 25 | double GLDegrees(double radians) 26 | { 27 | return (radians * 180 / Pi); 28 | } 29 | 30 | int GLRound(double x) 31 | { 32 | return ((int) floor(x + 0.5)); 33 | } 34 | 35 | int GLMin(int x, int y) 36 | { 37 | return ((x < y) ? x : y); 38 | } 39 | 40 | int GLMax(int x, int y) 41 | { 42 | return ((x > y) ? x : y); 43 | } 44 | 45 | double GLMinF(double x, double y) 46 | { 47 | return ((x < y) ? x : y); 48 | } 49 | 50 | double GLMaxF(double x, double y) 51 | { 52 | return ((x > y) ? x : y); 53 | } 54 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/config.csh: -------------------------------------------------------------------------------- 1 | #! /bin/csh -f 2 | #! File: config.csh 3 | #! Usage: csh config.csh flag command . . . 4 | #! Last modified on Sat Oct 1 12:54:21 1994 by eroberts 5 | #! ----------------------------------------------------- 6 | #! This shell script is used inside Makefiles to parameterize 7 | #! compilations for different Unix systems. The effect of the 8 | #! script is to execute the command and its arguments silently, 9 | #! throwing away any output to stdout or stderr. If the command 10 | #! succeeds, the config.csh program echoes the flag parameter; if not, 11 | #! it generates no output. The typical use of the program is 12 | #! within backquotes, as follows: 13 | #! 14 | #! gcc `csh config.csh -DCFLAG gcc -E -DCFLAG testfile.c` -c file.c 15 | #! 16 | #! If the internal command 17 | #! 18 | #! gcc -E -DCFLAG testfile.c 19 | #! 20 | #! succeeds, the outer compilation will define the CFLAG macro; 21 | #! if not, the macro will be undefined. Thus, if the option 22 | #! flag works correctly in the test case, it is used in the 23 | #! other compilations as well. 24 | #! 25 | eval $argv[2-] >& /dev/null 26 | if ($status == 0) echo $1 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Programming Abstractions in C: A Second Course in Computer Science 2 | ====================================== 3 | 4 | Intro 5 | ----- 6 | I'm reading Eric S. Roberts' [Programming Abstractions in C: A Second Course in Computer Science](http://www-cs-faculty.stanford.edu/~eroberts/books/ProgrammingAbstractionsInC/). Yea, I know, I've barely started [two](a_book_on_c) [other](c_aiwaa) C books. But my [CSE214](http://www.cs.sunysb.edu/~cse214/) class has taught me one thing: Java is not a language with which to learn data structures & algorithms. Even [cperciva](http://news.ycombinator.com/item?id=1748572) thinks so! 7 | 8 | Hacking 9 | ------- 10 | In order for the code samples to work, you need to run `make` in the `book_code/unix-xwindows` directory first. I'm using [Clang 2.8](http://clang.llvm.org/), so I've modified the book's original Makefile accordingly and have rewritten Roberts' gccx wrapper script, now called `roberts_clang`. Symlink that into `/usr/local/bin` (or somewhere else that's in your PATH) and you're set. 11 | 12 | Progress 13 | -------- 14 | I'm currently on page 39 (out of the 743 that I plan to read; Chapter 17, 'Looking Ahead to Java', is getting skipped). I'll be writing chapter notes on my [blog, once it's set up](http://matthewblair.net). 15 | -------------------------------------------------------------------------------- /ch1/as_i_read/powertab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: powertab.c 3 | * ---------------- 4 | * This program generates a table comparing values of the functions n^2 and 2^n. 5 | */ 6 | 7 | #include 8 | #include "../book_code/unix-xwindows/genlib.h" 9 | 10 | /* 11 | * Constants 12 | * --------- 13 | * LowerLimit -- Starting value for the table 14 | * UpperLimit -- Final value for the table 15 | */ 16 | 17 | #define LowerLimit 0 18 | #define UpperLimit 20 19 | 20 | /* Private function prototypes */ 21 | 22 | static int RaiseIntToPower(int n, int k); 23 | 24 | /* Main prototype */ 25 | 26 | int main() { 27 | int n; 28 | 29 | printf(" | 2 | N\n"); 30 | printf(" N | N | 2\n"); 31 | printf("----+----+-----\n"); 32 | for (n = LowerLimit; n <= UpperLimit; n++) { 33 | printf(" %2d | %3d | %4d\n", n, 34 | RaiseIntToPower(n, 2), 35 | RaiseIntToPower(2, n)); 36 | } 37 | 38 | return 0; 39 | } 40 | 41 | /* 42 | * Function: RaiseIntToPower 43 | * Usage: p = RaiseIntToPower(n, k); 44 | * --------------------------------- 45 | * This function returns n to the kth power. 46 | */ 47 | 48 | static int RaiseIntToPower(int n, int k) { 49 | int i, result; 50 | result = 1; 51 | for (i=0; i < k; i++) { 52 | result *= n; 53 | } 54 | 55 | return result; 56 | } 57 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/glibrary.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: glibrary.h 3 | * Version: 3.0 4 | * Last modified on Sat Oct 1 12:28:02 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This interface exports several simple, low-level functions that are 7 | * used in various other parts of the graphics package implementation. 8 | * Because these functions are otherwise likely to collide with student 9 | * functions, all external names include a GL prefix. 10 | */ 11 | 12 | #ifndef _glibrary_h 13 | #define _glibrary_h 14 | 15 | /* 16 | * Functions: GLRadians, GLDegrees 17 | * Usage: radians = GLRadians(degrees); 18 | * degrees = GLDegrees(radians); 19 | * ------------------------------------ 20 | * These functions convert back and forth between degrees and radians. 21 | */ 22 | 23 | double GLRadians(double degrees); 24 | double GLDegrees(double radians); 25 | 26 | /* 27 | * Function: GLRound 28 | * Usage: n = GLRound(x); 29 | * ---------------------- 30 | * This function rounds a double to the nearest integer. 31 | */ 32 | 33 | int GLRound(double x); 34 | 35 | /* 36 | * Functions: GLMin, GLMax 37 | * Usage: min = GLMin(x, y); 38 | * max = GLMax(x, y); 39 | * ------------------------- 40 | * These functions find the minimum and maximum of two integers. 41 | */ 42 | 43 | int GLMin(int x, int y); 44 | int GLMax(int x, int y); 45 | 46 | /* 47 | * Functions: GLMinF, GLMaxF 48 | * Usage: min = GLMinF(x, y); 49 | * max = GLMaxF(x, y); 50 | * -------------------------- 51 | * These functions find the minimum and maximum of two doubles. 52 | */ 53 | 54 | double GLMinF(double x, double y); 55 | double GLMaxF(double x, double y); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/xcompat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xcompat.h 3 | * Version: 3.0 4 | * Last modified on Thu Sep 22 15:52:24 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This interface exists entirely for portability with Unix systems 7 | * other than BSD 4.4, which is the assumed model for the rest of 8 | * the code. To get the graphics library to run on other systems 9 | * (such as System V, POSIX, and so forth), the graphics library 10 | * approaches the portability problem by reimplementing the BSD 11 | * system calls and functions in terms of the corresponding 12 | * facilities on the target machine. The advantage of this 13 | * approach is that the clients can work with only one model 14 | * that is relatively well understood. 15 | * 16 | * This interface has no effect unless one of the following macros 17 | * is defined: 18 | * 19 | * HasPoll -- Used if the System V poll call is available 20 | * 21 | * Additional conditionals may be added as new environments are 22 | * supported. 23 | */ 24 | 25 | #ifndef _xcompat_h 26 | #define _xcompat_h 27 | 28 | #ifdef HasPoll 29 | 30 | #include 31 | 32 | #ifndef FOPEN_MAX 33 | # define FOPEN_MAX 128 34 | #endif 35 | 36 | #undef fd_set 37 | #undef FD_ZERO 38 | #undef FD_SET 39 | #undef FD_CLR 40 | #undef FD_ISSET 41 | 42 | #define fd_set fdSetT 43 | #define FD_ZERO(fdptr) (memset((fdptr)->fds, sizeof (fd_set), 0)) 44 | #define FD_SET(fd, fdptr) ((fdptr)->fds[fd] = 1) 45 | #define FD_CLR(fd, fdptr) ((fdptr)->fds[fd] = 0) 46 | #define FD_ISSET(fd, fdptr) ((fdptr)->fds[fd]) 47 | 48 | typedef struct { 49 | char fds[FOPEN_MAX]; 50 | } fdSetT; 51 | 52 | #define select SimulateSelect 53 | 54 | int SimulateSelect(int width, 55 | fd_set *readfds, 56 | fd_set *writefds, 57 | fd_set *exceptfds, 58 | struct timeval *tvp); 59 | 60 | #endif /* HasPoll */ 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/random.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: random.c 3 | * Version: 1.0 4 | * Last modified on Mon Sep 13 10:42:45 1993 by eroberts 5 | * ----------------------------------------------------- 6 | * This file implements the random.h interface. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "genlib.h" 14 | #include "random.h" 15 | 16 | /* 17 | * Function: Randomize 18 | * ------------------- 19 | * This function operates by setting the random number 20 | * seed to the current time. The srand function is 21 | * provided by the library and requires an 22 | * integer argument. The time function is provided 23 | * by . 24 | */ 25 | 26 | void Randomize(void) 27 | { 28 | srand((int) time(NULL)); 29 | } 30 | 31 | /* 32 | * Function: RandomInteger 33 | * ----------------------- 34 | * This function first obtains a random integer in 35 | * the range [0..RAND_MAX] by applying four steps: 36 | * (1) Generate a real number in the interval [0,1) 37 | * (2) Scale it to the appropriate range size 38 | * (3) Truncate the value to an integer 39 | * (4) Translate it to the appropriate starting point 40 | */ 41 | 42 | int RandomInteger(int low, int high) 43 | { 44 | int k; 45 | double d; 46 | 47 | d = (double) rand() / ((double) RAND_MAX + 1); 48 | k = (int) (d * (high - low + 1)); 49 | return (low + k); 50 | } 51 | 52 | /* 53 | * Function: RandomReal 54 | * -------------------- 55 | * The implementation of RandomReal is similar to that 56 | * of RandomInteger, without the truncation step. 57 | */ 58 | 59 | double RandomReal(double low, double high) 60 | { 61 | double d; 62 | 63 | d = (double) rand() / ((double) RAND_MAX + 1); 64 | return (low + d * (high - low)); 65 | } 66 | 67 | /* 68 | * Function: RandomChance 69 | * ---------------------- 70 | * This function uses RandomReal to generate a number 71 | * between 0 and 100, which it then compares to p. 72 | */ 73 | 74 | bool RandomChance(double p) 75 | { 76 | return (RandomReal(0, 1) < p); 77 | } 78 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/random.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: random.h 3 | * Version: 1.0 4 | * Last modified on Fri Jul 22 16:44:36 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This interface provides several functions for generating 7 | * pseudo-random numbers. 8 | */ 9 | 10 | #ifndef _random_h 11 | #define _random_h 12 | 13 | #include "genlib.h" 14 | #include 15 | 16 | /* 17 | * Constant: RAND_MAX 18 | * ------------------ 19 | * Unfortunately, several libraries that supposedly conform to 20 | * the ANSI standard do not define RAND_MAX in . To 21 | * reduce portability problems, this interface defines RAND_MAX 22 | * to be the largest positive integer if it is undefined. 23 | */ 24 | 25 | #ifndef RAND_MAX 26 | # define RAND_MAX ((int) ((unsigned) ~0 >> 1)) 27 | #endif 28 | 29 | /* 30 | * Function: Randomize 31 | * Usage: Randomize(); 32 | * ------------------- 33 | * This function sets the random seed so that the random sequence 34 | * is unpredictable. During the debugging phase, it is best not 35 | * to call this function, so that program behavior is repeatable. 36 | */ 37 | 38 | void Randomize(void); 39 | 40 | /* 41 | * Function: RandomInteger 42 | * Usage: n = RandomInteger(low, high); 43 | * ------------------------------------ 44 | * This function returns a random integer in the range low to high, 45 | * inclusive. 46 | */ 47 | 48 | int RandomInteger(int low, int high); 49 | 50 | /* 51 | * Function: RandomReal 52 | * Usage: d = RandomReal(low, high); 53 | * --------------------------------- 54 | * This function returns a random real number in the half-open 55 | * interval [low .. high), meaning that the result is always 56 | * greater than or equal to low but strictly less than high. 57 | */ 58 | 59 | double RandomReal(double low, double high); 60 | 61 | /* 62 | * Function: RandomChance 63 | * Usage: if (RandomChance(p)) . . . 64 | * --------------------------------- 65 | * The RandomChance function returns TRUE with the probability 66 | * indicated by p, which should be a floating-point number between 67 | * 0 (meaning never) and 1 (meaning always). For example, calling 68 | * RandomChance(.30) returns TRUE 30 percent of the time. 69 | */ 70 | 71 | bool RandomChance(double p); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/gcalloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: gcalloc.h 3 | * Version: 1.0 4 | * Last modified on Wed Sep 21 16:21:37 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file is a stub version of the interface for a 7 | * garbage-collecting allocator that will be part of 8 | * a future library release. When the garbage-collecting 9 | * allocator is in use, the memory returned by the GetBlock 10 | * and FreeBlock functions in genlib.h can be traced and 11 | * collected automatically when it is no longer accessible. 12 | * 13 | * The garbage-collecting allocator is not part of the 14 | * current cslib distribution. Even so, functions in the 15 | * other libraries call the ProtectVariable and ProtectBlock 16 | * functions, so that they will continue to work when the 17 | * full library is released. Those functions are implemented 18 | * in genlib.c. 19 | */ 20 | 21 | #ifndef _gcalloc_h 22 | #define _gcalloc_h 23 | 24 | /* 25 | * Macro: ProtectVariable 26 | * Usage: ProtectVariable(v); 27 | * -------------------------- 28 | * This macro registers a global variable with the allocation 29 | * system, so that the variable is traced when the garbage 30 | * collector is used. This operation needs is implemented 31 | * in genlib.c so that code can be written to function correctly 32 | * whether or not the garbage-collecting allocator is loaded. 33 | */ 34 | 35 | #define ProtectVariable(v) ProtectBlock(&v, sizeof v) 36 | 37 | /* 38 | * Function: ProtectBlock 39 | * Usage: ProtectBlock(ptr, nbytes); 40 | * --------------------------------- 41 | * This function is not usually called by clients (who will 42 | * ordinarily use ProtectVariable instead), but has the 43 | * effect of protecting the block of memory beginning at 44 | * ptr and extending for nbytes from the garbage collector. 45 | */ 46 | 47 | void ProtectBlock(void *ptr, size_t nbytes); 48 | 49 | /* 50 | * Global linkage variable: _acb 51 | * ----------------------------- 52 | * This variable is used to hold the allocation control block 53 | * that provides the linkage between this package and the 54 | * dynamic allocator. The reason for using the structure 55 | * as a linkage is so that the garbage-collecting allocator 56 | * need not even be loaded if it is not explicitly called. 57 | */ 58 | 59 | typedef struct { 60 | void *(*allocMethod)(size_t nbytes); 61 | void (*freeMethod)(void *ptr); 62 | void (*protectMethod)(void *ptr, size_t nbytes); 63 | } *_GCControlBlock; 64 | 65 | extern _GCControlBlock _acb; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/simpio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: simpio.h 3 | * Version: 1.0 4 | * Last modified on Wed Apr 27 07:29:13 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This interface provides access to a simple package of 7 | * functions that simplify the reading of input data. 8 | */ 9 | 10 | #ifndef _simpio_h 11 | #define _simpio_h 12 | 13 | #include "genlib.h" 14 | 15 | /* 16 | * Function: GetInteger 17 | * Usage: i = GetInteger(); 18 | * ------------------------ 19 | * GetInteger reads a line of text from standard input and scans 20 | * it as an integer. The integer value is returned. If an 21 | * integer cannot be scanned or if more characters follow the 22 | * number, the user is given a chance to retry. 23 | */ 24 | 25 | int GetInteger(void); 26 | 27 | /* 28 | * Function: GetLong 29 | * Usage: l = GetLong(); 30 | * --------------------- 31 | * GetLong reads a line of text from standard input and scans 32 | * it as a long integer. The value is returned as a long. 33 | * If an integer cannot be scanned or if more characters follow 34 | * the number, the user is given a chance to retry. 35 | */ 36 | 37 | long GetLong(void); 38 | 39 | /* 40 | * Function: GetReal 41 | * Usage: x = GetReal(); 42 | * --------------------- 43 | * GetReal reads a line of text from standard input and scans 44 | * it as a double. If the number cannot be scanned or if extra 45 | * characters follow after the number ends, the user is given 46 | * a chance to reenter the value. 47 | */ 48 | 49 | double GetReal(void); 50 | 51 | /* 52 | * Function: GetLine 53 | * Usage: s = GetLine(); 54 | * --------------------- 55 | * GetLine reads a line of text from standard input and returns 56 | * the line as a string. 57 | * [We assume implicitly -- this assumption is very natural -- 58 | * that the line does not contain the null character '\0'. (PF)] 59 | * The newline character that terminates 60 | * the input is not stored as part of the string. 61 | */ 62 | 63 | string GetLine(void); 64 | 65 | /* 66 | * Function: ReadLine 67 | * Usage: s = ReadLine(infile); 68 | * ---------------------------- 69 | * ReadLine reads a line of text from the input file and 70 | * returns the line as a string. 71 | * [We assume implicitly -- this assumption is very natural -- 72 | * that the line does not contain the null character '\0'. (PF)] 73 | * The newline character 74 | * that terminates the input is not stored as part of the 75 | * string. The ReadLine function returns NULL if infile 76 | * is at the end-of-file position. 77 | */ 78 | 79 | string ReadLine(FILE *infile); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/xcompat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xcompat.c 3 | * Version: 3.0 4 | * Last modified on Sat Oct 1 11:28:05 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This implementation and the corresponding interface simulate the 7 | * operation of any BSD4.2 calls that are required by the graphics 8 | * package but that are not defined in the local system. See the 9 | * interface for details. Note that this file generates no code 10 | * unless one of the conditions is set. 11 | */ 12 | 13 | #ifdef HasPoll 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "xcompat.h" 21 | 22 | /* Private function prototypes */ 23 | 24 | static void SigAlarmHandler(); 25 | 26 | /* 27 | * Function: SimulateSelect 28 | * ------------------------ 29 | * This function simulates the BSD select call by mapping it onto 30 | * the poll system call implemented under System V. 31 | */ 32 | 33 | int SimulateSelect(int width, 34 | fd_set *readfds, 35 | fd_set *writefds, 36 | fd_set *exceptfds, 37 | struct timeval *tvp) 38 | { 39 | struct pollfd fds[FOPEN_MAX]; 40 | int i, fd, nfds; 41 | short events; 42 | int timeout, nready; 43 | 44 | nfds = 0; 45 | if (tvp == NULL) { 46 | timeout = 0; 47 | } else { 48 | timeout = tvp->tv_sec * 1000 + (tvp->tv_usec + 500) / 1000; 49 | } 50 | for (fd = 0; fd < width; fd++) { 51 | events = 0; 52 | if (readfds && FD_ISSET(fd, readfds)) { 53 | events |= POLLIN; 54 | FD_CLR(fd, readfds); 55 | } 56 | if (writefds && FD_ISSET(fd, writefds)) { 57 | events |= POLLOUT; 58 | FD_CLR(fd, writefds); 59 | } 60 | if (exceptfds && FD_ISSET(fd, exceptfds)) { 61 | events |= POLLIN | POLLOUT; 62 | FD_CLR(fd, exceptfds); 63 | } 64 | if (events != 0) { 65 | fds[nfds].fd = fd; 66 | fds[nfds].events = events; 67 | fds[nfds].revents = 0; 68 | nfds++; 69 | } 70 | } 71 | nready = poll(fds, nfds, timeout); 72 | if (nready <= 0) return (nready); 73 | for (i = 0; i < nfds; i++) { 74 | if (fds[i].revents & (POLLIN | POLLPRI | POLLHUP)) { 75 | FD_SET(fds[i].fd, readfds); 76 | } 77 | if (fds[i].revents & POLLOUT) { 78 | FD_SET(fds[i].fd, writefds); 79 | } 80 | if (fds[i].revents & (POLLERR | POLLNVAL)) { 81 | FD_SET(fds[i].fd, exceptfds); 82 | } 83 | } 84 | return (nready); 85 | } 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/xmanager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xmanager.h 3 | * Version: 3.0 4 | * Last modified on Sat Oct 1 12:25:07 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file is the interface to the xmanager module, which is the 7 | * portion of the graphics implementation responsible for all of the 8 | * interaction with X. 9 | */ 10 | 11 | #ifndef _xmanager_h 12 | #define _xmanager_h 13 | 14 | #include "genlib.h" 15 | 16 | /* 17 | * Constants 18 | * --------- 19 | * CommandBufferSize -- Size of internal command buffer 20 | * MaxFontName -- Maximum length of font name 21 | * MaxTextString -- Length limit for DrawTextString 22 | */ 23 | 24 | #define CommandBufferSize 200 25 | #define MaxFontName 25 26 | #define MaxTextString 120 27 | 28 | /* 29 | * Type: commandT 30 | * -------------- 31 | * The client side of the graphics implementation and the X side of 32 | * the implementation communicate by means of commands sent across a 33 | * communication pipe. This enumeration type defines the command 34 | * codes. 35 | */ 36 | 37 | typedef enum { 38 | LineCmd, 39 | ArcCmd, 40 | TextCmd, 41 | WidthCmd, 42 | FontMetricsCmd, 43 | SetEraseCmd, 44 | StartRegionCmd, 45 | EndRegionCmd, 46 | ClearCmd, 47 | UpdateCmd, 48 | SetFontCmd, 49 | SetTitleCmd, 50 | GetWidthCmd, 51 | GetHeightCmd, 52 | GetMouseCmd, 53 | WaitForMouseCmd, 54 | SetColorCmd, 55 | ExitGraphicsCmd 56 | } commandT; 57 | 58 | /* 59 | * Function: XMInitialize 60 | * Usage: XMInitialize(title); 61 | * --------------------------- 62 | * This function starts the X manager process. The title argument is 63 | * a string to use as the title bar for the new window. 64 | */ 65 | 66 | void XMInitialize(string title); 67 | 68 | /* 69 | * Function: XMSendCommand 70 | * Usage: XMSendCommand(cmd, args); 71 | * -------------------------------- 72 | * This function sends the specified command to the X manager 73 | * process along with an argument string. 74 | */ 75 | 76 | void XMSendCommand(commandT cmd, string args); 77 | 78 | /* 79 | * Function: XMGetResponse 80 | * Usage: XMGetResponse(buffer); 81 | * ----------------------------- 82 | * This function reads a line from the X manager in response to 83 | * a command message. The string is stored in the buffer provided 84 | * by the client, which must be at least CommandBufferSize bytes. 85 | */ 86 | 87 | void XMGetResponse(char buffer[]); 88 | 89 | /* 90 | * Function: XMReleaseClient 91 | * Usage: XMReleaseClient(); 92 | * ------------------------- 93 | * This function is used by the X display code to release a 94 | * client waiting on an event. 95 | */ 96 | 97 | void XMReleaseClient(void); 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/README: -------------------------------------------------------------------------------- 1 | This is eroberts' original README. It no longer applies, but has been kept for completeness. 2 | -------------------------------------------------------------------------------------------- 3 | 4 | 5 | 6 | README file for the unix-xwindows subdirectory 7 | Last modified on Thu Oct 20 00:01:05 1994 by eroberts 8 | ________________________________________________________________________ 9 | 10 | The unix-xwindows directory contains an implementation of the cslib 11 | library designed for use with XWindows (version X11) on Unix systems. 12 | The development and testing of the package has been performed using the 13 | gcc compiler, which is available without charge from the GNU project at 14 | MIT. The procedure described below may work with other compilers as 15 | well, but the results are far less certain. 16 | 17 | TO INSTALL THE UNIX/XWINDOWS VERSION OF THE LIBRARIES ON YOUR MACHINE: 18 | 19 | 1. Use FTP to retrieve the file cslib.shar from the unix-xwindows 20 | directory of the aw.com archive to the home directory on your 21 | machine. 22 | 23 | 2. Execute the cslib.shar script by typing 24 | 25 | sh cslib.shar 26 | 27 | Executing the script creates a top-level directory called cslib that 28 | contains all of the relevant files. 29 | 30 | 3. Connect to the cslib directory. 31 | 32 | 4. Rebuild the object files and libraries by typing 33 | 34 | make 35 | 36 | Note: This step assumes that you are using gcc as your compiler. If 37 | you want to use a different compiler, you will need to edit the 38 | Makefile accordingly. 39 | 40 | 5. Copy the file gccx from the cslib directory into a directory on your 41 | command search path. If you will be the only person using the 42 | libraries, you could put this file in your private command 43 | directory, which is usually ~/bin. If you are installing this 44 | command for use by any user on the system, you will need to put it 45 | in a public directory, such as /usr/local/bin. For public 46 | installation to work, your cslib directory must be readable by other 47 | users. 48 | 49 | COMPILING PROGRAMS ON AN XWINDOWS SYSTEM 50 | 51 | The gccx command produced by the Makefile is a shell script that acts 52 | like gcc except that it automatically arranges to include the cslib 53 | header files and libraries whenever it is invoked as a command. For 54 | example, to create an executable application from the source file 55 | house.c, all you need to do is type: 56 | 57 | gccx -o house house.c 58 | 59 | NOTES AND DISCLAIMERS 60 | 61 | The cslib libraries are in the public domain and may be freely copied 62 | and distributed, although they remain under development. No warranties 63 | are made concerning their correctness or stability, and no user support 64 | is guaranteed. Bug reports and suggestions, however, are appreciated 65 | and may be sent to 66 | 67 | Eric Roberts 68 | -------------------------------------------------------------------------------- /ch1/exercises/ex12.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void printTens(int x) { 4 | switch(x) { 5 | case 2: printf("twenty"); return; 6 | case 3: printf("thirty"); return; 7 | case 4: printf("fourty"); return; 8 | case 5: printf("fifty"); return; 9 | case 6: printf("sixty"); return; 10 | case 7: printf("seventy"); return; 11 | case 8: printf("eighty"); return; 12 | case 9: printf("ninety"); return; 13 | } 14 | } 15 | 16 | void printOneDigit(int x) { 17 | switch(x) { 18 | case 1: printf("one"); return; 19 | case 2: printf("two"); return; 20 | case 3: printf("three"); return; 21 | case 4: printf("four"); return; 22 | case 5: printf("five"); return; 23 | case 6: printf("six"); return; 24 | case 7: printf("seven"); return; 25 | case 8: printf("eight"); return; 26 | case 9: printf("nine"); return; 27 | } 28 | } 29 | 30 | int hundreds(int x) { 31 | return x / 100; 32 | } 33 | 34 | int tens(int x) { 35 | return (x / 10) % 10; 36 | } 37 | 38 | int ones(int x) { 39 | return x % 10; 40 | } 41 | 42 | int thousands(int x) { 43 | return x / 1000; 44 | } 45 | 46 | int lastThree(int x) { 47 | return x % 1000; 48 | } 49 | 50 | int lastTwo(int x) { 51 | return x % 100; 52 | } 53 | 54 | void printNumber(int x) { 55 | if(x >= 0 && x < 10) { 56 | printOneDigit(x); 57 | return; 58 | } 59 | 60 | if (hundreds(x) > 0) { 61 | printOneDigit(hundreds(x)); 62 | printf(" hundred "); 63 | } 64 | 65 | if((lastTwo(x) >= 10 && lastTwo(x) <= 13) || lastTwo(x) == 15 || lastTwo(x) == 18) { 66 | switch(lastTwo(x)) { 67 | case 10: printf("ten"); return; 68 | case 11: printf("eleven"); return; 69 | case 12: printf("twelve"); return; 70 | case 13: printf("thirteen"); return; 71 | case 15: printf("fifteen"); return; 72 | case 18: printf("eighteen"); return; 73 | } 74 | } 75 | 76 | if(lastTwo(x) == 14 || lastTwo(x) == 16 || lastTwo(x) == 17 || lastTwo(x) == 19) { //Yikes, I know. But this is the reusable functions question. 77 | printOneDigit(x % 10); 78 | printf("teen"); 79 | return; 80 | } 81 | 82 | if(tens(x) > 1) { 83 | printTens(tens(x)); 84 | if (ones(x) > 0) { 85 | printf("-"); 86 | printOneDigit(ones(x)); 87 | } 88 | return; 89 | } 90 | 91 | printOneDigit(ones(x)); 92 | return; 93 | 94 | } 95 | 96 | void printTotal(int x) { 97 | if(x > 999999) { 98 | printf("That's too large."); 99 | return; 100 | } 101 | 102 | if(x == 0) { 103 | printf("zero"); 104 | return; 105 | } 106 | 107 | if(thousands(x) > 0) { 108 | printNumber(thousands(x)); 109 | printf(" thousand "); 110 | } 111 | printNumber(lastThree(x)); 112 | return; 113 | } 114 | 115 | int main(void) { 116 | 117 | int input; 118 | 119 | printf("Enter numbers in their decimal form.\n"); 120 | printf("To stop, enter a negative value."); 121 | 122 | do { 123 | printf("\nNumber: "); 124 | scanf("%d", &input); 125 | printTotal(input); 126 | } 127 | while (input >= 0); 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for cslib/xwindows directory 2 | # Last modified on Sat Oct 1 12:56:29 1994 by eroberts 3 | #**************************************************************** 4 | 5 | OBJECTS = \ 6 | genlib.o \ 7 | exception.o \ 8 | strlib.o \ 9 | simpio.o \ 10 | random.o \ 11 | graphics.o \ 12 | xmanager.o \ 13 | xdisplay.o \ 14 | xcompat.o \ 15 | glibrary.o 16 | 17 | CSLIB = cslib.a 18 | 19 | CC = clang 20 | CFLAGS = -I. $(CCFLAGS) 21 | 22 | # *************************************************************** 23 | # Entry to bring the package up to date 24 | # The "make all" entry should be the first real entry 25 | 26 | all: $(CSLIB) 27 | 28 | # *************************************************************** 29 | # Standard entries to remove files from the directories 30 | # tidy -- eliminate unwanted files 31 | # clean -- delete derived files in preparation for rebuild 32 | # scratch -- synonym for clean 33 | 34 | tidy: 35 | rm -f ,* .,* *~ core a.out *.err 36 | 37 | clean scratch: tidy 38 | rm -f *.o *.a gccx 39 | 40 | # *************************************************************** 41 | # C compilations 42 | 43 | genlib.o: genlib.c genlib.h exception.h gcalloc.h 44 | $(CC) $(CFLAGS) -c genlib.c 45 | 46 | exception.o: exception.c exception.h genlib.h 47 | $(CC) $(CFLAGS) -c exception.c 48 | 49 | strlib.o: strlib.c strlib.h genlib.h 50 | $(CC) $(CFLAGS) -c strlib.c 51 | 52 | simpio.o: simpio.c simpio.h strlib.h genlib.h 53 | $(CC) $(CFLAGS) -c simpio.c 54 | 55 | random.o: random.c random.h genlib.h 56 | $(CC) $(CFLAGS) -c random.c 57 | 58 | graphics.o: graphics.c graphics.h extgraph.h xmanager.h xcompat.h \ 59 | glibrary.h genlib.h gcalloc.h simpio.h strlib.h Makefile 60 | $(CC) $(CFLAGS) -c graphics.c 61 | 62 | xmanager.o: xmanager.c xmanager.h xdisplay.h xcompat.h glibrary.h \ 63 | genlib.h exception.h simpio.h Makefile 64 | $(CC) $(CFLAGS) -c xmanager.c 65 | 66 | xdisplay.o: xdisplay.c xdisplay.h xmanager.h glibrary.h genlib.h strlib.h \ 67 | Makefile 68 | $(CC) $(CFLAGS) -c xdisplay.c 69 | 70 | xcompat.o: xcompat.c xcompat.h Makefile 71 | $(CC) $(CFLAGS) -c xcompat.c 72 | 73 | glibrary.o: glibrary.c glibrary.h genlib.h 74 | $(CC) $(CFLAGS) -c glibrary.c 75 | 76 | # *************************************************************** 77 | # Entry to reconstruct the library archive 78 | 79 | $(CSLIB): $(OBJECTS) 80 | -rm -f $(CSLIB) 81 | ar cr $(CSLIB) $(OBJECTS) 82 | ranlib $(CSLIB) 83 | 84 | # *************************************************************** 85 | # Entry to reconstruct the gccx script 86 | 87 | gccx: Makefile 88 | @echo '#! /bin/csh -f' > gccx 89 | @echo 'set INCLUDE =' `pwd` >> gccx 90 | @echo 'set CSLIB = $$INCLUDE/cslib.a' >> gccx 91 | @echo 'set LIBRARIES = ($$CSLIB -lX11 -lm)' >> gccx 92 | @echo 'foreach x ($$*)' >> gccx 93 | @echo ' if ("x$$x" == "x-c") then' >> gccx 94 | @echo ' set LIBRARIES = ""' >> gccx 95 | @echo ' break' >> gccx 96 | @echo ' endif' >> gccx 97 | @echo 'end' >> gccx 98 | @echo 'gcc -g -I$$INCLUDE $$* $$LIBRARIES' >> gccx 99 | @chmod a+x gccx 100 | @echo '[gccx script created]' 101 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/exception.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: exception.c 3 | * Version: 1.0 4 | * Last modified on Sun Jul 24 10:28:11 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file implements the C exception handler. Much of the 7 | * real work is done in the exception.h header file. 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "genlib.h" 14 | #include "gcalloc.h" 15 | #include "exception.h" 16 | 17 | /* 18 | * Constant: MaxUnhandledMessage 19 | * ----------------------------- 20 | * This constant should be large enough to accommodate the 21 | * unhandled exception message, including the exception name. 22 | */ 23 | 24 | #define MaxUnhandledMessage 100 25 | 26 | /* Publically accessible exceptions */ 27 | 28 | exception ANY = { "ANY" }; 29 | exception ErrorException = { "ErrorException" }; 30 | 31 | /* 32 | * Global variable: exceptionStack 33 | * ------------------------------- 34 | * This variable is the head pointer to a linked list of 35 | * context blocks that act as the exception stack. The chain 36 | * pointer is referenced by the macros in exception.h and must 37 | * therefore be exported, but clients should not reference it 38 | * directly. 39 | */ 40 | 41 | context_block *exceptionStack = NULL; 42 | 43 | /* Private function prototypes */ 44 | 45 | static context_block *FindHandler(exception *e); 46 | 47 | /* Public entries */ 48 | 49 | /* 50 | * Function: RaiseException 51 | * ------------------------ 52 | * This function operates by finding an appropriate handler 53 | * and then using longjmp to return to the context stored 54 | * there after resetting the exception stack. If no handler 55 | * exists, the function notes an unhandled exception. Much 56 | * of the complexity comes from the fact that allocation 57 | * within the exception handler may fail. 58 | */ 59 | 60 | void RaiseException(exception *e, string name, void *value) 61 | { 62 | context_block *cb; 63 | char errbuf[MaxUnhandledMessage + 1]; 64 | string errmsg; 65 | int errlen; 66 | 67 | cb = FindHandler(e); 68 | if (cb == NULL) { 69 | sprintf(errbuf, "Unhandled exception (%.30s)", name); 70 | errlen = strlen(errbuf); 71 | if (_acb == NULL) { 72 | errmsg = malloc(errlen + 1); 73 | } else { 74 | errmsg = _acb->allocMethod(errlen + 1); 75 | } 76 | if (errmsg == NULL) { 77 | errmsg = "Unhandled exception: unknown"; 78 | } else { 79 | strcpy(errmsg, errbuf); 80 | } 81 | Error(errmsg); 82 | } 83 | exceptionStack = cb; 84 | cb->id = e; 85 | cb->value = value; 86 | cb->name = name; 87 | longjmp(cb->jmp, ES_Exception); 88 | } 89 | 90 | /* 91 | * Function: HandlerExists 92 | * ----------------------- 93 | * This public entry is used primarily by the Error function 94 | * to determine if ErrorException has been trapped, although 95 | * it is available to other clients as well. 96 | */ 97 | 98 | bool HandlerExists(exception *e) 99 | { 100 | return (FindHandler(e) != NULL); 101 | } 102 | 103 | /* Private functions */ 104 | 105 | /* 106 | * Function: FindHandler 107 | * --------------------- 108 | * This function searches the exception stack to find the 109 | * first active handler for the indicated exception. If a 110 | * match is found, the context block pointer is returned. 111 | * If not, FindHandler returns NULL. 112 | */ 113 | 114 | static context_block *FindHandler(exception *e) 115 | { 116 | context_block *cb; 117 | exception *t; 118 | int i; 119 | 120 | for (cb = exceptionStack; cb != NULL; cb = cb->link) { 121 | for (i = 0; i < cb->nx; i++) { 122 | t = cb->array[i]; 123 | if (t == e || t == &ANY) return (cb); 124 | } 125 | } 126 | return (NULL); 127 | } 128 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/genlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: genlib.c 3 | * Version: 1.0 4 | * Last modified on Sun Jul 24 10:29:46 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file implements the general C library package. See the 7 | * interface description in genlib.h for details. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "genlib.h" 16 | #include "gcalloc.h" 17 | #include "exception.h" 18 | 19 | /* 20 | * Constants: 21 | * ---------- 22 | * ErrorExitStatus -- Status value used in exit call 23 | * MaxErrorMessage -- Longest error message allowed 24 | */ 25 | 26 | #define ErrorExitStatus 1 27 | #define MaxErrorMessage 500 28 | 29 | /* Section 1 -- Define new "primitive" types */ 30 | 31 | /* 32 | * Constant: UNDEFINED 33 | * ------------------- 34 | * This entry defines the target of the UNDEFINED constant. 35 | */ 36 | 37 | char undefined_object[] = "UNDEFINED"; 38 | 39 | /* Section 2 -- Memory allocation */ 40 | 41 | /* 42 | * Implementation notes: 43 | * --------------------- 44 | * The code for the memory allocator is divided between 45 | * genlib.c and gcalloc.c, and the division strategy may at 46 | * first seem unnatural, since the function ProtectBlock is 47 | * declared in gcalloc.h but defined here in genlib.c. The 48 | * intention is to minimize the size of object files 49 | * produced by linkers that search a library for modules 50 | * that are actually referenced. The libraries themselves 51 | * need to call ProtectBlock (usually through the macro 52 | * ProtectVariable), but will not require the actual code 53 | * for the allocator unless InitGCAllocator is explicitly 54 | * called. 55 | */ 56 | 57 | /* 58 | * Global variable: _acb 59 | * --------------------- 60 | * This variable is used to hold a method suite that makes it 61 | * easy to substitute a garbage-collecting allocator for the 62 | * ANSI allocator. 63 | */ 64 | 65 | _GCControlBlock _acb = NULL; 66 | 67 | /* Memory allocation implementation */ 68 | 69 | void *GetBlock(size_t nbytes) 70 | { 71 | void *result; 72 | 73 | if (_acb == NULL) { 74 | result = malloc(nbytes); 75 | } else { 76 | result = _acb->allocMethod(nbytes); 77 | } 78 | if (result == NULL) Error("No memory available"); 79 | return (result); 80 | } 81 | 82 | void FreeBlock(void *ptr) 83 | { 84 | if (_acb == NULL) { 85 | free(ptr); 86 | } else { 87 | _acb->freeMethod(ptr); 88 | } 89 | } 90 | 91 | void ProtectBlock(void *ptr, size_t nbytes) 92 | { 93 | if (_acb != NULL) _acb->protectMethod(ptr, nbytes); 94 | } 95 | 96 | /* Section 3 -- Basic error handling */ 97 | 98 | /* 99 | * Implementation notes: Error 100 | * --------------------------- 101 | * Writing the Error function requires some care, since it is 102 | * called in circumstances in which parts of the system may be 103 | * broken. In particular, it is not acceptable for Error to 104 | * call GetBlock, since the error condition may be that the 105 | * system is out of memory, in which case calling GetBlock would 106 | * fail. The error string should be allocated dynamically, 107 | * so that this function can be used in reentrant code. 108 | * Note that it is critical to exit if the length bound for 109 | * an error message is exceeded, since this error almost 110 | * certainly corrupts the stack. 111 | */ 112 | 113 | void Error(string msg, ...) 114 | { 115 | va_list args; 116 | char errbuf[MaxErrorMessage + 1]; 117 | string errmsg; 118 | int errlen; 119 | 120 | va_start(args, msg); 121 | vsprintf(errbuf, msg, args); 122 | va_end(args); 123 | errlen = strlen(errbuf); 124 | if (errlen > MaxErrorMessage) { 125 | fprintf(stderr, "Error: Error Message too long\n"); 126 | exit(ErrorExitStatus); 127 | } 128 | if (_acb == NULL) { 129 | errmsg = malloc(errlen + 1); 130 | } else { 131 | errmsg = _acb->allocMethod(errlen + 1); 132 | } 133 | if (errmsg == NULL) { 134 | errmsg = "No memory available"; 135 | } else { 136 | strcpy(errmsg, errbuf); 137 | } 138 | if (HandlerExists(&ErrorException)) { 139 | RaiseException(&ErrorException, "ErrorException", errmsg); 140 | } else { 141 | fprintf(stderr, "Error: %s\n", errmsg); 142 | exit(ErrorExitStatus); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/simpio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: simpio.c 3 | * Version: 3.0 4 | * Last modified on Tue Oct 4 11:24:40 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file implements the simpio.h interface. 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include "genlib.h" 13 | #include "strlib.h" 14 | #include "simpio.h" 15 | 16 | /* 17 | * Constants: 18 | * ---------- 19 | * InitialBufferSize -- Initial buffer size for ReadLine 20 | */ 21 | 22 | #define InitialBufferSize 120 23 | 24 | /* Exported entries */ 25 | 26 | /* 27 | * Functions: GetInteger, GetLong, GetReal 28 | * --------------------------------------- 29 | * These functions first read a line and then call sscanf to 30 | * translate the number. Reading an entire line is essential to 31 | * good error recovery, because the characters after the point of 32 | * error would otherwise remain in the input buffer and confuse 33 | * subsequent input operations. The sscanf line allows white space 34 | * before and after the number but no other extraneous characters. 35 | */ 36 | 37 | int GetInteger(void) 38 | { 39 | string line; 40 | int value; 41 | char termch; 42 | 43 | while (TRUE) { 44 | line = GetLine(); 45 | if (line == NULL) Error("GetInteger: unexpected end of file"); 46 | switch (sscanf(line, " %d %c", &value, &termch)) { 47 | case 1: 48 | FreeBlock(line); 49 | return (value); 50 | case 2: 51 | printf("Unexpected character: '%c'\n", termch); 52 | break; 53 | default: 54 | printf("Please enter an integer\n"); 55 | break; 56 | } 57 | FreeBlock(line); 58 | printf("Retry: "); 59 | } 60 | } 61 | 62 | long GetLong(void) 63 | { 64 | string line; 65 | long value; 66 | char termch; 67 | 68 | while (TRUE) { 69 | line = GetLine(); 70 | if (line == NULL) Error("GetLong: unexpected end of file"); 71 | switch (sscanf(line, " %ld %c", &value, &termch)) { 72 | case 1: 73 | FreeBlock(line); 74 | return (value); 75 | case 2: 76 | printf("Unexpected character: '%c'\n", termch); 77 | break; 78 | default: 79 | printf("Please enter an integer\n"); 80 | break; 81 | } 82 | FreeBlock(line); 83 | printf("Retry: "); 84 | } 85 | } 86 | 87 | double GetReal(void) 88 | { 89 | string line; 90 | double value; 91 | char termch; 92 | 93 | while (TRUE) { 94 | line = GetLine(); 95 | if (line == NULL) Error("GetReal: unexpected end of file"); 96 | switch (sscanf(line, " %lf %c", &value, &termch)) { 97 | case 1: 98 | FreeBlock(line); 99 | return (value); 100 | case 2: 101 | printf("Unexpected character: '%c'\n", termch); 102 | break; 103 | default: 104 | printf("Please enter a real number\n"); 105 | break; 106 | } 107 | FreeBlock(line); 108 | printf("Retry: "); 109 | } 110 | } 111 | 112 | /* 113 | * Function: GetLine 114 | * ----------------- 115 | * This function is a simple wrapper; all the work is done by 116 | * ReadLine. 117 | */ 118 | 119 | string GetLine(void) 120 | { 121 | return (ReadLine(stdin)); 122 | } 123 | 124 | /* 125 | * Function: ReadLine 126 | * ------------------ 127 | * This function operates by reading characters from the file 128 | * into a dynamically allocated buffer. 129 | * [We assume that none of the characters read is '\0'. (PF)] 130 | * If the buffer becomes 131 | * full before the end of the line is reached, a new buffer 132 | * twice the size of the previous one is allocated. 133 | */ 134 | 135 | string ReadLine(FILE *infile) 136 | { 137 | string line, nline; 138 | int n, ch, size; 139 | 140 | n = 0; 141 | size = InitialBufferSize; 142 | line = GetBlock(size + 1); 143 | while ((ch = getc(infile)) != '\n' && ch != EOF) { 144 | if (n == size) { 145 | size *= 2; 146 | nline = (string) GetBlock(size + 1); 147 | strncpy(nline, line, n); 148 | FreeBlock(line); 149 | line = nline; 150 | } 151 | line[n++] = ch; 152 | } 153 | if (n == 0 && ch == EOF) { 154 | FreeBlock(line); 155 | return (NULL); 156 | } 157 | line[n] = '\0'; 158 | nline = (string) GetBlock(n + 1); 159 | strcpy(nline, line); 160 | FreeBlock(line); 161 | return (nline); 162 | } 163 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/graphics.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: graphics.h 3 | * Version: 1.0 4 | * Last modified on Mon Jun 6 11:03:27 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This interface provides access to a simple library of 7 | * functions that make it possible to draw lines and arcs 8 | * on the screen. This interface presents a portable 9 | * abstraction that can be used with a variety of window 10 | * systems implemented on different hardware platforms. 11 | */ 12 | 13 | #ifndef _graphics_h 14 | #define _graphics_h 15 | 16 | /* 17 | * Overview 18 | * -------- 19 | * This library provides several functions for drawing lines 20 | * and circular arcs in a region of the screen that is 21 | * defined as the "graphics window." Once drawn, these 22 | * lines and arcs stay in their position, which means that 23 | * the package can only be used for static pictures and not 24 | * for animation. 25 | * 26 | * Individual points within the window are specified by 27 | * giving their x and y coordinates. These coordinates are 28 | * real numbers measured in inches, with the origin in the 29 | * lower left corner, as it is in traditional mathematics. 30 | * 31 | * The calls available in the package are listed below. More 32 | * complete descriptions are included with each function 33 | * description. 34 | * 35 | * InitGraphics(); 36 | * MovePen(x, y); 37 | * DrawLine(dx, dy); 38 | * DrawArc(r, start, sweep); 39 | * width = GetWindowWidth(); 40 | * height = GetWindowHeight(); 41 | * x = GetCurrentX(); 42 | * y = GetCurrentY(); 43 | */ 44 | 45 | /* 46 | * Function: InitGraphics 47 | * Usage: InitGraphics(); 48 | * ---------------------- 49 | * This procedure creates the graphics window on the screen. 50 | * The call to InitGraphics must precede any calls to other 51 | * functions in this package and must also precede any printf 52 | * output. In most cases, the InitGraphics call is the first 53 | * statement in the function main. 54 | */ 55 | 56 | void InitGraphics(void); 57 | 58 | /* 59 | * Function: MovePen 60 | * Usage: MovePen(x, y); 61 | * --------------------- 62 | * This procedure moves the current point to the position 63 | * (x, y), without drawing a line. The model is that of 64 | * the pen being lifted off the graphics window surface and 65 | * then moved to its new position. 66 | */ 67 | 68 | void MovePen(double x, double y); 69 | 70 | /* 71 | * Function: DrawLine 72 | * Usage: DrawLine(dx, dy); 73 | * ------------------------ 74 | * This procedure draws a line extending from the current 75 | * point by moving the pen dx inches in the x direction 76 | * and dy inches in the y direction. The final position 77 | * becomes the new current point. 78 | */ 79 | 80 | void DrawLine(double dx, double dy); 81 | 82 | /* 83 | * Function: DrawArc 84 | * Usage: DrawArc(r, start, sweep); 85 | * -------------------------------- 86 | * This procedure draws a circular arc, which always begins 87 | * at the current point. The arc itself has radius r, and 88 | * starts at the angle specified by the parameter start, 89 | * relative to the center of the circle. This angle is 90 | * measured in degrees counterclockwise from the 3 o'clock 91 | * position along the x-axis, as in traditional mathematics. 92 | * For example, if start is 0, the arc begins at the 3 o'clock 93 | * position; if start is 90, the arc begins at the 12 o'clock 94 | * position; and so on. The fraction of the circle drawn is 95 | * specified by the parameter sweep, which is also measured in 96 | * degrees. If sweep is 360, DrawArc draws a complete circle; 97 | * if sweep is 90, it draws a quarter of a circle. If the value 98 | * of sweep is positive, the arc is drawn counterclockwise from 99 | * the current point. If sweep is negative, the arc is drawn 100 | * clockwise from the current point. The current point at the 101 | * end of the DrawArc operation is the final position of the pen 102 | * along the arc. 103 | * 104 | * Examples: 105 | * DrawArc(r, 0, 360) Draws a circle to the left of the 106 | * current point. 107 | * DrawArc(r, 90, 180) Draws the left half of a semicircle 108 | * starting from the 12 o'clock position. 109 | * DrawArc(r, 0, 90) Draws a quarter circle from the 3 110 | * o'clock to the 12 o'clock position. 111 | * DrawArc(r, 0, -90) Draws a quarter circle from the 3 112 | * o'clock to the 6 o'clock position. 113 | * DrawArc(r, -90, -90) Draws a quarter circle from the 6 114 | * o'clock to the 9 o'clock position. 115 | */ 116 | 117 | void DrawArc(double r, double start, double sweep); 118 | 119 | /* 120 | * Functions: GetWindowWidth, GetWindowHeight 121 | * Usage: width = GetWindowWidth(); 122 | * height = GetWindowHeight(); 123 | * ------------------------------------------ 124 | * These functions return the width and height of the graphics 125 | * window, in inches. 126 | */ 127 | 128 | double GetWindowWidth(void); 129 | double GetWindowHeight(void); 130 | 131 | /* 132 | * Functions: GetCurrentX, GetCurrentY 133 | * Usage: x = GetCurrentX(); 134 | * y = GetCurrentY(); 135 | * ----------------------------------- 136 | * These functions return the current x and y positions. 137 | */ 138 | 139 | double GetCurrentX(void); 140 | double GetCurrentY(void); 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/strlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: strlib.c 3 | * Version: 1.0 4 | * Last modified on Fri Jul 15 14:10:41 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file implements the strlib.h interface. 7 | */ 8 | 9 | /* 10 | * General implementation notes: 11 | * ----------------------------- 12 | * This module implements the strlib library by mapping all 13 | * functions into the appropriate calls to the ANSI 14 | * interface. The implementations of the individual functions 15 | * are all quite simple and do not require individual comments. 16 | * For descriptions of the behavior of each function, see the 17 | * interface. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "genlib.h" 25 | #include "strlib.h" 26 | 27 | /* 28 | * Constant: MaxDigits 29 | * ------------------- 30 | * This constant must be larger than the maximum 31 | * number of digits that can appear in a number. 32 | */ 33 | 34 | #define MaxDigits 30 35 | 36 | /* Private function prototypes */ 37 | 38 | static string CreateString(int len); 39 | 40 | /* Section 1 -- Basic string operations */ 41 | 42 | string Concat(string s1, string s2) 43 | { 44 | string s; 45 | int len1, len2; 46 | 47 | if (s1 == NULL || s2 == NULL) { 48 | Error("NULL string passed to Concat"); 49 | } 50 | len1 = strlen(s1); 51 | len2 = strlen(s2); 52 | s = CreateString(len1 + len2); 53 | strcpy(s, s1); 54 | strcpy(s + len1, s2); 55 | return (s); 56 | } 57 | 58 | char IthChar(string s, int i) 59 | { 60 | int len; 61 | 62 | if (s == NULL) Error("NULL string passed to IthChar"); 63 | len = strlen(s); 64 | if (i < 0 || i > len) { 65 | Error("Index outside of string range in IthChar"); 66 | } 67 | return (s[i]); 68 | } 69 | 70 | string SubString(string s, int p1, int p2) 71 | { 72 | int len; 73 | string result; 74 | 75 | if (s == NULL) Error("NULL string passed to SubString"); 76 | len = strlen(s); 77 | if (p1 < 0) p1 = 0; 78 | if (p2 >= len) p2 = len - 1; 79 | len = p2 - p1 + 1; 80 | if (len < 0) len = 0; 81 | result = CreateString(len); 82 | strncpy(result, s + p1, len); 83 | result[len] = '\0'; 84 | return (result); 85 | } 86 | 87 | string CharToString(char ch) 88 | { 89 | string result; 90 | 91 | result = CreateString(1); 92 | result[0] = ch; 93 | result[1] = '\0'; 94 | return (result); 95 | } 96 | 97 | int StringLength(string s) 98 | { 99 | if (s == NULL) Error("NULL string passed to StringLength"); 100 | return (strlen(s)); 101 | } 102 | 103 | string CopyString(string s) 104 | { 105 | string newstr; 106 | 107 | if (s == NULL) Error("NULL string passed to CopyString"); 108 | newstr = CreateString(strlen(s)); 109 | strcpy(newstr, s); 110 | return (newstr); 111 | } 112 | 113 | /* Section 2 -- String comparison functions */ 114 | 115 | bool StringEqual(string s1, string s2) 116 | { 117 | if (s1 == NULL || s2 == NULL) { 118 | Error("NULL string passed to StringEqual"); 119 | } 120 | return (strcmp(s1, s2) == 0); 121 | } 122 | 123 | int StringCompare(string s1, string s2) 124 | { 125 | if (s1 == NULL || s2 == NULL) { 126 | Error("NULL string passed to StringCompare"); 127 | } 128 | return (strcmp(s1, s2)); 129 | } 130 | 131 | /* Section 3 -- Search functions */ 132 | 133 | int FindChar(char ch, string text, int start) 134 | { 135 | char *cptr; 136 | 137 | if (text == NULL) Error("NULL string passed to FindChar"); 138 | if (start < 0) start = 0; 139 | if (start > strlen(text)) return (-1); 140 | cptr = strchr(text + start, ch); 141 | if (cptr == NULL) return (-1); 142 | return ((int) (cptr - text)); 143 | } 144 | 145 | int FindString(string str, string text, int start) 146 | { 147 | char *cptr; 148 | 149 | if (str == NULL) Error("NULL pattern string in FindString"); 150 | if (text == NULL) Error("NULL text string in FindString"); 151 | if (start < 0) start = 0; 152 | if (start > strlen(text)) return (-1); 153 | cptr = strstr(text + start, str); 154 | if (cptr == NULL) return (-1); 155 | return ((int) (cptr - text)); 156 | } 157 | 158 | /* Section 4 -- Case-conversion functions */ 159 | 160 | string ConvertToLowerCase(string s) 161 | { 162 | string result; 163 | int i; 164 | 165 | if (s == NULL) { 166 | Error("NULL string passed to ConvertToLowerCase"); 167 | } 168 | result = CreateString(strlen(s)); 169 | for (i = 0; s[i] != '\0'; i++) result[i] = tolower(s[i]); 170 | result[i] = '\0'; 171 | return (result); 172 | } 173 | 174 | string ConvertToUpperCase(string s) 175 | { 176 | string result; 177 | int i; 178 | 179 | if (s == NULL) { 180 | Error("NULL string passed to ConvertToUpperCase"); 181 | } 182 | result = CreateString(strlen(s)); 183 | for (i = 0; s[i] != '\0'; i++) result[i] = toupper(s[i]); 184 | result[i] = '\0'; 185 | return (result); 186 | } 187 | 188 | /* Section 5 -- Functions for converting numbers to strings */ 189 | 190 | string IntegerToString(int n) 191 | { 192 | char buffer[MaxDigits]; 193 | 194 | sprintf(buffer, "%d", n); 195 | return (CopyString(buffer)); 196 | } 197 | 198 | int StringToInteger(string s) 199 | { 200 | int result; 201 | char dummy; 202 | 203 | if (s == NULL) { 204 | Error("NULL string passed to StringToInteger"); 205 | } 206 | if (sscanf(s, " %d %c", &result, &dummy) != 1) { 207 | Error("StringToInteger called on illegal number %s", s); 208 | } 209 | return (result); 210 | } 211 | 212 | string RealToString(double d) 213 | { 214 | char buffer[MaxDigits]; 215 | 216 | sprintf(buffer, "%G", d); 217 | return (CopyString(buffer)); 218 | } 219 | 220 | double StringToReal(string s) 221 | { 222 | double result; 223 | char dummy; 224 | 225 | if (s == NULL) Error("NULL string passed to StringToReal"); 226 | if (sscanf(s, " %lg %c", &result, &dummy) != 1) { 227 | Error("StringToReal called on illegal number %s", s); 228 | } 229 | return (result); 230 | } 231 | 232 | /* Private functions */ 233 | 234 | /* 235 | * Function: CreateString 236 | * Usage: s = CreateString(len); 237 | * ----------------------------- 238 | * This function dynamically allocates space for a string of 239 | * len characters, leaving room for the null character at the 240 | * end. 241 | */ 242 | 243 | static string CreateString(int len) 244 | { 245 | return ((string) GetBlock(len + 1)); 246 | } 247 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/genlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: genlib.h 3 | * Version: 1.0 4 | * Last modified on Sun Jul 24 10:32:49 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file contains several definitions that form the 7 | * core of a general-purpose ANSI C library developed by Eric 8 | * Roberts. The goal of this library is to provide a basic 9 | * set of tools and conventions that increase the readability 10 | * of C programs, particularly as they are used in a teaching 11 | * environment. 12 | * 13 | * The basic definitions provided by genlib.h are: 14 | * 15 | * 1. Declarations for several new "primitive" types 16 | * (most importantly bool and string) that are 17 | * used throughout the other libraries and 18 | * applications as fundamental types. 19 | * 20 | * 2. A new set of functions for memory allocation. 21 | * 22 | * 3. A function for error handling. 23 | * 24 | * 4. A repeat statement for loops with interior exits. 25 | */ 26 | 27 | #ifndef _genlib_h 28 | #define _genlib_h 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | /* Section 1 -- Define new "primitive" types */ 35 | 36 | /* 37 | * Type: bool 38 | * ---------- 39 | * This type has two values, FALSE and TRUE, which are equal to 0 40 | * and 1, respectively. Most of the advantage of defining this type 41 | * comes from readability because it allows the programmer to 42 | * provide documentation that a variable will take on only one of 43 | * these two values. Designing a portable representation, however, 44 | * is surprisingly hard, because many libraries and some compilers 45 | * define these names. The definitions are usually compatible but 46 | * may still be flagged as errors. 47 | */ 48 | 49 | #ifdef THINK_C 50 | typedef int bool; 51 | #else 52 | # ifdef TRUE 53 | # ifndef bool 54 | # define bool int 55 | # endif 56 | # else 57 | # ifdef bool 58 | # define FALSE 0 59 | # define TRUE 1 60 | # else 61 | typedef enum {FALSE, TRUE} bool; 62 | # endif 63 | # endif 64 | #endif 65 | 66 | /* 67 | * Type: string 68 | * ------------ 69 | * The type string is identical to the type char *, which is 70 | * traditionally used in C programs. The main point of defining a 71 | * new type is to improve program readability. At the abstraction 72 | * levels at which the type string is used, it is usually not 73 | * important to take the string apart into its component characters. 74 | * Declaring it as a string emphasizes this atomicity. 75 | */ 76 | 77 | typedef char *string; 78 | 79 | /* 80 | * Type: stream 81 | * ------------ 82 | * Like string, the stream type is used to provide additional 83 | * readability and is defined to be equivalent to FILE * 84 | * (which is particularly confusing because it violates 85 | * standard case conventions). This type is not used in 86 | * the text but is preserved in genlib.h, so it is possible 87 | * to teach all of CS1 without exposing any pointers. 88 | */ 89 | 90 | typedef FILE *stream; 91 | 92 | /* 93 | * Constant: UNDEFINED 94 | * ------------------- 95 | * Besides NULL, the only other constant of pointer type is 96 | * UNDEFINED, which is used in certain packages as a special 97 | * sentinel to indicate an undefined pointer value. In many 98 | * such contexts, NULL is a legitimate data value and is 99 | * therefore inappropriate as a sentinel. 100 | */ 101 | 102 | #define UNDEFINED ((void *) undefined_object) 103 | 104 | extern char undefined_object[]; 105 | 106 | /* Section 2 -- Memory allocation */ 107 | 108 | /* 109 | * General notes: 110 | * -------------- 111 | * These functions provide a common interface for memory 112 | * allocation. All functions in the library that allocate 113 | * memory do so using GetBlock and FreeBlock. Even though 114 | * the ANSI standard defines malloc and free for the same 115 | * purpose, using GetBlock and FreeBlock provides greater 116 | * compatibility with non-ANSI implementations, automatic 117 | * out-of-memory error detection, and the possibility of 118 | * substituting a garbage-collecting allocator. 119 | */ 120 | 121 | /* 122 | * Function: GetBlock 123 | * Usage: ptr = (type) GetBlock(nbytes); 124 | * ------------------------------------- 125 | * GetBlock allocates a block of memory of the given size. If 126 | * no memory is available, GetBlock generates an error. 127 | */ 128 | 129 | void *GetBlock(size_t nbytes); 130 | 131 | /* 132 | * Function: FreeBlock 133 | * Usage: FreeBlock(ptr); 134 | * ---------------------- 135 | * FreeBlock frees the memory associated with ptr, which must 136 | * have been allocated using GetBlock, New, or NewArray. 137 | */ 138 | 139 | void FreeBlock(void *ptr); 140 | 141 | /* 142 | * Macro: New 143 | * Usage: p = New(pointer-type); 144 | * ----------------------------- 145 | * The New pseudofunction allocates enough space to hold an 146 | * object of the type to which pointer-type points and returns 147 | * a pointer to the newly allocated pointer. Note that 148 | * "New" is different from the "new" operator used in C++; 149 | * the former takes a pointer type and the latter takes the 150 | * target type. 151 | */ 152 | 153 | #define New(type) ((type) GetBlock(sizeof *((type) NULL))) 154 | 155 | /* 156 | * Macro: NewArray 157 | * Usage: p = NewArray(n, element-type); 158 | * ------------------------------------- 159 | * NewArray allocates enough space to hold an array of n 160 | * values of the specified element type. 161 | */ 162 | 163 | #define NewArray(n, type) ((type *) GetBlock((n) * sizeof (type))) 164 | 165 | /* Section 3 -- Basic error handling */ 166 | 167 | /* 168 | * Function: Error 169 | * Usage: Error(msg, ...) 170 | * ---------------------- 171 | * Error generates an error string, expanding % constructions 172 | * appearing in the error message string just as printf does. 173 | * If an error handler exception has been introduced (see the 174 | * "exception.h" facility), the ErrorException exception is 175 | * raised with the expanded error string as argument. If 176 | * there is no ErrorException defined, the program exits 177 | * with a status code indicating failure (as given by the 178 | * constant ErrorExitStatus). The length of the error 179 | * message string following expansion must not exceed 180 | * MaxErrorMessage, and it is the client's responsibility 181 | * to ensure this. 182 | */ 183 | 184 | void Error(string msg, ...); 185 | 186 | /* Section 4 -- The repeat pseudo-statement */ 187 | 188 | /* 189 | * Statement form: repeat { ... } 190 | * ------------------------------ 191 | * Some instructors who have taught CS1 using this library 192 | * have found that using 193 | * 194 | * while (TRUE) 195 | * 196 | * to initiate a loop with an interior exit is confusing to 197 | * students, particularly when it comes at the beginning of 198 | * the course. This macro defines "repeat" as an infinite 199 | * loop construct for instructors who find it easier to 200 | * explain, although it is not used in the text. Similar 201 | * macro definitions are common in industry. 202 | */ 203 | 204 | #define repeat for (;;) 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/xdisplay.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xdisplay.h 3 | * Version: 3.0 4 | * Last modified on Sat Oct 1 12:25:08 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This interface is responsible for the actual display operations 7 | * of the X manager phase of the graphics.c implementation. In many 8 | * cases, the functions in this interface are similar to those in 9 | * the client interface to the graphics library. The graphics.h 10 | * interface, for example, contains DrawLine and DrawArc; this 11 | * interface exports XDDrawLine and XDDrawArc. The difference 12 | * is that the graphics.h functions simply send commands down a 13 | * communication channel, while the xdisplay.h functions actually 14 | * perform the rendering operations for the X window manager. 15 | */ 16 | 17 | #ifndef _xdisplay_h 18 | #define _xdisplay_h 19 | 20 | #include "genlib.h" 21 | 22 | /* 23 | * Function: XDOpenDisplay 24 | * Usage: XDOpenDisplay(title); 25 | * ---------------------------- 26 | * This function creates a display window and performs all the 27 | * necessary initialization for the X system. The title string 28 | * is displayed in the title bar. 29 | */ 30 | 31 | void XDOpenDisplay(string title); 32 | 33 | /* 34 | * Function: XDCloseDisplay 35 | * Usage: XDCloseDisplay(); 36 | * ------------------------ 37 | * This function closes the graphics window and frees all of the 38 | * X storage. 39 | */ 40 | 41 | void XDCloseDisplay(void); 42 | 43 | /* 44 | * Function: XDDisplayFD 45 | * Usage: fd = XDDisplayFD(); 46 | * -------------------------- 47 | * This function returns the Unix file descriptor of the X 48 | * connection to the graphics window display. 49 | */ 50 | 51 | int XDDisplayFD(void); 52 | 53 | /* 54 | * Function: XDProcessXEvent 55 | * Usage: if (XDProcessXEvent()) . . . 56 | * ----------------------------------- 57 | * This function checks to see if there is a pending X event 58 | * and, if so, processes that event. The function returns 59 | * TRUE if an event was detected and FALSE if no events were 60 | * pending so that the client can set a timeout before testing 61 | * again for another event. 62 | */ 63 | 64 | bool XDProcessXEvent(void); 65 | 66 | /* 67 | * Function: XDCheckForRedraw 68 | * Usage: XDCheckForRedraw(); 69 | * -------------------------- 70 | * This function checks to see if the window needs redrawing and, 71 | * if so, issues the X commands to update the screen. 72 | */ 73 | 74 | void XDCheckForRedraw(void); 75 | 76 | /* 77 | * Function: XDSetRedrawFlag 78 | * Usage: XDSetRedrawFlag(); 79 | * ------------------------- 80 | * This function informs the X display module that the screen needs 81 | * to be redrawn. 82 | */ 83 | 84 | void XDSetRedrawFlag(void); 85 | 86 | /* 87 | * Function: XDClearDisplay 88 | * Usage: XDClearDisplay(); 89 | * ------------------------ 90 | * XDClearDisplay erases the entire contents of the graphics window. 91 | */ 92 | 93 | void XDClearDisplay(void); 94 | 95 | /* 96 | * Function: XDDrawLine 97 | * Usage: XDDrawLine(x, y, dx, dy); 98 | * -------------------------------- 99 | * This function draws a line from (x, y) to (x+dx, y+dy). 100 | */ 101 | 102 | void XDDrawLine(double x, double y, double dx, double dy); 103 | 104 | /* 105 | * Function: XDDrawArc 106 | * Usage: XDDrawArc(x, y, rx, ry, start, sweep); 107 | * --------------------------------------------- 108 | * This function draws an elliptical arc segment centered at 109 | * (x, y) with cartesian radii rx and ry. Note that the 110 | * interpretation of (x, y) is not the current point as 111 | * it is in the DrawArc call. The start and sweep parameters 112 | * are interpreted as they are in DrawArc. 113 | */ 114 | 115 | void XDDrawArc(double x, double y, double rx, double ry, 116 | double start, double sweep); 117 | 118 | /* 119 | * Function: XDDrawText 120 | * Usage: XDDrawText(x, y, text); 121 | * ------------------------------ 122 | * This function displays the string text at (x, y) using the 123 | * current font and size. 124 | */ 125 | 126 | void XDDrawText(double x, double y, string text); 127 | 128 | /* 129 | * Function: XDTextWidth 130 | * Usage: width = XDTextWidth(text); 131 | * --------------------------------- 132 | * This function returns the width of the text string at the 133 | * current font and size. 134 | */ 135 | 136 | double XDTextWidth(string text); 137 | 138 | /* 139 | * Function: XDSetFont 140 | * Usage: str = XDSetFont(font, size, style); 141 | * ------------------------------------------ 142 | * This function finds the appropriate font/size/style combination 143 | * based on the user's request and returns a string consisting of 144 | * the size, style, and font names each separated by a space. 145 | */ 146 | 147 | string XDSetFont(string font, int size, int style); 148 | 149 | /* 150 | * Function: XDSetTitle 151 | * Usage: XDSetTitle(title); 152 | * ------------------------- 153 | * This function sets the title bar for the window. 154 | */ 155 | 156 | void XDSetTitle(string title); 157 | 158 | /* 159 | * Function: XDSetEraseMode 160 | * Usage: XDSetEraseMode(flag); 161 | * ---------------------------- 162 | * This function sets the erase mode setting according to the 163 | * Boolean flag. 164 | */ 165 | 166 | void XDSetEraseMode(bool flag); 167 | 168 | /* 169 | * Function: XDStartRegion 170 | * Usage: XDStartRegion(grayScale); 171 | * -------------------------------- 172 | * The XDStartRegion function sets the internal state so 173 | * that subsequent calls to XDDrawLine and XDDrawArc (which 174 | * have already been checked by the client to ensure that they 175 | * form a connected path) are used to create a fillable polygon. 176 | * The region is displayed by the corresponding XDEndRegion. 177 | */ 178 | 179 | void XDStartRegion(double grayScale); 180 | 181 | /* 182 | * Function: XDEndRegion 183 | * Usage: XDEndRegion(); 184 | * --------------------- 185 | * This function closes the region opened by XDStartRegion. 186 | */ 187 | 188 | void XDEndRegion(void); 189 | 190 | /* 191 | * Function: XDGetMouse 192 | * Usage: XDGetMouse(&buttonState, &x, &y); 193 | * ---------------------------------------- 194 | * This function returns the current mouse state through the 195 | * argument pointers. 196 | */ 197 | 198 | void XDGetMouse(bool *buttonStateP, double *xp, double *yp); 199 | 200 | /* 201 | * Function: XDWaitForMouse 202 | * Usage: XDWaitForMouse(buttonState); 203 | * ----------------------------------- 204 | * This function waits for the mouse to enter the specified 205 | * state (down = TRUE); 206 | */ 207 | 208 | void XDWaitForMouse(bool buttonState); 209 | 210 | /* 211 | * Function: XDSetWindowSize 212 | * Usage: XDSetWindowSize(width, height); 213 | * -------------------------------------- 214 | * This function sets the width and height values of the window 215 | * and must be called prior to XDOpenDisplay. 216 | */ 217 | 218 | void XDSetWindowSize(double width, double height); 219 | 220 | /* 221 | * Function: XDGetScreenSize 222 | * Usage: XDGetScreenSize(&screenWidth, &screenHeight); 223 | * ---------------------------------------------------- 224 | * This function returns the screen size and may be called in the 225 | * following circumstances, each of which is unusual: 226 | * (a) prior to the XDOpenDisplay call 227 | * (b) from the client fork 228 | */ 229 | 230 | void XDGetScreenSize(double *pScreenWidth, double *pScreenHeight); 231 | 232 | /* 233 | * Function: XDGetResolution 234 | * Usage: XDGetResolution(&xdpi, &ydpi); 235 | * ------------------------------------- 236 | * This function returns the screen resolution, possibly modified by 237 | * the scale reduction. Like XDGetScreenSize, this function 238 | * can be called prior to the XDOpenDisplay call or from the client 239 | * fork. 240 | */ 241 | 242 | void XDGetResolution(double *pXDPI, double *pYDPI); 243 | 244 | /* 245 | * Function: XDGetNColors 246 | * Usage: ncolors = XDGetNColors(); 247 | * -------------------------------- 248 | * This function returns the number of colors supported by the screen. 249 | * This function can be called prior to the XDOpenDisplay call or from 250 | * the client fork. 251 | */ 252 | 253 | int XDGetNColors(void); 254 | 255 | /* 256 | * Function: XDSetColor 257 | * Usage: XDSetColor(red, green, blue); 258 | * ------------------------------------ 259 | * This function sets the pen color as specified by the arguments. 260 | */ 261 | 262 | void XDSetColor(double red, double green, double blue); 263 | 264 | #endif 265 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/strlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: strlib.h 3 | * Version: 1.0 4 | * Last modified on Fri Jul 15 14:10:40 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * The strlib.h file defines the interface for a simple 7 | * string library. In the context of this package, strings 8 | * are considered to be an abstract data type, which means 9 | * that the client relies only on the operations defined for 10 | * the type and not on the underlying representation. 11 | */ 12 | 13 | /* 14 | * Cautionary note: 15 | * ---------------- 16 | * Although this interface provides an extremely convenient 17 | * abstraction for working with strings, it is not appropriate 18 | * for all applications. In this interface, the functions that 19 | * return string values (such as Concat and SubString) do so 20 | * by allocating new memory. Over time, a program that uses 21 | * this package will consume increasing amounts of memory 22 | * and eventually exhaust the available supply. If you are 23 | * writing a program that runs for a short time and stops, 24 | * the fact that the package consumes memory is not a problem. 25 | * If, however, you are writing an application that must run 26 | * for an extended period of time, using this package requires 27 | * that you make some provision for freeing any allocated 28 | * storage. 29 | */ 30 | 31 | #ifndef _strlib_h 32 | #define _strlib_h 33 | 34 | #include "genlib.h" 35 | 36 | /* Section 1 -- Basic string operations */ 37 | 38 | /* 39 | * Function: Concat 40 | * Usage: s = Concat(s1, s2); 41 | * -------------------------- 42 | * This function concatenates two strings by joining them end 43 | * to end. For example, Concat("ABC", "DE") returns the string 44 | * "ABCDE". 45 | */ 46 | 47 | string Concat(string s1, string s2); 48 | 49 | /* 50 | * Function: IthChar 51 | * Usage: ch = IthChar(s, i); 52 | * -------------------------- 53 | * This function returns the character at position i in the 54 | * string s. It is included in the library to make the type 55 | * string a true abstract type in the sense that all of the 56 | * necessary operations can be invoked using functions. Calling 57 | * IthChar(s, i) is like selecting s[i], except that IthChar 58 | * checks to see if i is within the range of legal index 59 | * positions, which extend from 0 to StringLength(s). 60 | * IthChar(s, StringLength(s)) returns the null character 61 | * at the end of the string. 62 | */ 63 | 64 | char IthChar(string s, int i); 65 | 66 | /* 67 | * Function: SubString 68 | * Usage: t = SubString(s, p1, p2); 69 | * -------------------------------- 70 | * SubString returns a copy of the substring of s consisting 71 | * of the characters between index positions p1 and p2, 72 | * inclusive. The following special cases apply: 73 | * 74 | * 1. If p1 is less than 0, it is assumed to be 0. 75 | * 2. If p2 is greater than the index of the last string 76 | * position, which is StringLength(s) - 1, then p2 is 77 | * set equal to StringLength(s) - 1. 78 | * 3. If p2 < p1, SubString returns the empty string. 79 | */ 80 | 81 | string SubString(string s, int p1, int p2); 82 | 83 | /* 84 | * Function: CharToString 85 | * Usage: s = CharToString(ch); 86 | * ---------------------------- 87 | * This function takes a single character and returns a 88 | * one-character string consisting of that character. The 89 | * CharToString function is useful, for example, if you 90 | * need to concatenate a string and a character. Since 91 | * Concat requires two strings, you must first convert 92 | * the character into a string. 93 | */ 94 | 95 | string CharToString(char ch); 96 | 97 | /* 98 | * Function: StringLength 99 | * Usage: len = StringLength(s); 100 | * ----------------------------- 101 | * This function returns the length of s. 102 | */ 103 | 104 | int StringLength(string s); 105 | 106 | /* 107 | * Function: CopyString 108 | * Usage: newstr = CopyString(s); 109 | * ------------------------------ 110 | * CopyString copies the string s into dynamically allocated 111 | * storage and returns the new string. This function is not 112 | * ordinarily required if this package is used on its own, 113 | * but is often necessary when you are working with more than 114 | * one string package. 115 | */ 116 | 117 | string CopyString(string s); 118 | 119 | /* Section 2 -- String comparison functions */ 120 | 121 | /* 122 | * Function: StringEqual 123 | * Usage: if (StringEqual(s1, s2)) ... 124 | * ----------------------------------- 125 | * This function returns TRUE if the strings s1 and s2 are 126 | * equal. For the strings to be considered equal, every 127 | * character in one string must precisely match the 128 | * corresponding character in the other. Uppercase and 129 | * lowercase characters are considered to be different. 130 | */ 131 | 132 | bool StringEqual(string s1, string s2); 133 | 134 | /* 135 | * Function: StringCompare 136 | * Usage: if (StringCompare(s1, s2) < 0) ... 137 | * ----------------------------------------- 138 | * This function returns a number less than 0 if string s1 139 | * comes before s2 in alphabetical order, 0 if they are equal, 140 | * and a number greater than 0 if s1 comes after s2. The 141 | * ordering is determined by the internal representation used 142 | * for characters, which is usually ASCII. 143 | */ 144 | 145 | int StringCompare(string s1, string s2); 146 | 147 | /* Section 3 -- Search functions */ 148 | 149 | /* 150 | * Function: FindChar 151 | * Usage: p = FindChar(ch, text, start); 152 | * ------------------------------------- 153 | * Beginning at position start in the string text, this 154 | * function searches for the character ch and returns the 155 | * first index at which it appears or -1 if no match is 156 | * found. 157 | */ 158 | 159 | int FindChar(char ch, string text, int start); 160 | 161 | /* 162 | * Function: FindString 163 | * Usage: p = FindString(str, text, start); 164 | * ---------------------------------------- 165 | * Beginning at position start in the string text, this 166 | * function searches for the string str and returns the 167 | * first index at which it appears or -1 if no match is 168 | * found. 169 | */ 170 | 171 | int FindString(string str, string text, int start); 172 | 173 | /* Section 4 -- Case-conversion functions */ 174 | 175 | /* 176 | * Function: ConvertToLowerCase 177 | * Usage: s = ConvertToLowerCase(s); 178 | * --------------------------------- 179 | * This function returns a new string with all 180 | * alphabetic characters converted to lower case. 181 | */ 182 | 183 | string ConvertToLowerCase(string s); 184 | 185 | /* 186 | * Function: ConvertToUpperCase 187 | * Usage: s = ConvertToUpperCase(s); 188 | * --------------------------------- 189 | * This function returns a new string with all 190 | * alphabetic characters converted to upper case. 191 | */ 192 | 193 | string ConvertToUpperCase(string s); 194 | 195 | /* Section 5 -- Functions for converting numbers to strings */ 196 | 197 | /* 198 | * Function: IntegerToString 199 | * Usage: s = IntegerToString(n); 200 | * ------------------------------ 201 | * This function converts an integer into the corresponding 202 | * string of digits. For example, IntegerToString(123) 203 | * returns "123" as a string. 204 | */ 205 | 206 | string IntegerToString(int n); 207 | 208 | /* 209 | * Function: StringToInteger 210 | * Usage: n = StringToInteger(s); 211 | * ------------------------------ 212 | * This function converts a string of digits into an integer. 213 | * If the string is not a legal integer or contains extraneous 214 | * characters, StringToInteger signals an error condition. 215 | */ 216 | 217 | int StringToInteger(string s); 218 | 219 | /* 220 | * Function: RealToString 221 | * Usage: s = RealToString(d); 222 | * --------------------------- 223 | * This function converts a floating-point number into the 224 | * corresponding string form. For example, calling 225 | * RealToString(23.45) returns "23.45". The conversion is 226 | * the same as that used for "%G" format in printf. 227 | */ 228 | 229 | string RealToString(double d); 230 | 231 | /* 232 | * Function: StringToReal 233 | * Usage: d = StringToReal(s); 234 | * --------------------------- 235 | * This function converts a string representing a real number 236 | * into its corresponding value. If the string is not a 237 | * legal floating-point number or if it contains extraneous 238 | * characters, StringToReal signals an error condition. 239 | */ 240 | 241 | double StringToReal(string s); 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /ch1/review_questions.md: -------------------------------------------------------------------------------- 1 | ### 1. What is the difference between a source file and an object file? 2 | 3 | A source file contains...source code. An object file is a compiled 4 | version of that source file, before it is linked with libraries and such. 5 | 6 | ### 2. What characters are used to mark comments in a C program? 7 | 8 | `/*` & `*/` are introduced in this book. `//` works for single-line comments. 9 | 10 | ### 3. In an `#include` line, the name of the library header file can be enclosed in either angle brackets or double quotation marks. What is the difference between the two forms of punctuation? 11 | 12 | Angle brackets are for system libraries. Double quotation marks are for your 13 | own header files. 14 | 15 | ### 4. How would you define a constant called CentimetersPerInch with the value 2.54? 16 | 17 | Well, I'd call it `CENTIMETERS_PER_INCH` for starters. Here you go: 18 | 19 | `#define CENTIMETERS_PER_INCH 2.54` 20 | 21 | ### 5. What is the name of the function that must be defined in every C program? 22 | 23 | main() 24 | 25 | ### 6. What is the purpose of the special character `\n` that appears at the end of most strings passed to printf? 26 | 27 | It ends the current line of output and begins another. 28 | 29 | ### 7. What four properties are established when you declare a variable? 30 | 31 | Type, scope, name, lifetime (isn't this tied to scope?) 32 | 33 | ### 8. Indicate which of the following are legal variable names in C: 34 | 35 | * `x` -> yes 36 | * `formula1` -> yes 37 | * `average_rainfall` -> yes 38 | * `%correct` -> no, has to start with a letter 39 | * `short` -> nope, that's a keyword 40 | * `tiny` -> yes 41 | * `total output` -> no, can't have spaces 42 | * `aReasonablyLongVariableName` -> yes, hello Java 43 | * `12MonthTotal` -> no, has to start with a letter 44 | * `marginal-cost` -> no hyphens 45 | * `b4hand` -> yes 46 | * `_stk_depth` -> yes (had to test this one, see [here](as_i_read/vars.c)) 47 | 48 | ### 9. What are the two attributes that define a data type? 49 | 50 | Domain and operations. 51 | 52 | ### 10. What is the difference between the types `short`, `int`, and `long`? 53 | 54 | The size of an implementation's long has to be greater than or equal to that 55 | of an int, which has to be greater than or equal to that of a short. 56 | Clang's limits.h gives 32767, 2,147,483,647 and 9.2e18, respectively 57 | (see [here](as_i_read/ints.c)) 58 | 59 | ### 11. What does ASCII stand for? 60 | 61 | American Standard Code for Information Interchange. Yep, had to look that 62 | one up. 63 | 64 | ### 12. List all possible values of type `bool`. 65 | 66 | { TRUE, FALSE }. Although Clang tells me there's no such thing as a bool type. 67 | 68 | ### 13. What statements would you include in a program to read a value from the user and store it in the variable `x`, which is declared as a double? 69 | 70 | This book would lead me to use GetReal() from the book's [I/O library](../book_code/unix-xwindows/simpio.h). [Yea, get real is right](as_i_read/double.c): 71 | 72 | scanf("%lf", &x); 73 | 74 | ### 14. Suppose that a function contains the following declarations: 75 | * `int i;` 76 | * `long l;` 77 | * `float f;` 78 | * `double d;` 79 | * `char c;` 80 | * `string s;` 81 | ### Write a series of printf calls that display the valyes of each of these variables on the screen. 82 | 83 | * `printf("%d", i);` 84 | * `printf("%ld", l);` 85 | * `printf("%f", f);` 86 | * `printf("%lf", d);` 87 | * `printf("%c", c);` 88 | * `printf("$s", s);` 89 | 90 | ### 15. Indicate the values and types of the following expressions: 91 | 92 | * `2 + 3` = 5, int 93 | * `19 / 5` = 3, int 94 | * `19.0 / 5` = 3.80, float 95 | * `3 * 6.0` = 18.0, float 96 | * `19 % 5` = 4, int 97 | * `2 % 7` = 2, int 98 | 99 | ### 16. What is the difference between the unary minus operator and the binary subtraction operator? 100 | 101 | Unary minus negates a value. Binary substraction subtracts one value from another. Also unary operators have higher precedence. 102 | 103 | ### 17. What does the term _truncation_ mean? 104 | 105 | In the context of C, _truncation_ is a loss of precision: 106 | 107 | int x; 108 | x = 2.5 / 1; 109 | printf("%d", x); // Prints 2.00000 110 | 111 | ### 18. By applying the appropriate precedence rules, calculate the result of each of the following expressions: 112 | 113 | * `6 + 5 / 4 - 3` = 4 114 | * `2 + 2 * (2 * 2 - 2) % 2 / 2` = 2 115 | * `10 + 9 * ((8 + 7) % 6) + 5 * 4 % 3 * 2 + 1` = 42 116 | * `1 + 2 + (3 + 4) * ((5 * 6 % 7 * 8) - 9) - 10` = 42 117 | 118 | ### 19. How do you specify a shorthand assignment operation? 119 | 120 | * `operand1 (operator)= operand2` 121 | * `x *= 2` doubles x. 122 | 123 | ### 20. What is the difference between the expressions `++x` and `x++`? 124 | 125 | `++x` is incremented before its value is returned, where `x++` returns the value of `x`, then increments it. 126 | 127 | ### 21. What does the term _short-circuit evaluation_ mean? 128 | 129 | When a boolean expression is evaluated, the evaluation completes as early as its value becomes clear. So in the following: 130 | 131 | int x = 1; 132 | int y = 2; 133 | if (x == 1 || y == 3) ... 134 | 135 | the expression `y == 3` is never evaluated, because only one of the expressions needs to be true, and the first is indeed true. 136 | 137 | ### 22. Write out the general synctactic form for each of the following control statements: `if`, `switch`, `while`, `for`. 138 | 139 | //This does not repeat. 140 | if(conditional_expression) { 141 | statements; 142 | } else { 143 | statements; 144 | } 145 | 146 | switch(expression) { 147 | case const1: 148 | statement; 149 | break; 150 | case const2: 151 | statement; 152 | break; 153 | default: 154 | statement; 155 | break;` 156 | } 157 | 158 | //This repeats until the conditional_expression is false. 159 | while(conditional_expression) { 160 | statements; 161 | } 162 | 163 | for(initialization; conditional_expression; step) { 164 | statements; 165 | } 166 | 167 | ### 23. Describe in English the general operation of the switch statement. 168 | 169 | Evaluate the expression inside of switch's parentheses. Run through each case statement, comparing the case's constant to the switch value. If equal, execute the associated statements. If not, move on to the next case statement. If you hit no matching values, execute `default`'s statements if it has been specified. If at any time you hit a `break;` statement, stop executing the switch statement. 170 | 171 | ### 24. What is a sentinel? 172 | 173 | A sentinel is a value that you use in a `while` loop to break out of the loop. It allows you to enclose all of the loop's functions inside of the `while` loop, because you often need to read a value before you evaluate a conditional expression. Here's an example: 174 | 175 | while (1) { 176 | printf("Please enter an integer. Enter 0 when you're done: "); 177 | scanf("%d", x); 178 | if (x == 0) { 179 | break; 180 | } 181 | //do something with x. 182 | } 183 | 184 | ### 25. What `for` loop control line would you use in each of the following situations: 185 | 186 | * Counting from 1 to 100 187 | 188 | `for (i=1;i<=100;i++)` 189 | 190 | * Counting by sevens starting at 0 until the number is three digits long 191 | 192 | `for(i=0;i<100;i+=7)` 193 | 194 | * Counting backward by twos from 100 to 0 195 | 196 | `for(i=100;i>=0;i-=2)` 197 | 198 | ### 26. What is a function prototype? 199 | 200 | A function prototype specifies a function's return values and its arguments. I am still not entirely clear on why one needs to include a prototype, but perhaps I'll learn later. 201 | 202 | ### 27. In your own words, describe what happens when you call a function in C. 203 | 204 | * The calling program evaluates the function's arguments. 205 | * A stack frame is created, containing space for all of the function's local variables. 206 | * The function's parameters are assigned their values. Type conversions are performed if necessary. 207 | * The function's statements are executed. 208 | * The `return` expression is evaluated and returned as the value of the function, if there is a return expression (some functions don't return anything). A type conversion (from the return value's type to the function's declared type) is performed if necessary. 209 | * The stack frame goes bye bye. 210 | * The calling program goes on its merry way. 211 | 212 | ### 28. What is meant by the term _stepwise refinement_? 213 | 214 | As you create your program, you will probably need to break it up into parts, enclosed in functions. As you implement everything, you might need to continue breaking things down into smaller functions until every piece is simple. Gotta love procedural programming. 215 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/exception.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: exception.h 3 | * Version: 1.0 4 | * Last modified on Thu Feb 23 12:45:01 1995 by eroberts 5 | * ----------------------------------------------------- 6 | * The exception package provides a general exception 7 | * handling mechanism for use with C that is portable 8 | * across a variety of compilers and operating systems. 9 | */ 10 | 11 | #ifndef _exception_h 12 | #define _exception_h 13 | 14 | /* 15 | * Overview: 16 | * -------- 17 | * The exception package makes it possible for clients to 18 | * specify a handler for an exceptional conditions in a 19 | * syntactically readable way. As a client, your first step 20 | * is to declare an exception condition name by declaring 21 | * a variable of type exception, as in 22 | * 23 | * exception MyException; 24 | * 25 | * Normal visibility rules apply, so that you should declare 26 | * the exception variable at the appropriate level. For 27 | * example, if an exception is local to an implementation, 28 | * it should be declared statically within that module. If 29 | * an exception condition is shared by many modules, the 30 | * exception variable should be declared in an interface 31 | * and exported to all clients that need it. This package 32 | * defines and exports the exception ErrorException, which 33 | * is likely to be sufficient for many clients. 34 | * 35 | * The basic functionality of exceptions is that one piece 36 | * of code can "raise" an exception so that it can then be 37 | * "handled" by special code in a dynamically enclosing 38 | * section of the program. Exceptions are raised by calling 39 | * the pseudo-function raise with the exception name, as in 40 | * 41 | * raise(MyException); 42 | * 43 | * Exceptions are handled using the "try" statement 44 | * (actually implemented using macros), which has the form: 45 | * 46 | * try { 47 | * . . . statements in the body of the block . . . 48 | * except(exception1) 49 | * . . . statements to handle exception 1 . . . 50 | * except(exception2) 51 | * . . . statements to handle exception 2 . . . 52 | * except(ANY) 53 | * . . . statements to handle any exception . . . 54 | * } endtry 55 | * 56 | * Any number of except clauses may appear (up to a 57 | * maximum defined by the constant MaxExceptionsPerScope), 58 | * and the ANY clause is optional. 59 | * 60 | * When the program encounters the "try" statement, the 61 | * statements in the body are executed. If no exception 62 | * conditions are raised during that execution, either 63 | * in this block or by a function call nested inside 64 | * this block, control passes to the end of the "try" 65 | * statement when the last statement in the block is 66 | * executed. If an exception is raised during the 67 | * dynamic execution of the block, control immediately 68 | * passes to the statements in the appropriate except 69 | * clause. Only the statements in that clause are 70 | * executed; no break statement is required to exit 71 | * the block. If no handler for the raised exception 72 | * appears anywhere in the control history, the program 73 | * exits with an error. 74 | * 75 | * Examples of use: 76 | * 77 | * 1. Catching errors. 78 | * 79 | * The following code fragment traps calls to Error, so 80 | * that the program does not quit but instead returns 81 | * to the top-level read-and-execute loop. 82 | * 83 | * while (TRUE) { 84 | * try { 85 | * printf("> "); 86 | * cmd = ReadCommand(); 87 | * ExecuteCommand(cmd); 88 | * except(ErrorException) 89 | * printf("Error: %s\n", (string) GetExceptionValue()); 90 | * -- additional handling code, if any -- 91 | * } endtry 92 | * } 93 | * 94 | * If either ReadCommand or ExecuteCommand calls Error, 95 | * control will be passed back to the main loop, after 96 | * executing any additional handler code. The error 97 | * message is passed as the exception value and can be 98 | * printed as shown in the example. 99 | * 100 | * 2. Handling control-C 101 | * 102 | * The following code extends the example above so that 103 | * typing ^C also returns to top-level. 104 | * 105 | * #include 106 | * 107 | * static exception ControlCException; 108 | * static int errorCount = 0; 109 | * static int ControlCHandler(); 110 | * 111 | * main() 112 | * { 113 | * string cmd; 114 | * 115 | * signal(SIGINT, ControlCHandler); 116 | * while (TRUE) { 117 | * try { 118 | * printf("> "); 119 | * cmd = ReadCommand(); 120 | * ExecuteCommand(cmd); 121 | * except(ControlCException); 122 | * printf("^C\n"); 123 | * signal(SIGINT, ControlCHandler); 124 | * except(ErrorException) 125 | * errorCount++; 126 | * } endtry 127 | * } 128 | * } 129 | * 130 | * static int ControlCHandler() 131 | * { 132 | * raise(ControlCException); 133 | * } 134 | */ 135 | 136 | /* 137 | * Actual interface specification 138 | * ------------------------------ 139 | * Most of the implementation of the exception mechanism is 140 | * actually done in the macros defined by this file. 141 | * Clients should ordinarily be able to read the description 142 | * above and ignore the detailed code below. 143 | */ 144 | 145 | #include 146 | #include 147 | #include "genlib.h" 148 | 149 | /* Define parameters and error status indicators */ 150 | 151 | #define MaxExceptionsPerScope 10 152 | #define ETooManyExceptClauses 101 153 | #define EUnhandledException 102 154 | 155 | /* Codes to keep track of the state of the try handler */ 156 | 157 | #define ES_Initialize 0 158 | #define ES_EvalBody 1 159 | #define ES_Exception 2 160 | 161 | /* 162 | * Type: exception 163 | * --------------- 164 | * Exceptions are specified by their address, so that the 165 | * actual structure does not matter. Strings are used here 166 | * so that exporters of exceptions can store the exception 167 | * name for the use of debuggers and other tools. 168 | */ 169 | 170 | typedef struct { string name; } exception; 171 | 172 | /* 173 | * Type: context_block 174 | * ------------------- 175 | * This structure is used internally to maintain a chain of 176 | * exception scopes on the control stack. 177 | */ 178 | 179 | typedef struct ctx_block { 180 | jmp_buf jmp; 181 | int nx; 182 | exception *array[MaxExceptionsPerScope]; 183 | exception *id; 184 | void *value; 185 | string name; 186 | struct ctx_block *link; 187 | } context_block; 188 | 189 | /* Declare the built-in exceptions */ 190 | 191 | extern exception ErrorException; 192 | extern exception ANY; 193 | 194 | /* Declare a global pointer to the context stack */ 195 | 196 | extern context_block *exceptionStack; 197 | 198 | /* 199 | * Function: RaiseException 200 | * Usage: RaiseException(&e, name, value); 201 | * --------------------------------------- 202 | * This function is called by the raise macro and does the 203 | * work necessary to raise the exception. See the exception.c file 204 | * for details. Clients do not ordinarily call this directly. 205 | */ 206 | 207 | void RaiseException(exception *e, string name, void *value); 208 | 209 | /* 210 | * Function: HandlerExists 211 | * Usage: if (HandlerExists(&e)) ... 212 | * --------------------------------- 213 | * Determines whether a handler exists for an exception in 214 | * the dynamically enclosing scope. Intended only for use 215 | * by special clients, such as the Error package. 216 | */ 217 | 218 | bool HandlerExists(exception *e); 219 | 220 | /* Define the pseudo-functions for raise and try */ 221 | 222 | #define raise(e) RaiseException(&e, #e, NULL) 223 | 224 | #define try \ 225 | { \ 226 | jmp_buf _jmp_; \ 227 | context_block _ctx_; \ 228 | volatile int _es_; \ 229 | _es_ = ES_Initialize; \ 230 | _ctx_.nx = 0; \ 231 | _ctx_.link = exceptionStack; \ 232 | exceptionStack = (context_block *) &_ctx_; \ 233 | if (setjmp(_jmp_) != 0) _es_ = ES_Exception; \ 234 | memcpy((void *) _ctx_.jmp, (void *) _jmp_, sizeof(jmp_buf)); \ 235 | while (1) { \ 236 | if (_es_ == ES_EvalBody) 237 | 238 | #define except(e) \ 239 | if (_es_ == ES_EvalBody) exceptionStack = _ctx_.link; \ 240 | break; \ 241 | } \ 242 | if (_es_ == ES_Initialize) { \ 243 | if (_ctx_.nx >= MaxExceptionsPerScope) \ 244 | exit(ETooManyExceptClauses); \ 245 | _ctx_.array[_ctx_.nx++] = &e; \ 246 | } else if (_ctx_.id == &e || &e == &ANY) { \ 247 | exceptionStack = _ctx_.link; 248 | 249 | #define endtry \ 250 | if (_es_ != ES_Initialize) break; \ 251 | _es_ = ES_EvalBody; \ 252 | } \ 253 | } 254 | 255 | #define GetExceptionName() _ctx_.name 256 | #define GetExceptionValue() _ctx_.value 257 | #define GetCurrentException() _ctx_.id 258 | 259 | #endif 260 | -------------------------------------------------------------------------------- /book_code/README: -------------------------------------------------------------------------------- 1 | 2 | ________________________________________________________________________ 3 | 4 | Welcome! You have reached the top-level directory in the FTP archive 5 | designed for use with the text "The Art and Science of C: A Library- 6 | Based Approach" (Addison-Wesley, 1995) and Programming Abstractions 7 | \in C: A Second Course in Computer Science, both by Eric Roberts. 8 | From here, you can get access to the source code for the libraries used 9 | in the books along with several other resources both for those teaching 10 | from the texts and for those learning from them. 11 | 12 | NOTES ON THE LIBRARIES 13 | 14 | To make it possible to teach ANSI C at the introductory level, "The Art 15 | and Science of C" is designed around several special libraries -- 16 | collectively known as the cslib library -- that hide much of the 17 | complexity associated with C until students are better equipped to 18 | understand how everything works. To use the text, you must have access 19 | to the cslib library on your own machine. Although the code for the 20 | cslib library is included in Appendix B of the text, it is much easier 21 | to obtain an electronic copy from the aw.com FTP site. 22 | 23 | With the exception of the graphics library presented in Chapter 7, the 24 | cslib library is completely standard and requires nothing more than what 25 | is provided as part of ANSI C. Thus, if you do not intend to use the 26 | graphics library, it doesn't matter what version of cslib you get -- 27 | they are all the same. However, because the mechanics of displaying 28 | screen images depend on the hardware and software configuration of each 29 | computer, you need an implementation of the graphics library that is 30 | appropriate to your computing environment. 31 | 32 | TO FIND THE RIGHT VERSION OF THE CSLIB LIBRARY FOR YOUR COMPUTING 33 | PLATFORM: 34 | 35 | 1. Check to see if your computing platform is any of the following: 36 | 37 | o A Macintosh running THINK C or Symantec C++ 38 | o An IBM PC (or clone) running the Borland C/C++ or Turbo C++ 39 | compiler 40 | o Any Unix system running the X Windows operating system 41 | 42 | For each of these platforms, there is a corresponding implementation 43 | of the libraries specific to that computing environment. To obtain 44 | it, you should connect to the appropriate subdirectory and follow 45 | the instructions in that README file. 46 | 47 | 2. If you are using one of the hardware systems from the preceding list 48 | with a different compiler or operating system, consider purchasing 49 | the software necessary to make your platform correspond to one of 50 | the standard implementations. For example, if you own an IBM PC but 51 | use some compiler other than the Borland or Turbo compiler, it might 52 | be worth installing one of those systems so that you can use all the 53 | features provided by the libraries, including interactive graphics. 54 | Although implementations for other platforms may be released in the 55 | future, adapting the graphics library is not a high-priority task 56 | now that there is at least one implementation for each of the major 57 | hardware platforms used in most universities. 58 | 59 | 3. If you cannot obtain the desired software or are using some other 60 | type of machine, get a copy of the standard version of the library 61 | code from the standard subdirectory. 62 | 63 | This implementation defines the libraries in an ANSI-standard form 64 | but does not provide any on-screen graphics. The standard 65 | implementation of the graphics library instead writes a file 66 | containing a text representation of the image suitable for printing 67 | on any PostScript printer. 68 | 69 | 4. Although the standard library implementation is highly portable and 70 | works without change on every machine I've tried, there may be some 71 | systems that cannot support it. If you find that your system does 72 | not support the standard libraries for some reason, you should try 73 | the simplified version of the library, which provides the minimum 74 | level of support necessary to run the programs in the text. 75 | 76 | SUBDIRECTORY INDEX 77 | 78 | The Roberts.CS1.C directory itself contains no files other than a README 79 | file. The files you need for the various libraries are collected in the 80 | following subdirectories, each of which has its own README file for 81 | further information: 82 | 83 | simplified This subdirectory contains the simplest possible version 84 | of the library code and matches the code printed in 85 | Appendix B of the text (except for two minor changes 86 | introduced to increase portability). This implementation 87 | is machine-independent and requires no system support 88 | beyond the standard ANSI libraries. The implementation 89 | of the graphics library provides no actual screen display 90 | but instead writes a PostScript file containing the 91 | image. The simplified version of the cslib library 92 | should be used only if you are unable to get the standard 93 | version or one of the platform-specific implementations 94 | to work in your environment. 95 | 96 | standard This subdirectory offers a more advanced version of the 97 | libraries than that used in the simplified package. One 98 | advantage of the standard version is that it is 99 | compatible with the programs in the forthcoming companion 100 | volume to "The Art and Science of C", which will cover 101 | more advanced material focusing on data structures and 102 | algorithms. Most importantly, the standard version of 103 | the library includes a portable exception-handling 104 | package that makes it possible to use better error- 105 | recovery strategies. Using the standard version of the 106 | cslib library, as opposed to the simplified set, makes 107 | for a smoother transition to more advanced work. 108 | 109 | Like the simplified package, the standard package 110 | implements the graphics library in a machine-independent 111 | way by writing a PostScript file. If you are working on 112 | a system for which there is a platform-specific 113 | implementation of the graphics library (these systems are 114 | enumerated later in this list), you should use that 115 | version instead. The standard version exists so that the 116 | fundamental libraries can be used on the widest possible 117 | set of platforms. 118 | 119 | unix-xwindows Along with the facilities provided by the standard 120 | library package, this subdirectory contains an 121 | implementation of the graphics library designed for use 122 | with the X Windows system, which is available on many 123 | computers that run the Unix operating system. The 124 | implementation is designed to work with several different 125 | versions of the Unix operating system and automatically 126 | configures itself to use the appropriate code for each. 127 | 128 | mac-think-c This subdirectory contains an implementation of the 129 | graphics library for THINK C (or Symantec C/C++) on the 130 | Apple Macintosh. It also includes an interface that 131 | allows students to make a typescript of their computer 132 | session, which is a difficult operation with older 133 | versions of THINK C. To work correctly, the Macintosh 134 | must be running System 7 of the operating system and the 135 | THINK C compiler must be version 5.0 or later. 136 | 137 | pc-borland This subdirectory contains two separate implementations 138 | of the graphics library for the Borland C/C++ compilers 139 | used on the IBM PC and its clones. The dos subdirectory 140 | provides a simple implementation of the basic graphics 141 | library on top of DOS. The windows subdirectory has a 142 | complete implementation of the graphics library that is 143 | compatible with Microsoft Windows. Except for the 144 | graphics library, the package is identical to the 145 | standard package. The pc-borland version of the 146 | libraries has been tested with Version 4 of the Borland 147 | C/C++ compiler. The same code may work with other 148 | versions of the compiler and associated software, but it 149 | impossible to test it with every version of the compiler. 150 | 151 | pc-turbo This subdirectory contains an implementations of the 152 | graphics library for the Turbo C++ compiler. The code is 153 | the same as that used in the windows version of the pc- 154 | borland package, but has different installation 155 | instructions. 156 | 157 | documents This subdirectory contains documentation on the libraries 158 | and the overall approach used in "The Art and Science of 159 | C". See the README file in the documents directory for 160 | more information. 161 | 162 | programs This subdirectory contains electronic copies of all 163 | sample programs used in the text. If you are teaching a 164 | course using this text and want solutions to the 165 | exercises, please contact your Addison-Wesley 166 | representative. 167 | 168 | NOTES AND DISCLAIMERS 169 | 170 | The cslib libraries are in the public domain and may be freely copied 171 | and distributed, although they remain under development. No warranties 172 | are made concerning their correctness or stability, and no user support 173 | is guaranteed. Bug reports and suggestions, however, are appreciated 174 | and may be sent to 175 | 176 | Eric Roberts 177 | 178 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/xmanager.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xmanager.c 3 | * Version: 3.0 4 | * Last modified on Sat Oct 1 12:28:02 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file implements the xmanager.h interface, which is responsible 7 | * for the X side of the communication between the graphics client and 8 | * the X manager. The xmanager module handles the process management 9 | * and intermodule communication. All work involving actual structures 10 | * is performed by the xdisplay module. 11 | */ 12 | 13 | /* 14 | * General implementation notes 15 | * ---------------------------- 16 | * As with all implementations of the graphics library, the X 17 | * implementation is complicated by the change in model. The X 18 | * programming paradigm is based on the idea of an "event loop," 19 | * in which the application program continually calls XNextEvent 20 | * to determine whether any activity is required. This strategy 21 | * runs counter to the more conventional view of a main program 22 | * as the highest level in the problem decomposition. In the 23 | * context of teaching programming, we want the main program and 24 | * its subprograms to draw the figures -- a strategy which is not 25 | * easy directly compatible with the X paradigm. 26 | * 27 | * To solve this problem, the X implementation creates two forks: 28 | * one to poll for X events and manage the screen and the other 29 | * to run the user program. These processes are established by 30 | * the first call to InitGraphics and remain in place until the 31 | * program terminates. So that program termination can be 32 | * handled correctly, the child process is the one that returns 33 | * to the InitGraphics caller where it runs the client program. 34 | * The parent process, which is called the X manager process, 35 | * never returns from InitGraphics. After initializing the 36 | * display, the X manager process runs an event loop waiting 37 | * simultaneously for graphical events and commands from the 38 | * client process. All of this mechanism is invisible to the 39 | * client. 40 | * 41 | * The two processes communicate by means of pipes running in 42 | * each direction. The client communicates with the X manager by 43 | * sending commands over its output pipe. The command format 44 | * consists of ASCII text lines in the following format: 45 | * 46 | * LLLCC XXXXXX 47 | * 48 | * where LLL is a three-digit length field giving the length of 49 | * the entire line (including the newline), CC is a two-digit 50 | * field specifying the command number and XXXXXX is an arbitrary 51 | * text string specifying arguments to the command. The length 52 | * of the text string can be determined from the line length 53 | * indicator. 54 | * 55 | * Some client operations require a response from the X manager. 56 | * These operations call XMGetResponse() to read a line from the 57 | * client. Because this operation is synchronous, there is no 58 | * need for the more cumbersome form used in the opposite 59 | * direction. 60 | * 61 | * This interface is used by both the client side (graphics.c) 62 | * and the X manager side (xmanager.c), but it is important to 63 | * remember that the two are running in different forks. For 64 | * each fork, the inPipe and outPipe variables correspond to 65 | * the local perspective. Thus, reading is always performed 66 | * on inPipe and writing is performed on outPipe. The ends 67 | * of the pipe are crossed between the processes so that data 68 | * that is written to outPipe by one fork appears in inPipe 69 | * on the other side. 70 | */ 71 | 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | 79 | #include "genlib.h" 80 | #include "exception.h" 81 | #include "simpio.h" 82 | #include "xmanager.h" 83 | #include "xdisplay.h" 84 | #include "xcompat.h" 85 | #include "glibrary.h" 86 | 87 | /* Constants */ 88 | 89 | #define ClientTimeout 0.05 90 | 91 | /* Private state variables */ 92 | 93 | static FILE *inPipe, *outPipe; 94 | static int infd; 95 | static pid_t child; 96 | 97 | static char cmdBuffer[CommandBufferSize]; 98 | 99 | static bool exitGraphicsFlag; 100 | 101 | /* Private function prototypes */ 102 | 103 | static void MainEventLoop(void); 104 | static bool ReadMessage(void); 105 | static void ProcessMessage(void); 106 | static void LineMessage(string args); 107 | static void ArcMessage(string args); 108 | static void TextMessage(string args); 109 | static void WidthMessage(string args); 110 | static void FontMetricsMessage(string args); 111 | static void SetEraseMessage(string args); 112 | static void StartRegionMessage(string args); 113 | static void EndRegionMessage(string args); 114 | static void ClearMessage(string args); 115 | static void UpdateMessage(string args); 116 | static void SetTitleMessage(string args); 117 | static void SetFontMessage(string args); 118 | static void GetMouseMessage(string args); 119 | static void WaitForMouseMessage(string args); 120 | static void SetColorMessage(string args); 121 | 122 | /* Exported entries */ 123 | 124 | void XMInitialize(string title) 125 | { 126 | int p1[2], p2[2]; 127 | 128 | XDOpenDisplay(title); 129 | pipe(p1); 130 | pipe(p2); 131 | if ((child = fork()) == 0) { 132 | close(p1[0]); 133 | close(p2[1]); 134 | inPipe = fdopen(p2[0], "r"); 135 | if (inPipe == NULL) Error("Can't open pipe from X client"); 136 | outPipe = fdopen(p1[1], "w"); 137 | if (outPipe == NULL) Error("Can't open pipe to X client"); 138 | } else { 139 | close(p1[1]); 140 | close(p2[0]); 141 | infd = p1[0]; 142 | outPipe = fdopen(p2[1], "w"); 143 | if (outPipe == NULL) Error("Can't open output pipe in X client"); 144 | exitGraphicsFlag = FALSE; 145 | try { 146 | MainEventLoop(); 147 | except(ErrorException) 148 | fprintf(stderr, "Error: %s\n", (string) GetExceptionValue()); 149 | XDCloseDisplay(); 150 | kill(child, SIGKILL); 151 | exit(1); 152 | } endtry 153 | (void) waitpid(child, NULL, 0); 154 | if (!exitGraphicsFlag) { 155 | printf("Press return to exit.\n"); 156 | infd = 0; 157 | MainEventLoop(); 158 | } 159 | XDCloseDisplay(); 160 | exit(0); 161 | } 162 | } 163 | 164 | void XMSendCommand(commandT cmd, string args) 165 | { 166 | int len; 167 | 168 | len = strlen(args) + 4; 169 | fprintf(outPipe, "%3d%2d %s\n", len, (int) cmd, args); 170 | fflush(outPipe); 171 | } 172 | 173 | void XMGetResponse(char buffer[]) 174 | { 175 | fgets(buffer, CommandBufferSize - 1, inPipe); 176 | } 177 | 178 | void XMReleaseClient(void) 179 | { 180 | fprintf(outPipe, "\n"); 181 | fflush(outPipe); 182 | } 183 | 184 | /* Private functions */ 185 | 186 | static void MainEventLoop(void) 187 | { 188 | struct timeval tv, *tvp; 189 | int xfd, width, sc; 190 | fd_set readmask, readset; 191 | string message; 192 | 193 | xfd = XDDisplayFD(); 194 | tv.tv_sec = ClientTimeout; 195 | tv.tv_usec = ((int) (ClientTimeout * 1000000)) % 1000000; 196 | FD_ZERO(&readmask); 197 | FD_SET(xfd, &readmask); 198 | FD_SET(infd, &readmask); 199 | width = GLMax(xfd, infd) + 1; 200 | tvp = &tv; 201 | while (TRUE) { 202 | if (XDProcessXEvent()) { 203 | tvp = &tv; 204 | } else { 205 | readset = readmask; 206 | sc = select(width, &readset, NULL, NULL, tvp); 207 | tvp = (sc == 0) ? NULL : &tv; 208 | if (sc == 0) { 209 | XDCheckForRedraw(); 210 | } else if (FD_ISSET(infd, &readset)) { 211 | if (!ReadMessage()) { 212 | XDCheckForRedraw(); 213 | return; 214 | } 215 | ProcessMessage(); 216 | XDSetRedrawFlag(); 217 | } 218 | } 219 | } 220 | } 221 | 222 | static bool ReadMessage(void) 223 | { 224 | int len; 225 | char lbuf[4]; 226 | 227 | if (infd == 0) { 228 | FreeBlock(GetLine()); 229 | return (FALSE); 230 | } 231 | if (read(infd, lbuf, 3) == 0) return (FALSE); 232 | lbuf[3] = '\0'; 233 | len = atoi(lbuf); 234 | if (read(infd, cmdBuffer, len) != len) { 235 | Error("Unexpected end of file"); 236 | } 237 | if (cmdBuffer[len-1] != '\n') { 238 | Error("Unexpected end of command"); 239 | } 240 | cmdBuffer[len-1] = '\0'; 241 | return (TRUE); 242 | } 243 | 244 | static void ProcessMessage(void) 245 | { 246 | int id; 247 | string args; 248 | 249 | args = cmdBuffer + 3; 250 | sscanf(cmdBuffer, "%d", &id); 251 | switch ((commandT) id) { 252 | case LineCmd: LineMessage(args); break; 253 | case ArcCmd: ArcMessage(args); break; 254 | case TextCmd: TextMessage(args); break; 255 | case WidthCmd: WidthMessage(args); break; 256 | case FontMetricsCmd: FontMetricsMessage(args); break; 257 | case SetEraseCmd: SetEraseMessage(args); break; 258 | case StartRegionCmd: StartRegionMessage(args); break; 259 | case EndRegionCmd: EndRegionMessage(args); break; 260 | case ClearCmd: ClearMessage(args); break; 261 | case UpdateCmd: UpdateMessage(args); break; 262 | case SetTitleCmd: SetTitleMessage(args); break; 263 | case SetFontCmd: SetFontMessage(args); break; 264 | case GetMouseCmd: GetMouseMessage(args); break; 265 | case WaitForMouseCmd: WaitForMouseMessage(args); break; 266 | case SetColorCmd: SetColorMessage(args); break; 267 | case ExitGraphicsCmd: exitGraphicsFlag = TRUE; break; 268 | default: Error("Internal error: Illegal command"); break; 269 | } 270 | } 271 | 272 | static void LineMessage(string args) 273 | { 274 | int n; 275 | double x, y, dx, dy; 276 | 277 | n = sscanf(args, "%lg %lg %lg %lg", &x, &y, &dx, &dy); 278 | if (n != 4) { 279 | Error("Internal error: Bad arguments to LineCmd"); 280 | } 281 | XDDrawLine(x, y, dx, dy); 282 | } 283 | 284 | static void ArcMessage(string args) 285 | { 286 | int n; 287 | double x, y, rx, ry, start, sweep; 288 | 289 | n = sscanf(args, "%lg %lg %lg %lg %lg %lg", 290 | &x, &y, &rx, &ry, &start, &sweep); 291 | if (n != 6) { 292 | Error("Internal error: Bad arguments to ArcCmd"); 293 | } 294 | XDDrawArc(x, y, rx, ry, start, sweep); 295 | } 296 | 297 | static void TextMessage(string args) 298 | { 299 | int n; 300 | char *space; 301 | double x, y; 302 | 303 | n = sscanf(args, "%lg %lg", &x, &y); 304 | if (n != 2) { 305 | Error("Internal error: Bad arguments to TextCmd"); 306 | } 307 | space = strchr(strchr(args, ' ') + 1, ' '); 308 | if (space == NULL) { 309 | Error("Internal error: Bad arguments to TextCmd"); 310 | } 311 | XDDrawText(x, y, space + 1); 312 | } 313 | 314 | static void WidthMessage(string args) 315 | { 316 | double width; 317 | 318 | width = XDTextWidth(args); 319 | fprintf(outPipe, "%.12g\n", width); 320 | fflush(outPipe); 321 | } 322 | 323 | static void FontMetricsMessage(string args) 324 | { 325 | double ascent, descent, height; 326 | 327 | DisplayFontMetrics(&ascent, &descent, &height); 328 | fprintf(outPipe, "%.12g %.12g %.12g\n", ascent, descent, height); 329 | fflush(outPipe); 330 | } 331 | 332 | static void SetEraseMessage(string args) 333 | { 334 | int n, flag; 335 | 336 | n = sscanf(args, "%d", &flag); 337 | if (n != 1) { 338 | Error("Internal error: Bad arguments to SetEraseCmd"); 339 | } 340 | XDSetEraseMode((bool) flag); 341 | } 342 | 343 | static void StartRegionMessage(string args) 344 | { 345 | int n; 346 | double grayScale; 347 | 348 | n = sscanf(args, "%lg", &grayScale); 349 | if (n != 1) { 350 | Error("Internal error: Bad arguments to SetEraseCmd"); 351 | } 352 | XDStartRegion(grayScale); 353 | } 354 | 355 | static void EndRegionMessage(string args) 356 | { 357 | XDEndRegion(); 358 | } 359 | 360 | static void ClearMessage(string args) 361 | { 362 | XDClearDisplay(); 363 | } 364 | 365 | static void UpdateMessage(string args) 366 | { 367 | XDSetRedrawFlag(); 368 | XDCheckForRedraw(); 369 | } 370 | 371 | static void SetTitleMessage(string args) 372 | { 373 | XDSetTitle(args); 374 | } 375 | 376 | static void SetFontMessage(string args) 377 | { 378 | int n, size, style; 379 | char *space; 380 | 381 | n = sscanf(args, "%d %d", &size, &style); 382 | if (n != 2) { 383 | Error("Internal error: Bad arguments to SetFontCmd"); 384 | } 385 | space = strchr(args, ' '); 386 | if (space == NULL) { 387 | Error("Internal error: Bad arguments to TextCmd"); 388 | } 389 | space = strchr(space + 1, ' '); 390 | if (space == NULL) { 391 | Error("Internal error: Bad arguments to TextCmd"); 392 | } 393 | fprintf(outPipe, "%s\n", XDSetFont(space + 1, size, style)); 394 | fflush(outPipe); 395 | } 396 | 397 | static void GetMouseMessage(string args) 398 | { 399 | bool down; 400 | double x, y; 401 | 402 | XDGetMouse(&down, &x, &y); 403 | fprintf(outPipe, "%d, %.12g, %.12g\n", down, x, y); 404 | fflush(outPipe); 405 | } 406 | 407 | static void WaitForMouseMessage(string args) 408 | { 409 | XDWaitForMouse(StringEqual(args, "D")); 410 | } 411 | 412 | static void SetColorMessage(string args) 413 | { 414 | double red, green, blue; 415 | int n; 416 | 417 | n = sscanf(args, "%lg %lg %lg", &red, &green, &blue); 418 | if (n != 3) { 419 | Error("Internal error: Bad arguments to SetColorCmd"); 420 | } 421 | XDSetColor(red, green, blue); 422 | } 423 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/extgraph.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: extgraph.h 3 | * Version: 3.0 4 | * Last modified on Tue Oct 4 11:24:41 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This interface is the extended graphics interface. 7 | * It includes all of the facilities in graphics.h, plus 8 | * several additional functions that are designed to 9 | * support more sophisticated, interactive graphics. 10 | */ 11 | 12 | #ifndef _extgraph_h 13 | #define _extgraph_h 14 | 15 | #include "genlib.h" 16 | 17 | /* Exported functions */ 18 | 19 | /* Section 1 -- Basic functions from graphics.h */ 20 | 21 | #include "graphics.h" 22 | 23 | /* Section 2 -- Elliptical arcs */ 24 | 25 | /* 26 | * Function: DrawEllipticalArc 27 | * Usage: DrawEllipticalArc(rx, ry, start, sweep); 28 | * ----------------------------------------------- 29 | * This procedure draws an elliptical arc. It is exactly 30 | * the same in its operation as DrawArc in the graphics.h 31 | * interface, except that the radius is different along the 32 | * two axes. 33 | */ 34 | 35 | void DrawEllipticalArc(double rx, double ry, 36 | double start, double sweep); 37 | 38 | /* Section 3 -- Graphical regions*/ 39 | 40 | /* 41 | * Functions: StartFilledRegion, EndFilledRegion 42 | * Usage: StartFilledRegion(density); 43 | * . . . other calls . . . 44 | * EndFilledRegion(); 45 | * ------------------------------ 46 | * These calls make it possible to draw filled shapes on the 47 | * display. After calling StartFilledRegion, any calls to 48 | * DrawLine and DrawArc are used to create a shape definition 49 | * and do not appear on the screen until EndFilledRegion is 50 | * called. The lines and arcs must be consecutive, in the 51 | * sense that each new element must start where the last 52 | * one ended. MovePen calls may occur at the beginning 53 | * or the end of the region, but not in the interior. When 54 | * EndFilledRegion is called, the entire region appears on the 55 | * screen, with its interior filled in. The density parameter 56 | * is a number between 0 and 1 and indicates how the dot density 57 | * to be used for the fill pattern. If density is 1, the shape 58 | * will be filled in a solid color; if it is 0, the fill will be 59 | * invisible. In between, the implementation will use a dot 60 | * pattern that colors some of the screen dots but not others. 61 | */ 62 | 63 | void StartFilledRegion(double density); 64 | void EndFilledRegion(void); 65 | 66 | /* Section 4 -- String functions */ 67 | 68 | /* 69 | * Function: DrawTextString 70 | * Usage: DrawTextString(text); 71 | * ---------------------------- 72 | * This function displays the string text at the current point 73 | * in the current font and size. The current point is updated 74 | * so that the next DrawTextString command would continue from 75 | * the next character position. The string may not include the 76 | * newline character. 77 | */ 78 | 79 | void DrawTextString(string text); 80 | 81 | /* 82 | * Function: TextStringWidth 83 | * Usage: w = TextStringWidth(text); 84 | * --------------------------------- 85 | * This function returns the width of the text string if displayed 86 | * at the current font and size. 87 | */ 88 | 89 | double TextStringWidth(string text); 90 | 91 | /* 92 | * Function: SetFont 93 | * Usage: SetFont(font); 94 | * --------------------- 95 | * This function sets a new font according to the font string, 96 | * which is case-independent. Different systems support different 97 | * fonts, although common ones like "Times" and "Courier" are often 98 | * supported. Initially, the font is set to "Default" which is 99 | * always supported, although the underlying font is system 100 | * dependent. If the font name is unrecognized, no error is 101 | * generated, and the font remains unchanged. If you need to 102 | * detect this condition, you can call GetFont to see if the 103 | * change took effect. By not generating an error in this case, 104 | * programs become more portable. 105 | */ 106 | 107 | void SetFont(string font); 108 | 109 | /* 110 | * Function: GetFont 111 | * Usage: font = GetFont(); 112 | * ------------------------ 113 | * This function returns the current font name as a string. 114 | */ 115 | 116 | string GetFont(void); 117 | 118 | /* 119 | * Function: SetPointSize 120 | * Usage: SetPointSize(size); 121 | * -------------------------- 122 | * This function sets a new point size. If the point size is 123 | * not supported for a particular font, the closest existing 124 | * size is selected. 125 | */ 126 | 127 | void SetPointSize(int size); 128 | 129 | /* 130 | * Function: GetPointSize 131 | * Usage: size = GetPointSize(); 132 | * ----------------------------- 133 | * This function returns the current point size. 134 | */ 135 | 136 | int GetPointSize(void); 137 | 138 | /* 139 | * Text style constants 140 | * -------------------- 141 | * The constants Bold and Italic are used in the SetStyle 142 | * command to specify the desired text style. They may also 143 | * be used in combination by adding these constants together, 144 | * as in Bold + Italic. The constant Normal indicates the 145 | * default style. 146 | */ 147 | 148 | #define Normal 0 149 | #define Bold 1 150 | #define Italic 2 151 | 152 | /* 153 | * Function: SetStyle 154 | * Usage: SetStyle(style); 155 | * ----------------------- 156 | * This function establishes the current style properties 157 | * for text based on the parameter style, which is an integer 158 | * representing the sum of any of the text style constants. 159 | */ 160 | 161 | void SetStyle(int style); 162 | 163 | /* 164 | * Function: GetStyle 165 | * Usage: style = GetStyle(); 166 | * -------------------------- 167 | * This function returns the current style. 168 | */ 169 | 170 | int GetStyle(void); 171 | 172 | /* 173 | * Functions: GetFontAscent, GetFontDescent, GetFontHeight 174 | * Usage: ascent = GetFontAscent(); 175 | * descent = GetFontDescent(); 176 | * height = GetFontHeight(); 177 | * ------------------------------------------------------- 178 | * These functions return properties of the current font that are 179 | * used to calculate how to position text vertically on the page. 180 | * The ascent of a font is the distance from the baseline to the 181 | * top of the largest character; the descent is the maximum 182 | * distance any character extends below the baseline. The height 183 | * is the total distance between two lines of text, including the 184 | * interline space (which is called leading). 185 | * 186 | * Examples: 187 | * To change the value of y so that it indicates the next text 188 | * line, you need to execute 189 | * 190 | * y -= GetFontHeight(); 191 | * 192 | * To center text vertically around the coordinate y, you need 193 | * to start the pen at 194 | * 195 | * y - GetFontAscent() / 2 196 | */ 197 | 198 | double GetFontAscent(void); 199 | double GetFontDescent(void); 200 | double GetFontHeight(void); 201 | 202 | /* Section 5 -- Mouse support */ 203 | 204 | /* 205 | * Functions: GetMouseX, GetMouseY 206 | * Usage: x = GetMouseX(); 207 | * y = GetMouseY(); 208 | * ------------------------------- 209 | * These functions return the x and y coordinates of the mouse, 210 | * respectively. The coordinate values are real numbers measured 211 | * in inches from the origin and therefore match the drawing 212 | * coordinates. 213 | */ 214 | 215 | double GetMouseX(void); 216 | double GetMouseY(void); 217 | 218 | /* 219 | * Functions: MouseButtonIsDown 220 | * Usage: if (MouseButtonIsDown()) . . . 221 | * ------------------------------------- 222 | * This function returns TRUE if the mouse button is currently 223 | * down. For maximum compatibility among implementations, the 224 | * mouse is assumed to have one button. If the mouse has more 225 | * than one button, this function returns TRUE if any button 226 | * is down. 227 | */ 228 | 229 | bool MouseButtonIsDown(void); 230 | 231 | /* 232 | * Functions: WaitForMouseDown, WaitForMouseUp 233 | * Usage: WaitForMouseDown(); 234 | * WaitForMouseUp(); 235 | * ------------------------------------------- 236 | * The WaitForMouseDown function waits until the mouse button 237 | * is pressed and then returns. WaitForMouseUp waits for the 238 | * button to be released. 239 | */ 240 | 241 | void WaitForMouseDown(void); 242 | void WaitForMouseUp(void); 243 | 244 | /* Section 6 -- Color support */ 245 | 246 | /* 247 | * Function: HasColor 248 | * Usage: if (HasColor()) . . . 249 | * ---------------------------- 250 | * This function returns TRUE if the graphics window can display a 251 | * color image. Note that this condition is stronger than simply 252 | * checking whether a color display is available. Because color 253 | * windows require more memory than black and white ones, this 254 | * function will return FALSE with a color screen if there is 255 | * not enough memory to store a colored image. On the Macintosh, 256 | * for example, it is usually necessary to increase the partition 257 | * size to at least 1MB before color windows can be created. 258 | */ 259 | 260 | bool HasColor(void); 261 | 262 | /* 263 | * Function: SetPenColor 264 | * Usage: SetPenColor(color); 265 | * -------------------------- 266 | * This function sets the color of the pen used for any drawing, 267 | * including lines, text, and filled regions. The color is a 268 | * string, which will ordinarily be one of the following 269 | * predefined color names: 270 | * 271 | * Black, Dark Gray, Gray, Light Gray, White, 272 | * Red, Yellow, Green, Cyan, Blue, Magenta 273 | * 274 | * The first line corresponds to standard gray scales and the 275 | * second to the primary and secondary colors of light. The 276 | * built-in set is limited to these colors because they are 277 | * likely to be the same on all hardware devices. For finer 278 | * color control, you can use the DefineColor function to 279 | * create new color names as well. 280 | */ 281 | 282 | void SetPenColor(string color); 283 | 284 | /* 285 | * Function: GetPenColor 286 | * Usage: color = GetPenColor(); 287 | * ----------------------------- 288 | * This function returns the current pen color as a string. 289 | */ 290 | 291 | string GetPenColor(void); 292 | 293 | /* 294 | * Function: DefineColor 295 | * Usage: DefineColor(name, red, green, blue); 296 | * ------------------------------------------- 297 | * This function allows the client to define a new color name 298 | * by supplying intensity levels for the colors red, green, 299 | * and blue, which are the primary colors of light. The 300 | * color values are provided as real numbers between 0 and 1, 301 | * indicating the intensity of that color. For example, 302 | * the predefined color Magenta has full intensity red and 303 | * blue but no green and is therefore defined as: 304 | * 305 | * DefineColor("Magenta", 1, 0, 1); 306 | * 307 | * DefineColor allows you to create intermediate colors on 308 | * many displays, although the results vary significantly 309 | * depending on the hardware. For example, the following 310 | * usually gives a reasonable approximation of brown: 311 | * 312 | * DefineColor("Brown", .35, .20, .05); 313 | */ 314 | 315 | void DefineColor(string name, 316 | double red, double green, double blue); 317 | 318 | /* Section 7 -- Miscellaneous functions */ 319 | 320 | /* 321 | * Function: SetEraseMode 322 | * Usage: SetEraseMode(TRUE); 323 | * SetEraseMode(FALSE); 324 | * --------------------------- 325 | * The SetEraseMode function sets the value of the internal 326 | * erasing flag. Setting this flag is similar to setting the 327 | * color to "White" in its effect but does not affect the 328 | * current color setting. When erase mode is set to FALSE, 329 | * normal drawing is restored, using the current color. 330 | */ 331 | 332 | void SetEraseMode(bool mode); 333 | 334 | /* 335 | * Function: GetEraseMode 336 | * Usage: mode = GetEraseMode(); 337 | * ----------------------------- 338 | * This function returns the current state of the erase mode flag. 339 | */ 340 | 341 | bool GetEraseMode(void); 342 | 343 | /* 344 | * Function: SetWindowTitle 345 | * Usage: SetWindowTitle(title); 346 | * ----------------------------- 347 | * This function sets the title of the graphics window, if such 348 | * an operation is possible on the display. If it is not possible 349 | * for a particular implementation, the call is simply ignored. 350 | * This function may be called prior to the InitGraphics call to 351 | * set the initial name of the window. 352 | */ 353 | 354 | void SetWindowTitle(string title); 355 | 356 | /* 357 | * Function: GetWindowTitle 358 | * Usage: title = GetWindowTitle(); 359 | * -------------------------------- 360 | * This function returns the title of the graphics window. If the 361 | * implementation does not support titles, this call returns the 362 | * empty string. 363 | */ 364 | 365 | string GetWindowTitle(void); 366 | 367 | /* 368 | * Function: UpdateDisplay 369 | * Usage: UpdateDisplay(); 370 | * ----------------------- 371 | * This function initiates an immediate update of the graphics 372 | * window and is necessary for animation. Ordinarily, the 373 | * graphics window is updated only when the program waits for 374 | * user input. 375 | */ 376 | 377 | void UpdateDisplay(void); 378 | 379 | /* 380 | * Function: Pause 381 | * Usage: Pause(seconds); 382 | * ---------------------- 383 | * The Pause function updates the graphics window and then 384 | * pauses for the indicated number of seconds. This function 385 | * is useful for animation where the motion would otherwise 386 | * be too fast. 387 | */ 388 | 389 | void Pause(double seconds); 390 | 391 | /* 392 | * Function: ExitGraphics 393 | * Usage: ExitGraphics(); 394 | * ---------------------- 395 | * The ExitGraphics function closes the graphics window and 396 | * exits from the application without waiting for any additional 397 | * user interaction. 398 | */ 399 | 400 | void ExitGraphics(void); 401 | 402 | /* 403 | * Functions: SaveGraphicsState, RestoreGraphicsState 404 | * Usage: SaveGraphicsState(); 405 | * . . . graphical operations . . . 406 | * RestoreGraphicsState(); 407 | * --------------------------------------------------- 408 | * The SaveGraphicsState function saves the current graphics 409 | * state (the current pen position, the font, the point size, 410 | * and the erase mode flag) internally, so that they can be 411 | * restored by the next RestoreGraphicsState call. These two 412 | * functions must be used in pairs but may be nested to any depth. 413 | */ 414 | 415 | void SaveGraphicsState(void); 416 | void RestoreGraphicsState(void); 417 | 418 | /* 419 | * Functions: GetFullScreenWidth, GetFullScreenHeight 420 | * Usage: width = GetFullScreenWidth(); 421 | * height = GetFullScreenHeight(); 422 | * -------------------------------------- 423 | * These functions return the height and width of the entire 424 | * display screen, not the graphics window. Their only 425 | * significant use is for applications that need to adjust 426 | * the size of the graphics window based on available screen 427 | * space. These functions may be called before InitGraphics 428 | * has been called. 429 | */ 430 | 431 | double GetFullScreenWidth(void); 432 | double GetFullScreenHeight(void); 433 | 434 | /* 435 | * Functions: SetWindowSize 436 | * Usage: SetWindowSize(width, height); 437 | * ------------------------------------ 438 | * This function sets the window size to the indicated dimensions, 439 | * if possible. This function should be called before the graphics 440 | * window is created by InitGraphics. Attempts to change the size 441 | * of an existing window are ignored by most implementations. This 442 | * function should be used sparingly because it reduces the 443 | * portability of applications, particularly if the client 444 | * requests more space than is available on the screen. 445 | */ 446 | 447 | void SetWindowSize(double width, double height); 448 | 449 | /* 450 | * Functions: GetXResolution, GetYResolution 451 | * Usage: xres = GetXResolution(); 452 | * yres = GetYResolution(); 453 | * ----------------------------------------- 454 | * These functions return the number of pixels per inch along 455 | * each of the coordinate directions and are useful for applications 456 | * in which it is important for short distances to be represented 457 | * uniformly in terms of dot spacing. Even though the x and y 458 | * resolutions are the same for most displays, clients should 459 | * not rely on this property. 460 | * 461 | * Note: Lines in the graphics library are one pixel unit wide and 462 | * have a length that is always one pixel longer than you might 463 | * expect. For example, the function call 464 | * 465 | * DrawLine(2 / GetXResolution(), 0); 466 | * 467 | * draws a line from the current point to the point two pixels 468 | * further right, which results in a line of three pixels. 469 | */ 470 | 471 | double GetXResolution(void); 472 | double GetYResolution(void); 473 | 474 | #endif 475 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/graphics.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: graphics.c 3 | * Version: 3.0 4 | * Last modified on Sat Oct 1 12:28:01 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file is the top-level file in the implementation of the 7 | * graphics.h interface for X windows. The complete implementation 8 | * also includes the following subsidiary modules: 9 | * 10 | * glibrary.h Various low-level mathematical functions 11 | * xmanager.h Mediates communication with the X operations 12 | * xdisplay.h Performs the actual drawing operations 13 | * xcompat.h Maintains BSD compatibility on System V. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "genlib.h" 25 | #include "gcalloc.h" 26 | #include "simpio.h" 27 | #include "strlib.h" 28 | #include "extgraph.h" 29 | #include "glibrary.h" 30 | #include "xmanager.h" 31 | #include "xdisplay.h" 32 | #include "xcompat.h" 33 | 34 | /* 35 | * Parameters 36 | * ---------- 37 | * DesiredWidth -- Desired width of the graphics window in inches 38 | * DesiredHeight -- Desired height of the graphics window in inches 39 | * DefaultSize -- Default point size 40 | * MaxColors -- Maximum number of color names allowed 41 | * MinColors -- Minimum number of colors the device must support 42 | */ 43 | 44 | #define DesiredWidth 7.0 45 | #define DesiredHeight 4.0 46 | #define DefaultSize 12 47 | #define MaxColors 256 48 | #define MinColors 16 49 | 50 | /* 51 | * Type: graphicsStateT 52 | * -------------------- 53 | * This structure holds the variables that make up the graphics state. 54 | */ 55 | 56 | typedef struct graphicsStateT { 57 | double cx, cy; 58 | string font; 59 | int size; 60 | int style; 61 | bool erase; 62 | int color; 63 | struct graphicsStateT *link; 64 | } *graphicsStateT; 65 | 66 | /* 67 | * Type: regionStateT 68 | * ------------------ 69 | * The region assembly process has the character of a finite state 70 | * machine with the following four states: 71 | * 72 | * NoRegion Region has not yet been started 73 | * RegionStarting Region is started but no line segments yet 74 | * RegionActive First line segment appears 75 | * PenHasMoved Pen has moved during definition 76 | * 77 | * The current state determines whether other operations are legal 78 | * at that point. 79 | */ 80 | 81 | typedef enum { 82 | NoRegion, RegionStarting, RegionActive, PenHasMoved 83 | } regionStateT; 84 | 85 | /* 86 | * Type: colorEntryT 87 | * ----------------- 88 | * This type is used for the entries in the color table. 89 | */ 90 | 91 | typedef struct { 92 | string name; 93 | double red, green, blue; 94 | } colorEntryT; 95 | 96 | /* 97 | * Global variables 98 | * ---------------- 99 | * initialized -- TRUE if initialization has been done 100 | * windowTitle -- Current window title (initialized statically) 101 | * cmdBuffer -- Static buffer for sending commands 102 | * regionState -- Current state of the region 103 | * colorTable -- Table of defined colors 104 | * nColors -- Number of defined colors 105 | * colorOK -- TRUE if the display supports color 106 | * lastColor -- Previous color to avoid multiple changes 107 | * fontChanged -- TRUE if font information has changed 108 | * windowWidth -- Width of the window in inches 109 | * windowHeight -- Height of the window in inches 110 | * stateStack -- Stack of graphicStateT blocks 111 | * cx, cy -- Current coordinates | These 112 | * eraseMode -- Setting of erase flag | variables 113 | * textFont -- Current font | consititute 114 | * textStyle -- Current style | the current 115 | * pointSize -- Current point size | graphics 116 | * penColor -- Color of pen | state 117 | */ 118 | 119 | static bool initialized = FALSE; 120 | static string windowTitle = "Graphics Window"; 121 | 122 | static char cmdBuffer[CommandBufferSize]; 123 | 124 | static regionStateT regionState; 125 | 126 | static colorEntryT colorTable[MaxColors]; 127 | static int nColors; 128 | static bool colorOK; 129 | static int lastColor; 130 | static bool fontChanged; 131 | 132 | static double windowWidth = DesiredWidth; 133 | static double windowHeight = DesiredHeight; 134 | 135 | static graphicsStateT stateStack; 136 | 137 | static double cx, cy; 138 | static bool eraseMode; 139 | static string textFont; 140 | static int textStyle; 141 | static int pointSize; 142 | static int penColor; 143 | 144 | /* Private function prototypes */ 145 | 146 | static void InitCheck(void); 147 | static void InitGraphicsState(void); 148 | static void InstallFont(void); 149 | static void InitColors(void); 150 | static int FindColorName(string name); 151 | static bool ShouldBeWhite(void); 152 | static bool StringMatch(string s1, string s2); 153 | static void USleep(unsigned useconds); 154 | 155 | /* Exported entries */ 156 | 157 | /* Section 1 -- Basic functions from graphics.h */ 158 | 159 | /* 160 | * Function: InitGraphics 161 | * ---------------------- 162 | * The implementation below hides considerable complexity underneath 163 | * the InitXHandler call. If you are trying to modify or maintain 164 | * this implementation, it is important to understand how that 165 | * function is implemented. For details, see the xhandler.c 166 | * implementation. 167 | */ 168 | 169 | void InitGraphics(void) 170 | { 171 | if (initialized) { 172 | XMSendCommand(ClearCmd, ""); 173 | } else { 174 | initialized = TRUE; 175 | ProtectVariable(stateStack); 176 | ProtectVariable(windowTitle); 177 | ProtectVariable(textFont); 178 | XDSetWindowSize(windowWidth, windowHeight); 179 | XMInitialize(windowTitle); 180 | InitColors(); 181 | } 182 | InitGraphicsState(); 183 | } 184 | 185 | void MovePen(double x, double y) 186 | { 187 | InitCheck(); 188 | if (regionState == RegionActive) regionState = PenHasMoved; 189 | cx = x; 190 | cy = y; 191 | } 192 | 193 | void DrawLine(double dx, double dy) 194 | { 195 | InitCheck(); 196 | switch (regionState) { 197 | case NoRegion: case RegionActive: 198 | break; 199 | case RegionStarting: 200 | regionState = RegionActive; 201 | break; 202 | case PenHasMoved: 203 | Error("Region segments must be contiguous"); 204 | } 205 | sprintf(cmdBuffer, "%.12g %.12g %.12g %.12g", cx, cy, dx, dy); 206 | XMSendCommand(LineCmd, cmdBuffer); 207 | cx += dx; 208 | cy += dy; 209 | } 210 | 211 | void DrawArc(double r, double start, double sweep) 212 | { 213 | DrawEllipticalArc(r, r, start, sweep); 214 | } 215 | 216 | double GetWindowWidth(void) 217 | { 218 | return (windowWidth); 219 | } 220 | 221 | double GetWindowHeight(void) 222 | { 223 | return (windowHeight); 224 | } 225 | 226 | double GetCurrentX(void) 227 | { 228 | InitCheck(); 229 | return (cx); 230 | } 231 | 232 | double GetCurrentY(void) 233 | { 234 | InitCheck(); 235 | return (cy); 236 | } 237 | 238 | /* Section 2 -- Elliptical arcs */ 239 | 240 | void DrawEllipticalArc(double rx, double ry, 241 | double start, double sweep) 242 | { 243 | double x, y; 244 | 245 | InitCheck(); 246 | switch (regionState) { 247 | case NoRegion: case RegionActive: 248 | break; 249 | case RegionStarting: 250 | regionState = RegionActive; 251 | break; 252 | case PenHasMoved: 253 | Error("Region segments must be contiguous"); 254 | } 255 | x = cx + rx * cos(GLRadians(start + 180)); 256 | y = cy + ry * sin(GLRadians(start + 180)); 257 | sprintf(cmdBuffer, "%.12g %.12g %.12g %.12g %.12g %.12g", 258 | x, y, rx, ry, start, sweep); 259 | XMSendCommand(ArcCmd, cmdBuffer); 260 | cx = x + rx * cos(GLRadians(start + sweep)); 261 | cy = y + ry * sin(GLRadians(start + sweep)); 262 | } 263 | 264 | /* Section 3 -- Graphical structures */ 265 | 266 | void StartFilledRegion(double density) 267 | { 268 | InitCheck(); 269 | if (regionState != NoRegion) { 270 | Error("Region is already in progress"); 271 | } 272 | if (density < 0 || density > 1) { 273 | Error("Density for regions must be between 0 and 1"); 274 | } 275 | regionState = RegionStarting; 276 | sprintf(cmdBuffer, "%.12g", density); 277 | XMSendCommand(StartRegionCmd, cmdBuffer); 278 | } 279 | 280 | void EndFilledRegion(void) 281 | { 282 | InitCheck(); 283 | if (regionState == NoRegion) { 284 | Error("EndFilledRegion without StartFilledRegion"); 285 | } 286 | regionState = NoRegion; 287 | XMSendCommand(EndRegionCmd, ""); 288 | } 289 | 290 | /* Section 4 -- String functions */ 291 | 292 | void DrawTextString(string text) 293 | { 294 | InitCheck(); 295 | if (regionState != NoRegion) { 296 | Error("Text strings are illegal inside a region"); 297 | } 298 | if (strlen(text) > MaxTextString) { 299 | Error("Text string too long"); 300 | } 301 | InstallFont(); 302 | sprintf(cmdBuffer, "%.12g %.12g %s", cx, cy, text); 303 | XMSendCommand(TextCmd, cmdBuffer); 304 | cx += TextStringWidth(text); 305 | } 306 | 307 | double TextStringWidth(string text) 308 | { 309 | double result; 310 | 311 | InitCheck(); 312 | if (strlen(text) > MaxTextString) { 313 | Error("Text string too long"); 314 | } 315 | InstallFont(); 316 | sprintf(cmdBuffer, "%s", text); 317 | XMSendCommand(WidthCmd, cmdBuffer); 318 | XMGetResponse(cmdBuffer); 319 | (void) sscanf(cmdBuffer, "%lg", &result); 320 | return (result); 321 | } 322 | 323 | void SetFont(string font) 324 | { 325 | InitCheck(); 326 | if (strlen(font) > MaxFontName) Error("Font name too long"); 327 | textFont = CopyString(font); 328 | fontChanged = TRUE; 329 | } 330 | 331 | string GetFont(void) 332 | { 333 | InitCheck(); 334 | InstallFont(); 335 | return (CopyString(textFont)); 336 | } 337 | 338 | void SetPointSize(int size) 339 | { 340 | InitCheck(); 341 | pointSize = size; 342 | fontChanged = TRUE; 343 | } 344 | 345 | int GetPointSize(void) 346 | { 347 | InitCheck(); 348 | InstallFont(); 349 | return (pointSize); 350 | } 351 | 352 | void SetStyle(int style) 353 | { 354 | InitCheck(); 355 | textStyle = style; 356 | fontChanged = TRUE; 357 | } 358 | 359 | int GetStyle(void) 360 | { 361 | InitCheck(); 362 | InstallFont(); 363 | return (textStyle); 364 | } 365 | 366 | double GetFontAscent(void) 367 | { 368 | double ascent; 369 | 370 | InitCheck(); 371 | InstallFont(); 372 | XMSendCommand(FontMetricsCmd, ""); 373 | XMGetResponse(cmdBuffer); 374 | (void) sscanf(cmdBuffer, "%lg", &ascent); 375 | return (ascent); 376 | } 377 | 378 | double GetFontDescent(void) 379 | { 380 | double descent; 381 | 382 | InitCheck(); 383 | InstallFont(); 384 | XMSendCommand(FontMetricsCmd, ""); 385 | XMGetResponse(cmdBuffer); 386 | (void) sscanf(cmdBuffer, "%*lg %lg", &descent); 387 | return (descent); 388 | } 389 | 390 | double GetFontHeight(void) 391 | { 392 | double height; 393 | 394 | InitCheck(); 395 | InstallFont(); 396 | XMSendCommand(FontMetricsCmd, ""); 397 | XMGetResponse(cmdBuffer); 398 | (void) sscanf(cmdBuffer, "%*lg %*lg %lg", &height); 399 | return (height); 400 | } 401 | 402 | /* Section 5 -- Mouse support */ 403 | 404 | double GetMouseX(void) 405 | { 406 | double x, y; 407 | int state; 408 | 409 | InitCheck(); 410 | XMSendCommand(GetMouseCmd, ""); 411 | XMGetResponse(cmdBuffer); 412 | (void) sscanf(cmdBuffer, "%d, %lg, %lg", &state, &x, &y); 413 | return (x); 414 | } 415 | 416 | double GetMouseY(void) 417 | { 418 | string line; 419 | double x, y; 420 | int state; 421 | 422 | InitCheck(); 423 | XMSendCommand(GetMouseCmd, ""); 424 | XMGetResponse(cmdBuffer); 425 | (void) sscanf(cmdBuffer, "%d, %lg, %lg", &state, &x, &y); 426 | return (y); 427 | } 428 | 429 | bool MouseButtonIsDown(void) 430 | { 431 | string line; 432 | double x, y; 433 | int state; 434 | 435 | InitCheck(); 436 | XMSendCommand(GetMouseCmd, ""); 437 | XMGetResponse(cmdBuffer); 438 | (void) sscanf(cmdBuffer, "%d, %lg, %lg", &state, &x, &y); 439 | return (state != 0); 440 | } 441 | 442 | void WaitForMouseDown(void) 443 | { 444 | InitCheck(); 445 | XMSendCommand(WaitForMouseCmd, "D"); 446 | XMGetResponse(cmdBuffer); 447 | } 448 | 449 | void WaitForMouseUp(void) 450 | { 451 | InitCheck(); 452 | XMSendCommand(WaitForMouseCmd, "U"); 453 | XMGetResponse(cmdBuffer); 454 | } 455 | 456 | /* Section 6 -- Color support */ 457 | 458 | bool HasColor(void) 459 | { 460 | InitCheck(); 461 | return (colorOK); 462 | } 463 | 464 | void SetPenColor(string color) 465 | { 466 | int cindex; 467 | 468 | InitCheck(); 469 | cindex = FindColorName(color); 470 | if (cindex == -1) Error("Undefined color: %s", color); 471 | penColor = cindex; 472 | if (penColor == lastColor) return; 473 | lastColor = penColor; 474 | if (HasColor()) { 475 | sprintf(cmdBuffer, "%g %g %g\n", 476 | colorTable[cindex].red, 477 | colorTable[cindex].green, 478 | colorTable[cindex].blue); 479 | XMSendCommand(SetColorCmd, cmdBuffer); 480 | } else { 481 | SetEraseMode(eraseMode); 482 | } 483 | } 484 | 485 | string GetPenColor(void) 486 | { 487 | InitCheck(); 488 | return (colorTable[penColor].name); 489 | } 490 | 491 | void DefineColor(string name, 492 | double red, double green, double blue) 493 | { 494 | int cindex; 495 | 496 | InitCheck(); 497 | if (red < 0 || red > 1 || green < 0 || green > 1 || blue < 0 || blue > 1) { 498 | Error("DefineColor: All color intensities must be between 0 and 1"); 499 | } 500 | cindex = FindColorName(name); 501 | if (cindex == -1) { 502 | if (nColors == MaxColors) Error("DefineColor: Too many colors"); 503 | cindex = nColors++; 504 | } 505 | colorTable[cindex].name = CopyString(name); 506 | colorTable[cindex].red = red; 507 | colorTable[cindex].green = green; 508 | colorTable[cindex].blue = blue; 509 | } 510 | 511 | /* Section 7 -- Miscellaneous functions */ 512 | 513 | void SetEraseMode(bool mode) 514 | { 515 | InitCheck(); 516 | eraseMode = mode; 517 | sprintf(cmdBuffer, "%d", (int) (mode || ShouldBeWhite())); 518 | XMSendCommand(SetEraseCmd, cmdBuffer); 519 | } 520 | 521 | bool GetEraseMode(void) 522 | { 523 | InitCheck(); 524 | return (eraseMode); 525 | } 526 | 527 | void SetWindowTitle(string title) 528 | { 529 | windowTitle = CopyString(title); 530 | if (initialized) { 531 | sprintf(cmdBuffer, "%s", windowTitle); 532 | XMSendCommand(SetTitleCmd, cmdBuffer); 533 | } 534 | } 535 | 536 | string GetWindowTitle(void) 537 | { 538 | return (CopyString(windowTitle)); 539 | } 540 | 541 | void UpdateDisplay(void) 542 | { 543 | int cnt; 544 | 545 | InitCheck(); 546 | XMSendCommand(UpdateCmd, ""); 547 | } 548 | 549 | void Pause(double seconds) 550 | { 551 | if (initialized) UpdateDisplay(); 552 | USleep((unsigned) (seconds * 1000000)); 553 | } 554 | 555 | void ExitGraphics(void) 556 | { 557 | XMSendCommand(ExitGraphicsCmd, ""); 558 | exit(0); 559 | } 560 | 561 | void SaveGraphicsState(void) 562 | { 563 | graphicsStateT sb; 564 | 565 | InitCheck(); 566 | sb = New(graphicsStateT); 567 | sb->cx = cx; 568 | sb->cy = cy; 569 | sb->font = textFont; 570 | sb->size = pointSize; 571 | sb->style = textStyle; 572 | sb->erase = eraseMode; 573 | sb->color = penColor; 574 | sb->link = stateStack; 575 | stateStack = sb; 576 | } 577 | 578 | void RestoreGraphicsState(void) 579 | { 580 | graphicsStateT sb; 581 | 582 | InitCheck(); 583 | if (stateStack == NULL) { 584 | Error("RestoreGraphicsState called before SaveGraphicsState"); 585 | } 586 | sb = stateStack; 587 | cx = sb->cx; 588 | cy = sb->cy; 589 | textFont = sb->font; 590 | pointSize = sb->size; 591 | textStyle = sb->style; 592 | eraseMode = sb->erase; 593 | penColor = sb->color; 594 | stateStack = sb->link; 595 | FreeBlock(sb); 596 | fontChanged = TRUE; 597 | SetEraseMode(eraseMode); 598 | SetPenColor(colorTable[penColor].name); 599 | } 600 | 601 | double GetFullScreenWidth(void) 602 | { 603 | double screenWidth, screenHeight; 604 | 605 | XDGetScreenSize(&screenWidth, &screenHeight); 606 | return (screenWidth); 607 | } 608 | 609 | double GetFullScreenHeight(void) 610 | { 611 | double screenWidth, screenHeight; 612 | 613 | XDGetScreenSize(&screenWidth, &screenHeight); 614 | return (screenHeight); 615 | } 616 | 617 | void SetWindowSize(double width, double height) 618 | { 619 | if (initialized) { 620 | Error("The window size cannot be set after calling InitGraphics"); 621 | } 622 | windowWidth = width; 623 | windowHeight = height; 624 | } 625 | 626 | double GetXResolution(void) 627 | { 628 | double xdpi, ydpi; 629 | 630 | XDGetResolution(&xdpi, &ydpi); 631 | return (ydpi); 632 | } 633 | 634 | double GetYResolution(void) 635 | { 636 | double xdpi, ydpi; 637 | 638 | XDGetResolution(&xdpi, &ydpi); 639 | return (ydpi); 640 | } 641 | 642 | /* Private functions */ 643 | 644 | /* 645 | * Function: InitCheck 646 | * Usage: InitCheck(); 647 | * ------------------- 648 | * This function merely ensures that the package has been 649 | * initialized before the client functions are called. 650 | */ 651 | 652 | static void InitCheck(void) 653 | { 654 | if (!initialized) Error("InitGraphics has not been called"); 655 | } 656 | 657 | /* 658 | * Function: InitGraphicsState 659 | * Usage: InitGraphicsState(); 660 | * --------------------------- 661 | * This function initializes the graphics state elements to 662 | * their default values. 663 | */ 664 | 665 | static void InitGraphicsState(void) 666 | { 667 | cx = cy = 0; 668 | eraseMode = FALSE; 669 | textFont = "Default"; 670 | pointSize = DefaultSize; 671 | textStyle = Normal; 672 | stateStack = NULL; 673 | regionState = NoRegion; 674 | fontChanged = TRUE; 675 | SetPenColor("Black"); 676 | } 677 | 678 | static void InstallFont(void) 679 | { 680 | char fontbuf[MaxFontName]; 681 | string line; 682 | 683 | if (!fontChanged) return; 684 | sprintf(cmdBuffer, "%d %d %s", pointSize, textStyle, textFont); 685 | XMSendCommand(SetFontCmd, cmdBuffer); 686 | XMGetResponse(cmdBuffer); 687 | (void) sscanf(cmdBuffer, "%d %d %s", &pointSize, &textStyle, fontbuf); 688 | textFont = CopyString(fontbuf); 689 | fontChanged = FALSE; 690 | } 691 | 692 | /* 693 | * Function: InitColors 694 | * Usage: InitColors(); 695 | * -------------------- 696 | * This function defines the built-in colors. 697 | */ 698 | 699 | static void InitColors(void) 700 | { 701 | colorOK = (XDGetNColors() >= MinColors); 702 | lastColor = -1; 703 | nColors = 0; 704 | DefineColor("Black", 0, 0, 0); 705 | DefineColor("Dark Gray", .35, .35, .35); 706 | DefineColor("Gray", .6, .6, .6); 707 | DefineColor("Light Gray", .75, .75, .75); 708 | DefineColor("White", 1, 1, 1); 709 | DefineColor("Red", 1, 0, 0); 710 | DefineColor("Yellow", 1, 1, 0); 711 | DefineColor("Green", 0, 1, 0); 712 | DefineColor("Cyan", 0, 1, 1); 713 | DefineColor("Blue", 0, 0, 1); 714 | DefineColor("Magenta", 1, 0, 1); 715 | } 716 | 717 | /* 718 | * Function: FindColorName 719 | * Usage: index = FindColorName(name); 720 | * ----------------------------------- 721 | * This function returns the index of the named color in the 722 | * color table, or -1 if the color does not exist. 723 | */ 724 | 725 | static int FindColorName(string name) 726 | { 727 | int i; 728 | 729 | for (i = 0; i < nColors; i++) { 730 | if (StringMatch(name, colorTable[i].name)) return (i); 731 | } 732 | return (-1); 733 | } 734 | 735 | static bool ShouldBeWhite(void) 736 | { 737 | if (colorTable[penColor].red < .9) return (FALSE); 738 | if (colorTable[penColor].blue < .9) return (FALSE); 739 | if (colorTable[penColor].green < .9) return (FALSE); 740 | return (TRUE); 741 | } 742 | 743 | /* 744 | * Function: StringMatch 745 | * Usage: if (StringMatch(s1, s2)) . . . 746 | * ------------------------------------- 747 | * This function returns TRUE if two strings are equal, ignoring 748 | * case distinctions. 749 | */ 750 | 751 | static bool StringMatch(string s1, string s2) 752 | { 753 | register char *cp1, *cp2; 754 | 755 | cp1 = s1; 756 | cp2 = s2; 757 | while (tolower(*cp1) == tolower(*cp2)) { 758 | if (*cp1 == '\0') return (TRUE); 759 | cp1++; 760 | cp2++; 761 | } 762 | return (FALSE); 763 | } 764 | 765 | /* 766 | * Function: USleep 767 | * Usage: USleep(useconds); 768 | * ------------------------ 769 | * This function sleeps for the indicated number of microseconds. 770 | * Some versions of Unix implement a usleep call, but it does not 771 | * appear to be standard. The easiest way to implement it is by 772 | * calling select with no descriptors, since the compatibility 773 | * library has already made sure that select is available. 774 | */ 775 | 776 | static void USleep(unsigned useconds) 777 | { 778 | struct timeval tv; 779 | 780 | tv.tv_sec = useconds / 1000000; 781 | tv.tv_usec = useconds % 1000000; 782 | (void) select(1, NULL, NULL, NULL, &tv); 783 | } 784 | -------------------------------------------------------------------------------- /book_code/unix-xwindows/xdisplay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xdisplay.c 3 | * Version: 3.0 4 | * Last modified on Sat Oct 1 12:25:07 1994 by eroberts 5 | * ----------------------------------------------------- 6 | * This file implements the xdisplay.h interface, which is 7 | * responsible for all of the X windows interaction. Before 8 | * you attempt to understand this implementation, you need to 9 | * understand the basics of X11 library. 10 | */ 11 | 12 | /* 13 | * General implementation notes 14 | * ---------------------------- 15 | * This implementation creates two X drawables: the graphics 16 | * window itself (mainWindow) and an offscreen window (osWindow). 17 | * All rendering is done into the offscreen window. When update 18 | * events occur, the graphics window is updated by copying bits 19 | * from the offscreen window. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "genlib.h" 32 | #include "strlib.h" 33 | #include "glibrary.h" 34 | #include "extgraph.h" 35 | #include "xmanager.h" 36 | #include "xdisplay.h" 37 | 38 | /* 39 | * Parameters 40 | * ---------- 41 | * RequiredMargin -- Minimum margin on each side of screen (inches) 42 | * BorderPixels -- Width of the window border in pixels 43 | * MaxFontList -- Size of the font list we will accept 44 | * PStartSize -- Starting size for polygon (must be greater than 1) 45 | * DefaultFont -- Font that serves as the "Default" font 46 | */ 47 | 48 | #define RequiredMargin 0.5 49 | #define BorderPixels 1 50 | #define MaxFontList 500 51 | #define PStartSize 50 52 | #define DefaultFont "courier" 53 | 54 | /* 55 | * Other constants 56 | * --------------- 57 | * Epsilon -- Small offset used to avoid banding/aliasing 58 | * GCFgBg -- GC mask for both foreground and background 59 | */ 60 | 61 | #define Epsilon 0.000000001 62 | #define GCFgBg (GCForeground | GCBackground) 63 | 64 | /* 65 | * Static table: grayList 66 | * ---------------------- 67 | * This table contains the bitmaps for the various gray-scale 68 | * values. Adding more bitmaps to this list increases 69 | * the precision at which the client can specify gray scales. 70 | */ 71 | 72 | static char grayList[][8] = { 73 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 74 | { 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 }, 75 | { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA }, 76 | { 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD }, 77 | { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 78 | }; 79 | 80 | #define NGrays (sizeof grayList / sizeof grayList[0]) 81 | 82 | /* 83 | * Type: waitStateT 84 | * ---------------- 85 | * This type indicates the various conditions for which the 86 | * client might be waiting. 87 | */ 88 | 89 | typedef enum { 90 | NotWaiting, 91 | WaitingForMouseDown, 92 | WaitingForMouseUp 93 | } waitStateT; 94 | 95 | /* 96 | * Private variables 97 | * ----------------- 98 | * displayIsOpen -- TRUE if the display has been opened 99 | * redraw -- TRUE if mainWindow needs redrawing 100 | * eraseMode -- TRUE if erase mode has been set 101 | * xdpi, ydpi -- Dots per inch in each coordinate 102 | * disp -- X display containing windows 103 | * mainWindow -- Handle of graphics window 104 | * osWindow -- Handle of offscreen window bitmap 105 | * pixelDepth -- Depth of pixels on the screen 106 | * colormap -- Color map of screen 107 | * drawColor -- Color used for drawing (black) 108 | * eraseColor -- Color used for erasing (white) 109 | * mainGC -- The graphics context (GC) for mainWindow 110 | * drawGC -- The GC used for drawing on osWindow 111 | * eraseGC -- The GC used for erasing on osWindow 112 | * grayGC -- Array of GCs representing gray scales 113 | * grayStipple -- Array of stipples used for gray scales 114 | * currentFont -- Name of current font 115 | * currentSize -- Current point size 116 | * currentStyle -- Current style 117 | * fontInfo -- Font structure pointer for current font 118 | * windowWidth -- Width of the window in inches 119 | * windowHeight -- Height of the window in inches 120 | * screenWidth -- Width of the full screen in inches 121 | * screenHeight -- Height of the full screen in inches 122 | * argV, argC -- Used to set corresponding X window fields 123 | * waitState -- Indicates what mouse event is awaited 124 | * regionStarted -- TRUE is a region is in progress 125 | * regionGrayScale -- Gray scale density [0,1] 126 | * polygonPoints -- Array of points used in current region 127 | * nPolygonPoints -- Number of active points 128 | * polygonSize -- Number of allocated points 129 | */ 130 | 131 | static bool displayIsOpen = FALSE; 132 | static bool redraw = FALSE; 133 | static bool eraseMode; 134 | static double xdpi, ydpi; 135 | 136 | static Display *disp; 137 | static Window mainWindow; 138 | static Pixmap osWindow; 139 | static Colormap colormap; 140 | static int pixelDepth; 141 | static unsigned long drawColor, eraseColor; 142 | static GC mainGC, drawGC, eraseGC; 143 | static GC grayGC[NGrays]; 144 | static Pixmap grayStipple[NGrays]; 145 | static string currentFont; 146 | static int currentSize; 147 | static int currentStyle; 148 | static XFontStruct *fontInfo; 149 | static double windowWidth, windowHeight; 150 | static double screenWidth, screenHeight; 151 | 152 | static string argV[] = { "xdisplay" }; 153 | static int argC = sizeof argV / sizeof argV[0]; 154 | 155 | static waitStateT waitState; 156 | 157 | static bool regionStarted; 158 | static double regionGrayScale; 159 | static XPoint *polygonPoints; 160 | static int nPolygonPoints; 161 | static int polygonSize; 162 | 163 | /* Private function prototypes */ 164 | 165 | static void StartToOpenDisplay(void); 166 | static void InitGC(void); 167 | static void ForceRedraw(void); 168 | static void RedrawWindow(void); 169 | static void StartPolygon(void); 170 | static void AddSegment(int x0, int y0, int x1, int y1); 171 | static void DisplayPolygon(void); 172 | static void RenderArc(double x, double y, double rx, double ry, 173 | double start, double sweep); 174 | static int SizeFromFontName(string fontName); 175 | static double InchesX(int x); 176 | static double InchesY(int y); 177 | static int PixelsX(double x); 178 | static int PixelsY(double y); 179 | static int ScaleX(double x); 180 | static int ScaleY(double y); 181 | 182 | /* Exported entries */ 183 | 184 | /* 185 | * Function: XDOpenDisplay 186 | * ----------------------- 187 | * The XDOpenDisplay function is relatively long but consists of 188 | * little more than a series of X calls to establish the 189 | * parameters of the display. 190 | */ 191 | 192 | void XDOpenDisplay(string title) 193 | { 194 | XWMHints xwmh; 195 | XSizeHints xsh; 196 | XFontStruct *font; 197 | Screen *screen; 198 | XSetWindowAttributes xswa; 199 | long width, height; 200 | int bitmask, scnum; 201 | double xScale, yScale, scaleFactor; 202 | char geometry[100]; 203 | 204 | StartToOpenDisplay(); 205 | if ((font = XLoadQueryFont(disp, "fixed")) == NULL) { 206 | Error("Can't create font"); 207 | } 208 | scnum = DefaultScreen(disp); 209 | screen = ScreenOfDisplay(disp, scnum); 210 | colormap = DefaultColormap(disp, scnum); 211 | drawColor = BlackPixel(disp, scnum); 212 | eraseColor = WhitePixel(disp, scnum); 213 | xScale = yScale = 1.0; 214 | if (windowWidth > screenWidth - 2 * RequiredMargin) { 215 | xScale = (screenWidth - 2 * RequiredMargin) / windowWidth; 216 | } 217 | if (windowHeight > screenHeight - 2 * RequiredMargin) { 218 | yScale = (screenHeight - 2 * RequiredMargin) / windowHeight; 219 | } 220 | scaleFactor = GLMinF(xScale, yScale); 221 | xdpi *= scaleFactor; 222 | ydpi *= scaleFactor; 223 | width = PixelsX(windowWidth); 224 | height = PixelsY(windowHeight); 225 | sprintf(geometry, "%dx%d+%d+%d", width, height, 226 | (DisplayWidth(disp, scnum) - width) / 2, 227 | (DisplayHeight(disp, scnum) - height) / 2); 228 | bitmask = XGeometry(disp, scnum, geometry, geometry, 229 | BorderPixels, 230 | font->max_bounds.width, 231 | font->max_bounds.ascent + font->max_bounds.descent, 232 | 1, 1, &xsh.x, &xsh.y, &xsh.width, &xsh.height); 233 | if (bitmask & (XValue | YValue)) xsh.flags |= USPosition; 234 | if (bitmask & (WidthValue | HeightValue)) xsh.flags |= USSize; 235 | mainWindow = XCreateSimpleWindow(disp, DefaultRootWindow(disp), 236 | xsh.x, xsh.y, xsh.width, xsh.height, 237 | BorderPixels, drawColor, eraseColor); 238 | XSetStandardProperties(disp, mainWindow, title, title, 239 | None, argV, argC, &xsh); 240 | xwmh.flags = (InputHint | StateHint); 241 | xwmh.input = False; 242 | xwmh.initial_state = NormalState; 243 | XSetWMHints(disp, mainWindow, &xwmh); 244 | xswa.colormap = colormap; 245 | xswa.bit_gravity = CenterGravity; 246 | XChangeWindowAttributes(disp, mainWindow, 247 | CWColormap | CWBitGravity, &xswa); 248 | XSelectInput(disp, mainWindow, 249 | ExposureMask | ButtonPressMask | ButtonReleaseMask); 250 | XMapWindow(disp, mainWindow); 251 | osWindow = XCreatePixmap(disp, DefaultRootWindow(disp), 252 | xsh.width, xsh.height, pixelDepth); 253 | fontInfo = NULL; 254 | InitGC(); 255 | regionStarted = FALSE; 256 | waitState = NotWaiting; 257 | XDClearDisplay(); 258 | XFlush(disp); 259 | } 260 | 261 | /* 262 | * Function: XDCloseDisplay 263 | * ------------------------ 264 | * This function frees the X structures allocated by the package. 265 | */ 266 | 267 | void XDCloseDisplay(void) 268 | { 269 | int i; 270 | 271 | XDestroyWindow(disp, mainWindow); 272 | XFreePixmap(disp, osWindow); 273 | XFreeGC(disp, mainGC); 274 | XFreeGC(disp, drawGC); 275 | XFreeGC(disp, eraseGC); 276 | for (i = 0; i < NGrays; i++) { 277 | XFreeGC(disp, grayGC[i]); 278 | XFreePixmap(disp, grayStipple[i]); 279 | } 280 | XCloseDisplay(disp); 281 | } 282 | 283 | /* 284 | * Function: XDDisplayFD 285 | * --------------------- 286 | * This function simply returns the file descriptor for the X 287 | * connection. The client cannot obtain this directly because the 288 | * display identified disp is private to this module. 289 | */ 290 | 291 | int XDDisplayFD(void) 292 | { 293 | return (XConnectionNumber(disp)); 294 | } 295 | 296 | /* 297 | * Function: XDProcessXEvent 298 | * ------------------------- 299 | * This function is used to read and respond to a pending X event. 300 | * It returns TRUE if an event was processed. 301 | */ 302 | 303 | bool XDProcessXEvent(void) 304 | { 305 | XEvent event; 306 | 307 | if (!XPending(disp)) return (FALSE); 308 | XNextEvent(disp, &event); 309 | if (event.xany.window == mainWindow) { 310 | switch (event.type) { 311 | case Expose: 312 | if (event.xexpose.count == 0) RedrawWindow(); 313 | break; 314 | case ButtonPress: 315 | if (waitState == WaitingForMouseDown) { 316 | waitState = NotWaiting; 317 | XMReleaseClient(); 318 | } 319 | break; 320 | case ButtonRelease: 321 | if (waitState == WaitingForMouseUp) { 322 | waitState = NotWaiting; 323 | XMReleaseClient(); 324 | } 325 | break; 326 | } 327 | } 328 | return (TRUE); 329 | } 330 | 331 | /* 332 | * Function: XDCheckForRedraw 333 | * -------------------------- 334 | * This function allows the client to specify that a quiescent point 335 | * has been achieved and that it would be a good time to redraw the 336 | * window. A redraw occurs only if graphics updates have been made. 337 | */ 338 | 339 | void XDCheckForRedraw(void) 340 | { 341 | if (redraw) ForceRedraw(); 342 | } 343 | 344 | /* 345 | * Function: XDSetRedrawFlag 346 | * ------------------------- 347 | * This function allows the client to indicate that the display has 348 | * changed and that a redraw operation should be performed when the 349 | * next call to XDCheckForRedraw occurs. This mechanism currently 350 | * sets a single flag and should at some point be redesigned to 351 | * maintain the update region. 352 | */ 353 | 354 | void XDSetRedrawFlag(void) 355 | { 356 | redraw = TRUE; 357 | } 358 | 359 | /* 360 | * Function: XDClearDisplay 361 | * ------------------------ 362 | * This function erases the entire display by filling with the 363 | * erase color. 364 | */ 365 | 366 | void XDClearDisplay(void) 367 | { 368 | int itemp; 369 | unsigned int width, height, utemp; 370 | Window wtemp; 371 | 372 | if (XGetGeometry(disp, osWindow, &wtemp, &itemp, &itemp, 373 | &width, &height, &utemp, &utemp) == 0) return; 374 | XFillRectangle(disp, osWindow, eraseGC, 0, 0, width, height); 375 | } 376 | 377 | /* 378 | * Function: XDDrawLine 379 | * -------------------- 380 | * This function draws the requested line unless a region is in 381 | * progress, in which case it adds the line segment to the polygon. 382 | */ 383 | 384 | void XDDrawLine(double x, double y, double dx, double dy) 385 | { 386 | int x0, y0, x1, y1; 387 | 388 | x0 = ScaleX(x); 389 | y0 = ScaleY(y); 390 | x1 = ScaleX(x + dx); 391 | y1 = ScaleY(y + dy); 392 | if (regionStarted) { 393 | AddSegment(x0, y0, x1, y1); 394 | } else { 395 | XDrawLine(disp, osWindow, (eraseMode) ? eraseGC : drawGC, 396 | x0, y0, x1, y1); 397 | } 398 | } 399 | 400 | /* 401 | * Function: XDDrawArc 402 | * ------------------- 403 | * This function ordinarily scales its arguments and uses them 404 | * to draw an arc using the standard XDrawArc call. If, however, 405 | * a region has been started, that arc must be rendered using 406 | * line segments, which is handled by RenderArc. 407 | */ 408 | 409 | void XDDrawArc(double x, double y, double rx, double ry, 410 | double start, double sweep) 411 | { 412 | int ixc, iyc, irx, iry, istart, isweep; 413 | 414 | if (regionStarted) { 415 | RenderArc(x, y, rx, ry, start, sweep); 416 | } else { 417 | ixc = ScaleX(x); 418 | iyc = ScaleY(y); 419 | irx = PixelsX(rx); 420 | iry = PixelsY(ry); 421 | istart = GLRound(start); 422 | isweep = GLRound(sweep); 423 | if (isweep < 0) { 424 | isweep = -isweep; 425 | istart -= isweep; 426 | } 427 | if (istart < 0) { 428 | istart = 360 - (-istart % 360); 429 | } 430 | istart %= 360; 431 | XDrawArc(disp, osWindow, (eraseMode) ? eraseGC : drawGC, 432 | ixc - irx, iyc - iry, 2 * irx, 2 * iry, 433 | 64 * istart, 64 * isweep); 434 | } 435 | } 436 | 437 | /* 438 | * Function: XDDrawText 439 | * -------------------- 440 | * This function transforms the client arguments and makes the 441 | * appropriate X call to display text on the screen. 442 | */ 443 | 444 | void XDDrawText(double x, double y, string text) 445 | { 446 | int ix, iy; 447 | 448 | ix = ScaleX(x); 449 | iy = ScaleY(y); 450 | XDrawString(disp, osWindow, (eraseMode) ? eraseGC : drawGC, 451 | ix, iy, text, strlen(text)); 452 | } 453 | 454 | /* 455 | * Function: XDTextWidth 456 | * --------------------- 457 | * This function simply calls the XTextWidth function to compute the 458 | * width of the displayed text and scales it to the client coordinate 459 | * system. 460 | */ 461 | 462 | double XDTextWidth(string text) 463 | { 464 | return (InchesX(XTextWidth(fontInfo, text, strlen(text)))); 465 | } 466 | 467 | /* 468 | * Function: XDSetFont 469 | * ------------------- 470 | * This function searches the font names table for a font that matches 471 | * the argument characteristics as closely as possible. As required 472 | * by the extgraph.h client interface, the function does not change 473 | * the font if no such font exists and selects the closest existing 474 | * size. 475 | */ 476 | 477 | string XDSetFont(string font, int size, int style) 478 | { 479 | char fontbuf[MaxFontName + 30]; 480 | string fontName, *fontList; 481 | int i, nFonts, bestIndex, bestSize, thisSize; 482 | bool ok; 483 | 484 | fontName = ConvertToLowerCase(font); 485 | if (StringEqual(fontName, "default")) { 486 | sprintf(fontbuf, "*-%s-*", DefaultFont); 487 | } else { 488 | sprintf(fontbuf, "*-%s-*", fontName); 489 | } 490 | FreeBlock(fontName); 491 | fontList = XListFonts(disp, fontbuf, MaxFontList, &nFonts); 492 | if (nFonts != 0) { 493 | bestIndex = -1; 494 | for (i = 1; i < nFonts; i++) { 495 | fontName = fontList[i]; 496 | ok = TRUE; 497 | if (style & Bold) { 498 | ok &= FindString("-bold-", fontName, 0) != -1; 499 | } else { 500 | ok &= FindString("-medium-", fontName, 0) != -1; 501 | } 502 | if (style & Italic) { 503 | ok &= FindString("-i-", fontName, 0) != -1; 504 | } else { 505 | ok &= FindString("-r-", fontName, 0) != -1; 506 | } 507 | if (ok) { 508 | thisSize = SizeFromFontName(fontName); 509 | if (bestIndex == -1 || abs(size - thisSize) 510 | < abs(size - bestSize)) { 511 | bestSize = thisSize; 512 | bestIndex = i; 513 | } 514 | } 515 | } 516 | if (bestIndex != -1) { 517 | if (fontInfo != NULL) XFreeFont(disp, fontInfo); 518 | fontInfo = XLoadQueryFont(disp, fontList[bestIndex]); 519 | if (fontInfo == NULL) { 520 | Error("Internal error: Can't find font"); 521 | } 522 | XSetFont(disp, drawGC, fontInfo->fid); 523 | XSetFont(disp, eraseGC, fontInfo->fid); 524 | XFreeFontNames(fontList); 525 | currentFont = CopyString(font); 526 | currentSize = bestSize; 527 | currentStyle = style; 528 | } 529 | } 530 | sprintf(fontbuf, "%d %d %s", currentSize, currentStyle, currentFont); 531 | return (CopyString(fontbuf)); 532 | } 533 | 534 | /* 535 | * Function: DisplayFontMetrics 536 | * ---------------------------- 537 | * This function returns the necessary font metric information through 538 | * its argument pointers. 539 | */ 540 | 541 | void DisplayFontMetrics(double *pAscent, double *pDescent, double *pHeight) 542 | { 543 | *pAscent = InchesY(fontInfo->max_bounds.ascent); 544 | *pDescent = InchesY(fontInfo->max_bounds.descent); 545 | *pHeight = InchesY(fontInfo->ascent + fontInfo->descent); 546 | } 547 | 548 | /* 549 | * Function: XDSetTitle 550 | * -------------------- 551 | * This function copies the user-supplied string into the window and 552 | * icon name. 553 | */ 554 | 555 | void XDSetTitle(string title) 556 | { 557 | XTextProperty tp; 558 | 559 | XStringListToTextProperty(&title, 1, &tp); 560 | XSetWMIconName(disp, mainWindow, &tp); 561 | XSetWMName(disp, mainWindow, &tp); 562 | } 563 | 564 | /* 565 | * Function: XDSetEraseMode 566 | * ------------------------ 567 | * This function sets the internal state of the display logic so 568 | * that it maintains the correct state of the eraseMode flag. In 569 | * the rest of the code, the eraseMode flag is used to control 570 | * which colors are used. 571 | */ 572 | 573 | void XDSetEraseMode(bool flag) 574 | { 575 | eraseMode = flag; 576 | } 577 | 578 | /* 579 | * Function: XDStartRegion 580 | * ----------------------- 581 | * This function changes the state of the package so that subsequent 582 | * calls to XDDrawLine and XDDrawArc are used to add segments to a 583 | * polygonal region instead of having them appear on the display. 584 | * See the code for StartPolygon, AddSegment, and DisplayPolygon for 585 | * details. 586 | */ 587 | 588 | void XDStartRegion(double grayScale) 589 | { 590 | regionStarted = TRUE; 591 | regionGrayScale = grayScale; 592 | StartPolygon(); 593 | } 594 | 595 | /* 596 | * Function: XDEndRegion 597 | * --------------------- 598 | * This function closes the region opened by XDStartRegion 599 | * and displays the assembled polygon. 600 | */ 601 | 602 | void XDEndRegion(void) 603 | { 604 | DisplayPolygon(); 605 | regionStarted = FALSE; 606 | } 607 | 608 | /* 609 | * Function: XDGetMouse 610 | * -------------------- 611 | * This function returns the current mouse state through the 612 | * argument pointers. 613 | */ 614 | 615 | void XDGetMouse(bool *buttonStateP, double *xp, double *yp) 616 | { 617 | Window root, child; 618 | int rootX, rootY, winX, winY; 619 | unsigned int buttons; 620 | 621 | (void) XQueryPointer(disp, mainWindow, &root, &child, 622 | &rootX, &rootY, &winX, &winY, &buttons); 623 | *buttonStateP = (buttons != 0); 624 | *xp = InchesX(winX); 625 | *yp = windowHeight - InchesY(winY); 626 | } 627 | 628 | /* 629 | * Function: XDWaitForMouse 630 | * ------------------------ 631 | * This function waits for the mouse to enter the state given 632 | * by down. 633 | */ 634 | 635 | void XDWaitForMouse(bool buttonState) 636 | { 637 | bool currentState; 638 | double x, y; 639 | 640 | XDGetMouse(¤tState, &x, &y); 641 | if (buttonState == currentState) { 642 | XMReleaseClient(); 643 | } else { 644 | waitState = (buttonState) ? WaitingForMouseDown : WaitingForMouseUp; 645 | } 646 | } 647 | 648 | /* 649 | * Function: XDSetColor 650 | * -------------------- 651 | * This function sets the pen color as specified by the arguments. 652 | */ 653 | 654 | void XDSetColor(double red, double green, double blue) 655 | { 656 | XColor color; 657 | 658 | color.red = red * 65535; 659 | color.green = green * 65535; 660 | color.blue = blue * 65535; 661 | if (XAllocColor(disp, colormap, &color) != 0) { 662 | drawColor = color.pixel; 663 | XSetForeground(disp, drawGC, drawColor); 664 | } 665 | } 666 | 667 | /* 668 | * Function: XDSetWindowSize 669 | * ------------------------- 670 | * This function sets the width and height values of the window. 671 | */ 672 | 673 | void XDSetWindowSize(double width, double height) 674 | { 675 | windowWidth = width; 676 | windowHeight = height; 677 | } 678 | 679 | /* 680 | * Function: XDGetScreenSize 681 | * ------------------------- 682 | * This function returns the screen size and may be called 683 | * prior to the XDOpenDisplay call or from the client fork. 684 | */ 685 | 686 | void XDGetScreenSize(double *pScreenWidth, double *pScreenHeight) 687 | { 688 | StartToOpenDisplay(); 689 | *pScreenWidth = screenWidth; 690 | *pScreenHeight = screenHeight; 691 | } 692 | 693 | /* 694 | * Function: XDGetResolution 695 | * ------------------------- 696 | * This function returns the screen resolution, possibly modified by 697 | * the scale reduction. 698 | */ 699 | 700 | void XDGetResolution(double *pXDPI, double *pYDPI) 701 | { 702 | StartToOpenDisplay(); 703 | *pXDPI = xdpi; 704 | *pYDPI = ydpi; 705 | } 706 | 707 | /* 708 | * Function: XDGetNColors 709 | * ---------------------- 710 | * This function returns the number of colors supported by the screen. 711 | */ 712 | 713 | int XDGetNColors(void) 714 | { 715 | StartToOpenDisplay(); 716 | return (1 << pixelDepth); 717 | } 718 | 719 | /* Private functions */ 720 | 721 | /* 722 | * Function: StartToOpenDisplay 723 | * Usage: StartToOpenDisplay(); 724 | * ---------------------------- 725 | * This function checks to see if the display is open and, if 726 | * not, does just enough work to ensure that the fixed parameters 727 | * are set. 728 | */ 729 | 730 | static void StartToOpenDisplay(void) 731 | { 732 | int scnum; 733 | Screen *screen; 734 | 735 | if (displayIsOpen) return; 736 | if ((disp = XOpenDisplay(NULL)) == NULL) { 737 | Error("Can't open display"); 738 | } 739 | scnum = DefaultScreen(disp); 740 | screen = ScreenOfDisplay(disp, scnum); 741 | pixelDepth = DefaultDepth(disp, scnum); 742 | screenWidth = WidthMMOfScreen(screen) / 25.4; 743 | screenHeight = HeightMMOfScreen(screen) / 25.4; 744 | xdpi = WidthOfScreen(screen) / screenWidth; 745 | ydpi = HeightOfScreen(screen) / screenHeight; 746 | displayIsOpen = TRUE; 747 | } 748 | 749 | /* 750 | * Function: InitGC 751 | * Usage: InitGC(); 752 | * ---------------- 753 | * This function is used as part of the initialization and creates 754 | * all of the GC structures used for drawing. 755 | */ 756 | 757 | static void InitGC(void) 758 | { 759 | XGCValues gcv; 760 | Window root; 761 | int i; 762 | 763 | gcv.foreground = eraseColor; 764 | gcv.background = eraseColor; 765 | eraseGC = XCreateGC(disp, osWindow, GCFgBg, &gcv); 766 | gcv.foreground = drawColor; 767 | mainGC = XCreateGC(disp, mainWindow, GCFgBg, &gcv); 768 | drawGC = XCreateGC(disp, osWindow, GCFgBg, &gcv); 769 | gcv.fill_style = FillOpaqueStippled; 770 | root = DefaultRootWindow(disp); 771 | for (i = 0; i < NGrays; i++) { 772 | grayStipple[i] = gcv.stipple = 773 | XCreateBitmapFromData(disp, root, grayList[i], 8, 8); 774 | grayGC[i] = XCreateGC(disp, osWindow, 775 | GCFgBg | GCStipple | GCFillStyle, &gcv); 776 | } 777 | } 778 | 779 | /* 780 | * Function: ForceRedraw 781 | * Usage: ForceRedraw(); 782 | * --------------------- 783 | * This function forces the screen to update itself by sending an 784 | * Expose event for the window. 785 | */ 786 | 787 | static void ForceRedraw(void) 788 | { 789 | XEvent event; 790 | 791 | event.type = Expose; 792 | event.xany.window = mainWindow; 793 | event.xexpose.count = 0; 794 | XSendEvent(disp, mainWindow, False, 0, &event); 795 | XFlush(disp); 796 | } 797 | 798 | /* 799 | * Function: RedrawWindow 800 | * Usage: RedrawWindow(); 801 | * ---------------------- 802 | * This function redraws the active display window by copying bits 803 | * from the offscreen bitmap. 804 | */ 805 | 806 | static void RedrawWindow(void) 807 | { 808 | int itemp; 809 | unsigned int width, height, utemp; 810 | Window wtemp; 811 | 812 | if (XGetGeometry(disp, mainWindow, &wtemp, &itemp, &itemp, 813 | &width, &height, &utemp, &utemp) == 0) return; 814 | XCopyArea(disp, osWindow, mainWindow, mainGC, 0, 0, width, height, 815 | 0, 0); 816 | redraw = FALSE; 817 | } 818 | 819 | /* 820 | * Functions: StartPolygon, AddSegment, EndPolygon 821 | * Usage: StartPolygon(); 822 | * AddSegment(x0, y0, x1, y1); 823 | * AddSegment(x1, y1, x2, y2); 824 | * . . . 825 | * DisplayPolygon(); 826 | * ----------------------------------------------- 827 | * These functions implement the notion of a region in the X 828 | * world, where the easiest shape to fill is a polygon. Calling 829 | * StartPolygon initializes the array polygonPoints so that 830 | * subsequent calls to AddSegment will add points to it. 831 | * The points in the polygon are assumed to be contiguous, 832 | * because the client interface checks for this property. 833 | * Because polygons involving arcs can be quite large, the 834 | * AddSegment code extends the polygonPoints list if needed 835 | * by doubling the size of the array. All storage is freed 836 | * after calling DisplayPolygon, which uses the XFillPolygon 837 | * call to generate the display. 838 | */ 839 | 840 | static void StartPolygon(void) 841 | { 842 | polygonPoints = NewArray(PStartSize, XPoint); 843 | polygonSize = PStartSize; 844 | nPolygonPoints = 0; 845 | } 846 | 847 | static void AddSegment(int x0, int y0, int x1, int y1) 848 | { 849 | XPoint *newPolygon; 850 | int i; 851 | 852 | if (nPolygonPoints >= polygonSize) { 853 | polygonSize *= 2; 854 | newPolygon = NewArray(polygonSize, XPoint); 855 | for (i = 0; i < nPolygonPoints; i++) { 856 | newPolygon[i] = polygonPoints[i]; 857 | } 858 | FreeBlock(polygonPoints); 859 | polygonPoints = newPolygon; 860 | } 861 | if (nPolygonPoints == 0) { 862 | polygonPoints[nPolygonPoints].x = x0; 863 | polygonPoints[nPolygonPoints].y = y0; 864 | nPolygonPoints++; 865 | } 866 | polygonPoints[nPolygonPoints].x = x1; 867 | polygonPoints[nPolygonPoints].y = y1; 868 | nPolygonPoints++; 869 | } 870 | 871 | static void DisplayPolygon(void) 872 | { 873 | GC fillGC; 874 | int i, px; 875 | 876 | if (eraseMode) { 877 | fillGC = eraseGC; 878 | } else { 879 | px = regionGrayScale * (NGrays - 1) + 0.5 - Epsilon; 880 | fillGC = grayGC[px]; 881 | XSetForeground(disp, fillGC, drawColor); 882 | } 883 | if (polygonPoints[0].x != polygonPoints[nPolygonPoints-1].x 884 | || polygonPoints[0].y != polygonPoints[nPolygonPoints-1].y) { 885 | polygonPoints[nPolygonPoints++] = polygonPoints[0]; 886 | } 887 | XFillPolygon(disp, osWindow, fillGC, 888 | polygonPoints, nPolygonPoints, 889 | Complex, CoordModeOrigin); 890 | FreeBlock(polygonPoints); 891 | } 892 | 893 | /* 894 | * Function: RenderArc 895 | * Usage: RenderArc(x, y, rx, ry, start, sweep); 896 | * --------------------------------------------- 897 | * This function is identical to the XDDrawArc function except 898 | * that the arc is rendered using line segments as part of a 899 | * polygonal region. 900 | */ 901 | 902 | static void RenderArc(double x, double y, double rx, double ry, 903 | double start, double sweep) 904 | { 905 | double t, mint, maxt, dt; 906 | int ix0, iy0, ix1, iy1; 907 | 908 | if (sweep < 0) { 909 | start += sweep; 910 | sweep = -sweep; 911 | } 912 | dt = atan2(InchesY(1), GLMaxF(fabs(rx), fabs(ry))); 913 | mint = GLRadians(start); 914 | maxt = GLRadians(start + sweep); 915 | ix0 = ScaleX(x + rx * cos(mint)); 916 | iy0 = ScaleY(y + ry * sin(mint)); 917 | for (t = mint + dt; t < maxt; t += dt) { 918 | if (t > maxt - dt / 2) t = maxt; 919 | ix1 = ScaleX(x + rx * cos(t)); 920 | iy1 = ScaleY(y + ry * sin(t)); 921 | AddSegment(ix0, iy0, ix1, iy1); 922 | ix0 = ix1; 923 | iy0 = iy1; 924 | } 925 | } 926 | 927 | /* 928 | * Function: SizeFromFontName 929 | * Usage: SizeFromFontName(); 930 | * -------------------------- 931 | * This function is a simple utility for XDSetFont that returns 932 | * the font size from an X font name. 933 | */ 934 | 935 | static int SizeFromFontName(string fontName) 936 | { 937 | char *sizePtr; 938 | 939 | sizePtr = strstr(fontName, "--"); 940 | if (sizePtr == NULL) return (-1); 941 | return (atoi(sizePtr + 2)); 942 | } 943 | 944 | /* Low-level conversion functions */ 945 | 946 | /* 947 | * Functions: InchesX, InchesY 948 | * Usage: inches = InchesX(pixels); 949 | * inches = InchesY(pixels); 950 | * -------------------------------- 951 | * These functions convert distances measured in pixels to inches. 952 | * Because the resolution may not be uniform in the horizontal and 953 | * vertical directions, the coordinates are treated separately. 954 | */ 955 | 956 | static double InchesX(int x) 957 | { 958 | return ((double) x / xdpi); 959 | } 960 | 961 | static double InchesY(int y) 962 | { 963 | return ((double) y / ydpi); 964 | } 965 | 966 | /* 967 | * Functions: PixelsX, PixelsY 968 | * Usage: pixels = PixelsX(inches); 969 | * pixels = PixelsY(inches); 970 | * -------------------------------- 971 | * These functions convert distances measured in inches to pixels. 972 | */ 973 | 974 | static int PixelsX(double x) 975 | { 976 | return (GLRound(x * xdpi + Epsilon)); 977 | } 978 | 979 | static int PixelsY(double y) 980 | { 981 | return (GLRound(y * ydpi + Epsilon)); 982 | } 983 | 984 | /* 985 | * Functions: ScaleX, ScaleY 986 | * Usage: pixels = ScaleX(inches); 987 | * pixels = ScaleY(inches); 988 | * ------------------------------- 989 | * These functions are like PixelsX and PixelsY but convert coordinates 990 | * rather than lengths. The difference is that y-coordinate values must 991 | * be inverted top to bottom to support the cartesian coordinates of 992 | * the graphics.h model. 993 | */ 994 | 995 | static int ScaleX(double x) 996 | { 997 | return (PixelsX(x)); 998 | } 999 | 1000 | static int ScaleY(double y) 1001 | { 1002 | return (PixelsY(windowHeight - y)); 1003 | } 1004 | --------------------------------------------------------------------------------