Libraries
Introduction What ? A library is a set of functions packaged as a system resource and intended for use by other programs on the system. Normally, a library is not written for a single program but is intended for use by many programs.
Introduction Why ? By packaging the functions as a system resource, the code does not need to be rewritten for every program that uses it. A Simplistic Program Translation Scheme Translator m.c p ASCII source file Binary executable object file (memory image on disk)
A Simplistic Program Translation Scheme Translator m.c p ASCII source file Binary executable object file (memory image on disk) Problems: Efficiency: small change requires complete recompilation Modularity: hard to share common functions (e.g. printf) Solution: Linker
What a library is not ! A library is not an executable program It DOES NOT include the main function A library includes a number of functions(other than main) than can be combined with main() to form a complete executable program.
In 321 … Two major topics to be covered in 321 in Libraries : Using a Library Creating your own library (you will have to create your own library and use it in the 2nd project (Project Database Manager)
Using a Library There are 2 basic steps to using a library : - One or more header files must be included in the C program code. - the Library must be linked into the executable
Header Files #include <stdio.h> #include <math.h> main(){ An Example first : Use of math library The command line argument –lm tells the compiler to link (-l) to a library file named m. #include <stdio.h> #include <math.h> main(){ double x, s; S = 8.0; x=sqrt (s); printf(“%lf\n”, x); } gcc sqrt.c –lm
Header files A header file does NOT contain the code for any functions in the library (the code is in the .c file) It contains the instructions for how to use the library. It contains the function prototypes e.g. double sqrt(double x) constants e.g: #define M_PI 3.14159265358979323846 typedef and struct definitions to create library-specific aliases for common data types or to create new data types. E.g. : File data type => struct _IO_FILE { int _flags; int _fileno; …… } Typedef struct _IO_FILE FILE;
Header files typedef and struct definitions to create library-specific aliases for common data types or to create new data types. E.g. : File data type => struct _IO_FILE { int _flags; int _fileno; …… } typedef struct _IO_FILE FILE; This code that contains information about accessing a file is in stdio.h #include <stdio.h> FILE *fptr;
Location of header files Typically , header files are in /usr/include. If they are placed in any non-standard location, then the compiler must be told that by using the –I path . E.g. gcc –o sq sq.c –I/usr/include/mathlib – lm The option –I/usr/include/mathlib tells the compiler to look in the /usr/include/mathlib directory, in addition to the standard locations, for any requested include files.
Library Files Library files contain the actual code for the functions in the library must be linked with the main c file during compilation with the –l option are typically stored in /usr/lib on Unix
Library Files Library files On Unix, by convention, library files have an extension of a or so and they all have a prefix of lib If not in the default folder, then we have to use the –L option : gcc –o xp1 xp1.c –lX11 –L/usr/X11R6/lib
Examples of libraries Standard C library Curses.h X library
The Curses library Curses is a basic graphics library for use on a character terminal screen. Continued on slide 27
Linking Linking is the process of combining the machine code in object files (.o files on a Unix system) with machine code from library files to create an executable file
Static ~Dynamic Linking In static linking, the called program is link-edited into the calling program. In dynamic linking, the called and calling program will exist as separate load modules
Static ~Dynamic Linking(SL~DL) In static linking, when an executable is produced, all of the needed machine code from the library is copied into the executable file when the link command is run. When the executable is actually run, the operating system finds the needed library machine code and loads it into computer memory along with the machine code from the executable. In DL, machine code from the library is not copied into the executable file. Instead, information about where to find the library machine code is put in the executable file.
506 Kbyte 1 Kbyte statically linked with results in 5 Kbyte dynamically linked with results in Library functions are mapped into the process at runtime hello.o a.out libc.so libc.a
4.The include and library files can be used like any other library Making Libraries 1. Source code for the desired function must be compiled and stored in object code files 2. The object code files are packaged together to create a library file 3.An include file is written defining the prototypes of the functions in the library file – may include constants, struct definitions , global variables etc 4.The include and library files can be used like any other library
Static Libraries - Static libraries are known as archives Are created using the ar utility
int Largest(int first, int second) //one.c int Largest(int first, int second) { if (first > second) return(1); else if (second > first) return(2); else return(0); } //two.c void HelloWorld() printf("Hello world\n"); double SevenPointSeven() return(7.7); //custom_lib.h int Largest(int,int); void HelloWorld(); double SevenPointSeven(); //test_lib.c #include <stdio.h> #include "custom_lib.h" int main() int i,first,second; printf("Enter a number: "); scanf("%d",&first); printf("Enter a second number: "); scanf("%d",&second); i=Largest(first, second); if (i == 1) printf("First number is larger\n"); else if (i == 2) printf("Second number is larger\n"); printf("Same number\n"); printf("%lf\n",SevenPointSeven()); HelloWorld(); luna> gcc –c one.c luna> gcc –c two.c luna> ar r libcustom.a one.o two.o luna> ar t libcustom.a one.o two.o luna> gcc –o test_lib test_lib.c –lcustom –L. (-l option tells gcc to link to the library file libcustom.a -L. option tells gcc to search for the libraries in the current folder in addition to the system library path.)
Dynamic Link library Dynamically linked library is created by the link editor ld . File extension for a dynamic library is .so ( for shared object)
int Largest(int first, int second) //one.c int Largest(int first, int second) { if (first > second) return(1); else if (second > first) return(2); else return(0); } //two.c void HelloWorld() printf("Hello world\n"); double SevenPointSeven() return(7.7); //custom_lib.h int Largest(int,int); void HelloWorld(); double SevenPointSeven(); //test_lib.c #include <stdio.h> #include "custom_lib.h" int main() int i,first,second; printf("Enter a number: "); scanf("%d",&first); printf("Enter a second number: "); scanf("%d",&second); i=Largest(first, second); if (i == 1) printf("First number is larger\n"); else if (i == 2) printf("Second number is larger\n"); printf("Same number\n"); printf("%lf\n",SevenPointSeven()); HelloWorld(); The precise method for creating shared libraries varies between different systems. There are two main parts to the process; firstly the objects to be included in the shared library must be compiled, usually with options to indicate that the code is to be position-independent; secondly, these objects are linked together to form the library. luna> gcc –fpic –c one.c luna> gcc –fpic -c two.c Luna> gcc -shared -o libshared.so one.o two.o Luna> gcc hello.c l-lshared –L.
Five Secrets of Linking with Libraries 1. Dynamic libraries are called libsomething.so and static are called libsomething.a 2. You tell the compiler to link with , for example, libsomething.so by giving the option –lsomething (the lib and the file extension parts are dropped) 3. The compiler expects to find the libraries in certain directories .(/usr/lib by default – use the –Lpathname to tell the linker a list of other folders in which to search for libraries that have been specified with the –l option.) 4. Identify your libraries by looking at the header files you have used. 5. Always put the –l library option at the rightmost end of your compilation command line
The Curses library Curses is a basic graphics library for use on a character terminal screen. It provides the lowest level of graphics and dates back to the time when most computer displays could print only text (they couldn’t display images or graphics – these displays were called terminals) . The ncurses (new curses) library is managed by the GNU project . (www.gnu.org/software/ncurses/ncurses.html)
The Curses library – Purpose Many widely-used programs need to make use of a terminal’s cursor-movement capabilities. A familiar example is the vi text editor; most of its commands make use of such capabilities. For example, hitting the j key while in vi will make the cursor move up one line. Typing dd will result in the current line being erased, the lines below it moving up one line each, and the lines above it remaining unchanged. A potential problem with all this is that different terminals have different ways in which to specify a given type of cursor motion. For example, if a program wants to make the cursor move up one line on a VT100 terminal, the program needs to send the characters Escape, [, 2 and J but for a Televideo 920C terminal, the program would have to send the ctrl-K character, which has code 11
The Curses library – An example #include <stdio.h> int main(){ printf("%c%c%c%c", 27, '[', '2', 'J'); }
The Curses library- Purpose Clearly, the authors of programs like vi would go crazy trying to write different versions for every terminal, and worse yet, anyone else writing a program which needed cursor movement would have to “re-invent the wheel,” i.e. do the same work that the vi-writers did, a big waste of time. That is why the curses library was developed. The goal was to alleviate authors of cursor-oriented programs like vi of the need to write different code for different terminals. The programs would make calls to the library, and the library would sort out what to do for the given terminal type.
The Curses library For example, if your program needs to clear the screen, it would not (directly) use any character sequences like those above. Instead, it would simply make the call clear(); and curses would do the work on the program’s behalf, i.e. would print the characters Escape, [, 2 and J, causing the screen to clear.
The Curses library #include <stdio.h> #include <curses.h> int main(){ initscr(); //printf("%c%c%c%c", 27, '[', '2', 'J'); clear(); refresh(); endwin(); }
The Curses library Include and Library Files In order to use curses, you must include in your source code a statement #include <curses.h> and you must link in the curses library: gcc -o output sourcefile.c -lcurses
The Curses library – An example Performs the initialization – what sort of graphics card the system has, open the device driver for it, record its size and properties etc. #include <curses.h> main() { initscr(); clear(); /* clear screen */ move(10,20); /* row 10, column 20 */ addstr("Hello world"); /* add a string */ move(LINES-1,0); /* move to LL */ refresh(); /* update the screen */ getch(); /* wait for user input */ endwin(); } The device driver is closed and any dynamic memory allocated to it is freed
The curses Library – An example
The curses Library – I/O control There are 3 important concepts in I/O control : Buffering Echoing Blocking
Buffering Buffering refers to the process of temporarily storing bytes on a stream, and grouping them up before transferring them to the destination. A buffer can be used on any stream – input or output (as shown below) : (CR to flush) (refresh() to flush) Keyboard Program Display Line Buffer Buffer
Buffering By default, characters sent to the curses output window are buffered => the characters are not displayed until the buffer is flushed , sending all the characters to the terminal display . Flushing is accomplished by the refresh() function call.
Buffering Character input is unbuffered by default => functions that read the keyboard like getch() , return immediately after any key is pressed. Function scanf() is line buffered => it does not return until the user presses ENTER. The advantage with line buffering is that a user can correct typing mistakes using the backspace or delete key before actually committing the input to the program.
Buffering Line buffering can be turned on in curses using the nocbreak() function . #include <curses.h> main() { initscr(); /* turn on curses */ nocbreak(); /* turn on line buffering - use cbreak to turn it off */ /* by default keyboard input is unbuffered */ getch(); /* wait for user input */ refresh(); endwin(); /* turn off curses */ } => This program will allow the user to type any number of keystrokes; the program will not terminate until [ENTER] is pressed. This is because the input is line buffered.
Echoing Echoing refers to the process of copying bytes from the input stream to the output stream. When echoing is turned on, every byte that appears on the input stream is copied directly to the output stream, in addition to being given to the program for processing . Echoing is how a user can see what he or she is typing while providing input to a program.
Echoing By default, keyboard input in curses is echoed. #include <curses.h> main() { int i; initscr(); noecho(); /* turn off echoing */ for (i=0; i<5; i++) getch(); /* wait for user input */ endwin(); } You can use the echo() function to turn on the echoing.
Blocking Blocking refers to the process of how the program will wait for bytes to appear on the input stream. When the blocking is turned on, every function call for input will wait until data appears on the input stream . The program will not continue until input is received.
Blocking When blocking is turned off, the function call will check to see if data is present. If data is present, the read occurs normally, just as if blocking were turned on. If no data is present, the function call will return immediately and inform the program that no data was present. This allows the program to continue whether input data is present or not. For Example : scanf function is a blocking function – it will wait until input is received. The same is true for curses library.
Blocking #include <curses.h> main() { int i; initscr(); nodelay(stdscr, TRUE); /* turn off blocking */ for (i=0; i<5; i++) getch(); /* wait for user input? */ sleep(1); } endwin(); => When this program is run, even if the user doesn’t touch the keyboard, the program finishes in 5 seconds.
Blocking - Blocking does not have to be on (indefinite) or off (immediate) . Blocking can occur for a preselected amount of time , allowing the program to continue if no input is received during that time (timed blocking). For example : #include <stdio.h> main(){ int I; initscr(); halfdelay(5); //blocking = 5/10 second for (i=0; i<5; i++) getch(); //wait for user input endwin(); } blocking is set to 0.5 seconds each getch() will wait 0.5 sec for input to appear, but if nothing appears in that time, the function returns and the program continues. Thus, if this program is run without touching the keyboard, it will run for 2.5 sec and then end. 45
Blocking Being able to control blocking is important in several situations like : A banking machine typically does not wait forever for a user to provide a password, or to command a transaction. After waiting a fixed amount of time, a banking machine program typically continues to a portion of the program that ends the banking session, in order to protect the user. (timed blocking)
Printing on the screen There are three functions which you can use to print output on screen. addch() class: Print single character with attributes printw() class: Print formatted output similar to printf() mvprintw() can be used to move the cursor to a position and then print => mvprintw(10,2,”Welcome”) is the same as move(10,2); printw(“%s\n”, Welcome”); addstr() class: Print strings
getmaxyx ? getmaxyx(stdscr,row,col); /* get the number of rows and columns */
Reading input ! getch() class: Get a character scanw() class: Get formatted input getstr() class: Get strings
A Complete Example #include <curses.h> int main( void ) { char name[20], name_f[20]; int id, id_f, age_f=0, not_found=0; FILE *fptr; fptr=fopen("test_curses.txt", "r"); initscr(); // Start curses mode printw( "Welcome to our World !!!" ); // Print Hello World move(15,30); printw("Name : "); refresh(); // Print it on to the real screen getstr(name); // Wait for user input move(15, 70); printw("Id :"); scanw("%d",&id); mvprintw(20,20, "Your name is %s and Id is %d \n", name, id); while (fscanf(fptr,"%d%19s%d",&id_f,name_f,&age)!=EOF){ if (id==id_f){ printw("And your age is %d\n", age_f); not_found=1; } attron(A_BOLD); if (not_found==0) printw("You could not be found \n"); getch(); endwin(); // End curses mode return 0;
Some Functions : initscr() attron( ) endwin() getmaxyx(stdscr,row,col) clear() refresh() move() scanw() getch() getstr() printw() addch() addstr()