第六章 使用curses函数库管理基于文本的屏幕

来源:互联网 发布:淘宝如何做企业店铺 编辑:程序博客网 时间:2024/05/24 06:08

screen1.c

#include <unistd.h>#include <stdlib.h>#include <curses.h>int main(){    initscr();    move(5, 15);    printw("%s", "hello world");    refresh();    sleep(2);    endwin();    exit(EXIT_SUCCESS);}

initscr, endwin:所有的curses程序必须以initscr函数开始,以endwin函数结束

#include <curses.h>WINDOW *initscr(void);int endwin(void);
  1. initscr函数在一个程序中只调用一次,如果成功,它返回一个指向stdscr结构的指针.如果失败,它就输出一条诊断错误信息并使程序退出
  2. endwin函数在成功时候返回OK,失败时返回ERR.
  3. 可以先调用endwin函数退出curses,然后通过调用clearok(stdscr, 1)和refresh函数继续curses操作.这实际上是首先让curses忘记物理屏幕的样子,然后强迫它执行一次完整的屏幕原文重现

输出到屏幕

#include <curses.h>int addch(const chtype char_to_add);int addchstr(chtype *const string string_to_add);int printw(char *format, ...);int refresh(void);int box(WINDOW *win_ptr, chtype vertical_char,        chtype horizontal_char);int insch(chtype char_to_insert);int insertln(void);int delch(void);int deleteln(void);int beep(void);int flash(void);
  1. chtype实际上是unsigned long类型的一个typedef类型定义
  2. add系列函数在光标当前的位置添加指定的字符或者字符串
  3. printw函数采用与printf函数相同的方法对字符串进行格式化,然后将其添加到光标的当前位置
  4. refresh函数的作用是刷新物理屏幕,成功时候返回OK,发生错误时返回ERR
  5. box函数用来围绕一个窗口绘制方框
  6. insch函数插入一个字符,将已有字符向右移
  7. insertln函数的作用是插入一个空白行,将现有行一次向下移一行
  8. 两个delete函数的作用与上述两个insert函数正好相反
  9. 如果让程序发出声音,可以调用beep函数
  10. flash函数的作用是使屏幕发生闪烁,如果无法产生闪烁效果,他将尝试在终端发出声音

从屏幕读取

#include <curses.h>chtype inch(void);int instr(char *string);int innstr(char *string, int number_of_characters);
  1. inch函数总是可用的,但是instr和innstr函数并不总被支持
  2. inch函数返回光标当前位置的字符及其属性信息
  3. 注意inch返回的并不是一个字符,而是一个chtype类型的变量
  4. instr和innstr函数则将返回内容写到字符数组中

清除屏幕

#include <curses.h>int erase(void);int clear(void);int clrtobot(void);int clrtoeol(void);
  1. erase函数在每个屏幕位置写上空白字符.clear函数的功能类似erase函数他也用于清屏
  2. clear函数可以通过在内部调用一个底层函数clearok来强制重现屏幕原文.
  3. clearok函数会强制执行清屏操作,并在下一次调用refresh函数时重现屏幕原文
  4. clrtobot函数清除当前光标位置直到屏幕结尾的所有内容
  5. clrtoeol函数清除当前光标位置直到光标所处行行尾的所有内容

移动光标

#include <curses.h>int move(int new_y, int new_x);int leaveok(WINDOW *window_ptr, bool leave_flag);
  1. move函数用来将裸机光标的位置移到指定地点.
  2. leaveok函数设置了一个标志,该标志用于控制在屏幕刷新后cueses将物理光标放置的位置,默认情况下,该标志设置为flase,这意味着屏幕刷新后,硬件光标将停留在屏幕上逻辑光标所处的位置.如果该标志被设置为true,则硬件光标会被随机的放在屏幕上任意的位置

字符属性

#include <curses.h>int attron(chtype attribute);int attroff(chtype attribute);int attrset(chtyppe attribute);int standout(void);int standend(void);
  1. 每个cueses字符都可以有一些属性用于控制字符在屏幕上的显示方式,前提是用于显示的硬件设备能够支持要求的属性.
  2. 预定义的属性有:A_BLINK, A_BOLD, A_DIM, A_REVERSE, A_STANDOUT和A_UNDERLINE
  3. attrset函数设置cueses的属性
  4. attron和attroff函数在不影响其它属性的前提下启用或者关闭指定的属性
  5. standout和standend函数提供了一种更加通用的强调或者”突出”模式,在大多数终端上它通常被映射为反白显示

moveadd.c:移动,插入和属性

#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <curses.h>int main(){    const char witch_one[] = " First Witch ";    const char witch_two[] = " Second Witch ";    const char *scan_ptr;    initscr();    move(5, 15);    attron(A_BOLD);    printw("%s", "Macbeth");    attroff(A_BOLD);    refresh();    sleep(1);    move(8, 15);    attron(A_STANDOUT);    printw("%s", "Thunder and lightning");    attroff(A_STANDOUT);    refresh();    sleep(1);    move(10, 10);    printw("%s", "When shall we stree meet again");    move(11, 23);    printw("%s", "In thunder, lightning, or in rain?");    move(13, 10);    printw("%s", "when the hurglyburly's done,");    move(14, 23);    printw("%s", "when the battle's lost and won.");    refresh();    sleep(1);    attron(A_DIM);    scan_ptr = witch_one + strlen(witch_one) - 1;    while(scan_ptr != witch_one){        move(10, 10);        insch(*scan_ptr--);    }    scan_ptr = witch_two + strlen(witch_two) - 1;    while(scan_ptr != witch_two){        move(13, 10);        insch(*scan_ptr--);    }    attroff(A_DIM);    refresh();    sleep(1);    move(LINES -1, COLS -1);    refresh();    sleep(1);    endwin();    exit(EXIT_SUCCESS);}
  1. scan_ptr = witch_one + strlen(witch_one) - 1: 表示sca_ptr的地址相当一witch_one的地址向右移动strlen(witch) - 1

键盘模式

#include <curses.h>int echo(void);int noecho(void);int cbreak(void);int nocbreak(void);int raw(void);int noraw(void);
  1. 两个echo函数用于开启或关闭输入字符的回显功能
  2. 预处理模式(cooked模式)是基于行的,只有在用户按下回车键之后才会被传送给程序
  3. cbreak模式:在这种模式下字符一经键入就被立即传递给程序
  4. raw函数调用的作用是关闭特殊字符的处理
  5. nocbreak函数调用将输入模式重新设置为cooked模式,但特殊字符的处理方式保持不变
  6. noraw函数调用同时恢复cooked模式和特殊字符处理功能

键盘输入

#include <curses.h>int getch(void);int getstr(char *string);int getnstr(char *string, int number_of_characters);int scanw(char *format, ...);
  1. 这些函数的行为与非curses版本的getchar,gets和scanf非常类似
  2. getstr函数对齐返回的字符串的长度没有限制,所以使用这个函数时要非常小心,如果所使用的curses版本支持getnstr函数(他可以限制对读取的字符数目加以限制),你就应该尽可能的使用它来代替getstr函数

ipmode.c

#include <unistd.h>#include <stdlib.h>#include <curses.h>#include <string.h>#define PW_LEN 256#define NAME_LEN 256int main(){    char name[NAME_LEN];    char password[PW_LEN];    const char *real_password = "xyzzy";    int i = 0;    initscr();    move(5, 10);    printw("%s", "Please login: ");    move(7, 10);    printw("%s", "User name:");    getstr(name);    move(8, 10);    printw("%s", "Password: ");    refresh();    cbreak();    noecho();    memset(password, '\0', sizeof(password));    while(i < PW_LEN){        password[i] = getch();        if(password[i] == '\n')            break;        move(8, 20 + i);        addch('*');        refresh();        i++;    }    echo();    nocbreak();    move(11, 10);    if(strncmp(real_password, password,               strlen(real_password)) == 0)        printw("%s", "Correct");    else        printw("%s", "Incorrect");    printw("%s", " password");    refresh();    sleep(2);    endwin();    exit(EXIT_SUCCESS);}
  1. void *memset(void *s, int ch, size_t n);
  2. 函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
  3. memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法

WINDOW结构

#include <curses.h>WINDOW *newwin(int num_of_lines, int num_of_cols,               int start_y, int start_x);int delwin(WINDOW *window_to_delete);
  1. newwin函数的作用是创建一个新窗口.他返回一个指向新窗口的指针,如果新窗口创建失败则返回null
  2. 如果想让新窗口的右下角正好落在屏幕的右下角,,可以将newwin函数的行,列参数设为0
  3. 所有窗口的范围都必须在当前屏幕范围之内,如果新窗口任何一部分落在当前屏幕范围之外,则newwin函数调用将失败
  4. delwin函数的作用是删除一个先前通过newwin函数创建的窗口.因为调用newwin函数可能会给新窗口分配内存,所以当不再需要这些窗口时,不要忘记通过delwin函数将其删除
  5. 千万不要尝试删除curses自己的窗口stdscr和curscr

通用函数

#include <curses.h>int addch(const chtype char);int waddch(WINDOW *window_pointer,           const chtype char);int mvaddch(int y, int x, const chtype char);int mvwaddch(WINDOW *window_pointer, int y,             int x, const chtype char);int printw(char *format, ...);int wprintw(WINDOW *window_pointer,            char *format, ...);int mvprintw(int y, int x, char *format, ...);int mvwprintw(WINDOW *window_pointer, int y,              int x, char *format, ...);
  1. 前缀w用于窗口,mv用于光标移动,mvw用于在窗口内移动光标
  2. 如果给函数增加了w前缀,就必须在该函数的参数表的最前面加一个WINDOW指针参数
  3. 如果给函数增加的是mv前缀,则需要在函数的参数表的最前面增加两个参数分别是纵坐标y和横坐标x,两个坐标指定了执行操作的位置
  4. 如果给函数增加mvw前缀,就需要多传递3个参数,它们分别是一个WINDOW指针,y和x坐标值

移动和更新窗口

#include <curses.h>int mvwin(WINDOW *window_to_move,          int new_y, int new_x);int wrefresh(WINDOW *window_ptr);int wclear(WINDOW *window_ptr);int werase(WINDOW *window_ptr);int touchwin(WINDOW *window_ptr);int scrollok(WINDOW *window_ptr,             bool scroll_flag);int scroll(WINDOW *window_ptr);
  1. mvwin函数的作用是在屏幕上移动一个窗口,不允许窗口的任何部分查处屏幕范围
  2. wrefresh, wclear和werases函数分别是前面介绍的refresh, clear, erase函数的通用版本.他们只是多了一个WINDOW指针参数,从而可针对特定的窗口进行操作,而不仅仅局限于stdscr
  3. touchwin函数非常特殊,她的作用是通知curses函数库其指针参数指向的窗口的内容已发生改变
  4. 两个scroll函数控制窗口的卷屏.如果传递给scrollok函数的布尔值值true(通常是非零值),则允许卷屏.默认情况下是不允许卷屏的.scroll函数的作用只是把窗口的内容上卷一行

mutiw1.c

#include <unistd.h>#include <stdlib.h>#include <curses.h>int main(){    WINDOW *new_window_ptr;    WINDOW *popup_window_ptr;    int x_loop;    int y_loop;    char a_letter = 'a';    initscr();    move(5, 5);    printw("%s", "Testing mutiple windows");    refresh();    for(y_loop = 0; y_loop < LINES - 1; y_loop++){        for(x_loop = 0; x_loop < COLS - 1; x_loop++){            mvwaddch(stdscr, y_loop, x_loop, a_letter);            a_letter++;            if(a_letter > 'z') a_letter = 'a';        }    }    /* Update the screen */    refresh();    sleep(2);    new_window_ptr = newwin(10, 25, 5, 5);    mvwprintw(new_window_ptr, 2, 2, "%s",              "Hello World");    mvwprintw(new_window_ptr, 5, 2, "%s",              "Notice how very long lines wrap inside the window");    wrefresh(new_window_ptr);    sleep(2);    a_letter = '0';    for(y_loop = 0; y_loop < LINES - 1; y_loop++){        for(x_loop = 0; x_loop < COLS - 1; x_loop++){            mvwaddch(stdscr, y_loop, x_loop, a_letter);            a_letter++;            if(a_letter > '9') a_letter = '0';        }    }    refresh();    sleep(2);    wrefresh(new_window_ptr);    sleep(2);    touchwin(new_window_ptr);    wrefresh(new_window_ptr);    sleep(2);    popup_window_ptr = newwin(10, 20, 8, 8);    box(popup_window_ptr, '|', '-');    mvwprintw(popup_window_ptr, 5, 2, "%s",              "Pop Up window!");    wrefresh(popup_window_ptr);    sleep(2);    touchwin(new_window_ptr);    wrefresh(new_window_ptr);    sleep(2);    wclear(new_window_ptr);    wrefresh(new_window_ptr);    sleep(2);    delwin(new_window_ptr);    touchwin(popup_window_ptr);    wrefresh(popup_window_ptr);    sleep(2);    delwin(popup_window_ptr);    touchwin(stdscr);    refresh();    sleep(2);    endwin();    exit(EXIT_SUCCESS);}

优化屏幕刷新

#include <curses.h>int wnoutrefresh(WINDOW *window_ptr);int doupdate(void);
  1. wnoutrefresh函数用于决定把哪些字符发送到屏幕上,但是他并不真正的发送这些字符,真正将更新发送到终端的工作由doupdate函数来完成.
  2. 如果只是调用了wnoutrefresh函数之后立即调用doupdate函数,则效果和直接调用wrefresh一样
  3. 如果想重新绘制多个窗口,你可以为每个窗口分别调用wnoutrefresh函数,然后只需要在最后一个wnoutrefresh之后调用一次doupdate函数

子窗口

#include <curses.h>WINDOW *subwin(WINDOW *parent, int num_of_lines,int num_of_cols,               int start_y, int start_x);int delwin(WINDOW *window_to_delete);
  1. subwin函数的参数几乎与newwin函数一样,子窗口的删除过程也和其他窗口一样
  2. 区别:子窗口没有自己独立的屏幕字符存储空间,他们与父窗口共享同一字符存储空间,这意味着对子窗口内容的任何修改都会反应到父窗口中

subsc1.c

#include <unistd.h>#include <stdlib.h>#include <curses.h>int main(){    WINDOW *sub_window_ptr;    int x_loop;    int y_loop;    int counter;    char a_letter = '1';    initscr();    for(y_loop = 0; y_loop < LINES - 1; y_loop++){        for(x_loop = 0; x_loop < COLS - 1; x_loop++){            mvwaddch(stdscr, y_loop, x_loop, a_letter);            a_letter++;            if(a_letter > '9') a_letter = '1';        }    }    sub_window_ptr = subwin(stdscr, 10, 20, 10, 10);    scrollok(sub_window_ptr, 1);    touchwin(stdscr);    refresh();    sleep(3);    werase(sub_window_ptr);    mvwprintw(sub_window_ptr, 2, 0, "%s",              "This window will now scroll");    wrefresh(sub_window_ptr);    sleep(1);    for(counter = 1; counter < 10; counter++){        wprintw(sub_window_ptr, "%s",                 "This text is both wraping and scrolling");        wrefresh(sub_window_ptr);        sleep(1);    }    delwin(sub_window_ptr);    touchwin(stdscr);    refresh();    sleep(2);    endwin();    exit(EXIT_FAILURE);}

keypad模式

#include <curses.h>int keypad(WINDOW *window_ptr, bool keypad_flag);
  1. keypad模式主要用于处理键盘的功能.例如方向键和功能键等
  2. 头文件curses.h通过一组以KEY_为前缀的定义来管理逻辑键
  3. 函数调用成功时返回OK.失败时,返回ERR
  4. 在该模式中curses将接管按键转义序列的处理工作,读取键盘操作不仅能够返回用户按下的键,还将返回与逻辑按键对应的KEY_定义

keypad模式的三个小小限制

  1. 识别escape转义序列的过程是与时间相关的
  2. 为了让curses能够区分”单独按下Escape键”和”一个以Escape字符开头的键盘转义序列”,它必须等待一小段时间
  3. curses不能吹二义性的转义序列

keypad.c

#include <unistd.h>#include <stdlib.h>#include <curses.h>#define LOCAL_ESCAPE_KEY 27int main(){    int key;    initscr();    crmode();    keypad(stdscr, true);    noecho();    clear();    mvprintw(5, 5, "%s", "keypad demonstration.Press 'q' to quit");    move(7, 5);    refresh();    key = getch();    while(key != ERR && key != 'q'){        move(7, 5);        clrtoeol();        if((key >= 'A' && key <= 'Z') ||           (key >= 'a' && key <= 'z')){            printw("key was %c", (char)key);        }        else {            switch(key){                case LOCAL_ESCAPE_KEY:                    printw("%s", "escape key");                    break;                case KEY_END:                    printw("%s", "END key");                    break;                case KEY_BEG:                    printw("%s", "BEGINNING key");                    break;                case KEY_RIGHT:                    printw("%s", "RIGHT key");                    break;                case KEY_LEFT:                    printw("%s", "LEFT key");                    break;                case KEY_UP:                    printw("%s", "UP key");                    break;                case KEY_DOWN:                    printw("%s", "DOWN key");                    break;                default:                    printw("%s", "Unmatched-%d", key);                    break;            }        }        refresh();        key = getch();    }    endwin();    exit(EXIT_SUCCESS);}

彩色显示

#include <curses.h>bool has_colors(void);int start_color(void);int init_pair(short pair_number, short foreground,            short background);int COLOR_PAIR(int pair_number);int pair_content(short pair_number,short *foreground,            short *background);
  1. 如果终端支持彩色功能,has_colors函数将返回true.然后你需要调用start_color函数,如果该函数成功初始化了颜色显示功能,他将返回OK.
  2. 一旦start_color函数调用成功,变量COLOR_PAIRS将被设置成最大值64,变量COLORS定义将被设置为最大值8
  3. 在把颜色作为属性使用之前,你必须先调用init_pair函数对准备使用的颜色组合进行初始化
  4. 对颜色属性的访问是通过COLOR_PAIR函数来完成
  5. pair_content作用是获取已定义的颜色组合的信息

color.c

#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <curses.h>int main(){    int i;    initscr();    if(!has_colors()){        endwin();        fprintf(stderr, "Error, no color surport on this terminal\n");        exit(1);    }    if(start_color() != OK){        endwin();        fprintf(stderr, "Error, could not initialize colors\n");        exit(2);    }    clear();    mvprintw(5, 5, "There are %d COLORS, and %d COLOR_PAIRS avaiable", COLORS, COLOR_PAIRS);    refresh();    init_pair(1, COLOR_RED, COLOR_BLACK);    init_pair(2, COLOR_RED, COLOR_GREEN);    init_pair(3, COLOR_GREEN, COLOR_RED);    init_pair(4, COLOR_YELLOW, COLOR_BLUE);    init_pair(5, COLOR_BLACK, COLOR_WHITE);    init_pair(6, COLOR_MAGENTA, COLOR_BLUE);    init_pair(7, COLOR_CYAN, COLOR_WHITE);    for(i = 1; i <= 7; i++){        attroff(A_BOLD);        attrset(COLOR_PAIR(i));        mvprintw(5 + i, 5, "Color pair %d", i);        attrset(COLOR_PAIR(i) | A_BOLD);        mvprintw(5 + i, 25, "Bold color pair %d", i);        refresh();        sleep(2);    }    endwin();    exit(EXIT_SUCCESS);}

pad

#include <curses.h>WINDOW *newpad(int num_of_lines,int num_of_columns);int prefresh(WINDOW *pad_ptr, int pad_row, int pad_column,             int screen_row_min, int screen_col_min,             int screen_row_max, int screen_col_max);
  1. newpad函数的返回值是一个指向WINDOW结构的指针,这和newwin一样,pad用delwin函数来删除
  2. pad使用不同的函数prefresh来执行刷新操作,这个函数的的作用是将pad从坐标(pad_row, pad_column)开始区域写到屏幕上指定的显示区域,该区域的范围从坐标(int screen_row_min, int screen_row_max, int screen_col_max)
  3. curses还提供了,pnoutrefresh,它的作用和函数wnoutrefresh一样

pad.c

#include <unistd.h>#include <stdlib.h>#include <curses.h>int main(){    WINDOW *pad_ptr;    int x, y;    int pad_lines;    int pad_cols;    char disp_char;    initscr();    pad_lines = LINES + 50;    pad_cols = COLS + 50;    pad_ptr = newpad(pad_lines, pad_cols);    disp_char = 'a';    for(x = 0; x < pad_lines; x++){        for(y = 0; y < pad_cols; y++){            mvwaddch(pad_ptr, x, y, disp_char);            if(disp_char == 'z') disp_char = 'a';            disp_char++;        }    }    prefresh(pad_ptr, 0, 0, 2, 2, 9, 9);    sleep(10);    prefresh(pad_ptr, LINES + 5, COLS + 7, 5, 5, 21, 19);    sleep(10);    delwin(pad_ptr);    endwin();    exit(EXIT_SUCCESS);}

main.c

#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <curses.h>#define MAX_STRING 80   /* Longest allowed response         */#define MAX_ENTRY 1024  /* Longest allowed database entry   */#define MESSAGE_LINE 6  /* Misc. messages on this line      */#define ERROR_LINE 22   /* Line to use for errors           */#define Q_LINE 20       /* Line for question                */#define PROMPT_LINE 18  /* Line for prompting on            */static char current_cd[MAX_STRING] = "\0";static char current_cat[MAX_STRING];const char *title_file = "title.cdb";const char *tracks_file = "tracks.cdb";const char *temp_file = "cdb.tmp";void clear_all_screen(void);void get_return(void);int get_confirm(void);int ger_choice(char *greet, char *choices[]);void draw_menu(char *options[],        int highlight, int start_row, int strat_col);void insert_title(char *cdtitle);void get_string(char *string);void add_record(void);void count_cds(void);void find_cd(void);void list_tracks(void);void remove_tracks(void);void remove_cd(void);void update_cd(void);char *main_menu[] = {    "add new CD",    "find CD",    "count CDs and tracks in the catalog",    "quit",    0};char *extended_menu[] = {    "add new CD",    "find CD",    "count CDs and tracks in the catalog",    "list tracks on current CD",    "remove current CD",    "update track information",    "quit",    0};int main(){    int choice;    initscr();    do {        choice = get_choice("Options:",                    current_cd[0] ? extended_menu : main_menu);        switch(choice){            case 'q':                break;            case 'a':                add_record();                break;            case 'c':                count_cds();                break;            case 'f':                find_cd();                break;            case 'l':                list_tracks();                break;            case 'r':                remove_cd();            case 'u':                update_cd();                break;        }    } while(choice != 'q');}int get_choice(char *greet, char *choices[]){    static int selected_row = 0;    int max_row = 0;    int start_screenrow = MESSAGE_LINE, start_screencol = 10;    char **option;    int selected;    int key = 0;    option = choices;    while(*option){        max_row++;        option++;    }    /* protect against menu getting shorter when CD seleted */    if(selected_row > max_row){        selected_row = 0;    }    clear_all_screen();    mvprintw(start_screenrow - 2, start_screencol, greet);    keypad(stdscr, true);    cbreak();    noecho();    key = 0;    while(key != 'q' && key != KEY_ENTER && key != '\n'){        if(key == KEY_UP){            if(selected_row == 0)              selected_row = max_row - 1;            else              selected_row--;        }        if(key == KEY_DOWN){            if(selected_row == (max_row - 1))              selected_row = 0;            else              selected_row++;        }        selected = *choices[selected_row];        draw_menu(choices, selected_row, start_screenrow, start_screencol);        key = getch();    }    keypad(stdscr, false);    nocbreak();    echo();    if(key == 'q')      selected = 'q';    return selected;}void draw_menu(char *options[], int current_highlight,               int start_row, int start_col){    int current_row = 0;    char **option_ptr;    char *txt_ptr;    option_ptr = options;    while(*option_ptr){        if(current_row == current_highlight)          attron(A_STANDOUT);        txt_ptr = options[current_row];        txt_ptr++;        mvprintw(start_row + current_row, start_col, "%s", txt_ptr);        if(current_row == current_highlight)          attroff(A_STANDOUT);        current_row++;        option_ptr++;    }    mvprintw(start_row + current_row + 3, start_col,             "Move highlight then press Return");    refresh();}void clear_all_screen(){    clear();    mvprintw(2, 20, "%s", "CD Database Application");    if(current_cd[0]){        mvprintw(ERROR_LINE, 0, "Current CD: %s: %s\n",                 current_cat, current_cd);    }    refresh();}void add_record(){    char catalog_number[MAX_STRING];    char cd_title[MAX_STRING];    char cd_type[MAX_STRING];    char cd_artist[MAX_STRING];    char cd_entry[MAX_STRING];    int screenrow = MESSAGE_LINE;    int screencol = 10;    clear_all_screen();    mvprintw(screenrow, screencol, "Enter new CD details");    screenrow += 2;    mvprintw(screenrow, screencol, "Catalog Number: ");    get_string(catalog_number);    screenrow++;    mvprintw(screenrow, screencol, "      CD title: ");    get_string(cd_title);    screenrow++;    mvprintw(screenrow, screencol, "       CD type: ");    get_string(cd_type);    screenrow++;    mvprintw(screenrow, screencol, "        Artist: ");    get_string(cd_artist);    screenrow++;    mvprintw(PROMPT_LINE - 2, 5, "About to add this new entry:");    sprintf(cd_entry, "%s,%s,%s,%s", catalog_number,            cd_title, cd_type, cd_artist);    mvprintw(PROMPT_LINE, 5, "%s", cd_entry);    refresh();    if(get_confirm()){        insert_title(cd_entry);        strcpy(current_cd, cd_title);        strcpy(current_cat, catalog_number);    }}void get_string(char *string){    int len;    wgetnstr(stdscr, string, MAX_STRING);    len = strlen(string);    if(len > 0 && string[len -1] == '\n')      string[len -1] = '\0';}int get_confirm(){    int confirmed = 0;    char first_char;    mvprintw(Q_LINE, 5, "Are you sure? ");    clrtoeol();    refresh();    cbreak();    first_char = getch();    if(first_char == 'Y' || first_char == 'y'){        confirmed = 1;    }    nocbreak();    if(!confirmed){        mvprintw(Q_LINE, 1, "   Canncelled");        clrtoeol();        refresh();        sleep(2);    }    return confirmed;}void insert_title(char *cdtitle){    FILE *fp = fopen(title_file, "a");    if(!fp){        mvprintw(ERROR_LINE, 0,                 "can not open CD titles database");    }     else {        fprintf(fp, "%s\n", cdtitle);        fclose(fp);    }}#define BOXED_LINES     11#define BOXED_ROWS      60#define BOX_LINE_POS    8#define BOX_ROW_POS     2void update_cd(){    FILE *tracks_fp;    char track_name[MAX_STRING];    int len;    int track = 1;    int screen_line = 1;    WINDOW *box_window_ptr;    WINDOW *sub_window_ptr;    clear_all_screen();    mvprintw(PROMPT_LINE, 0, "Re-entering tracks for CD. ");    if(!get_confirm())      return;    move(PROMPT_LINE, 0);    clrtoeol();    remove_tracks();    mvprintw(MESSAGE_LINE, 0, "Enter a blank line to finish");    tracks_fp = fopen(tracks_file, "a");    box_window_ptr = subwin(stdscr, BOXED_LINES + 2,          BOXED_ROWS + 2, BOX_LINE_POS - 1, BOX_ROW_POS -1);    if(!box_window_ptr)      return;    box(box_window_ptr, ACS_VLINE, ACS_HLINE);    sub_window_ptr = subwin(stdscr, BOXED_LINES, BOXED_ROWS, BOX_LINE_POS, BOX_ROW_POS);    if(!sub_window_ptr)      return;    scrollok(sub_window_ptr, true);    werase(sub_window_ptr);    touchwin(stdscr);    do{        mvwprintw(sub_window_ptr, screen_line++,                  BOX_ROW_POS + 2, "Track %d:", track);        clrtoeol();        refresh();        wgetnstr(sub_window_ptr, track_name, MAX_STRING);        len = strlen(track_name);        if(len > 0 && track_name[len - 1] == '\n')          track_name[len -1] = '\0';        if(*track_name)          fprintf(tracks_fp, "%s,%d,%s\n",                  current_cat, track, track_name);        track++;        if(screen_line > BOXED_LINES - 1){            scroll(sub_window_ptr);            screen_line--;        }    } while(*track_name);    delwin(sub_window_ptr);    fclose(tracks_fp);}void remove_cd(){    FILE *titles_fp, *temp_fp;    char entry[MAX_STRING];    int cat_length;    if(current_cd[0] == '\0')      return;    clear_all_screen();    mvprintw(PROMPT_LINE, 0, "About to remove CD %s: %s. ",             current_cat, current_cd);    if(!get_confirm())      return;    cat_length = strlen(current_cat);    /* copy the titles file to a temporary, ignoring this CD */    titles_fp = fopen(title_file, "r");    temp_fp= fopen(temp_file, "w");    while(fgets(entry, MAX_STRING, titles_fp)){        /*compare catalog number and copy entry if no match */        if(strncmp(current_cat, entry, cat_length) != 0)          fputs(entry, temp_fp);    }    fclose(titles_fp);    fclose(temp_fp);    /* delete the titles file, and rename the temporary file */    unlink(title_file);    rename(temp_file, title_file);    /* Now do the same for the tracks file */    remove_tracks();    /* Reset the CD to "None" */    current_cd[0] = '\0';}void remove_tracks(){    FILE *tracks_fp, *temp_fp;    char entry[MAX_STRING];    int cat_length;    if(current_cd[0] == '0')      return;    cat_length = strlen(current_cat);    tracks_fp = fopen(tracks_file, "r");    if(tracks_fp == (FILE *)NULL) return;    temp_fp = fopen(temp_file, "w");    while(fgets(entry, MAX_STRING, tracks_fp)){        /* compare catalog number and copy entry if no match */        if(strncmp(current_cat, entry, cat_length) != 0)          fputs(entry, temp_fp);    }    fclose(tracks_fp);    fclose(temp_fp);    /* delete the tracks file, and rename the temporary file */    unlink(tracks_file);    rename(temp_file, tracks_file);}void count_cds(){    FILE *titles_fp, *tracks_fp;    char entry[MAX_STRING];    int titles = 0;    int tracks = 0;    titles_fp = fopen(title_file, "r");    if(titles_fp){        while(fgets(entry, MAX_STRING, titles_fp))          titles++;        fclose(titles_fp);    }    tracks_fp = fopen(tracks_file, "r");    if(tracks_fp){        while(fgets(entry, MAX_STRING, tracks_fp))          tracks++;        fclose(tracks_fp);    }    mvprintw(ERROR_LINE, 0, "Database contains %d titles,             with a total of %d tracks.", titles, tracks);    get_return();}void find_cd(){    char match[MAX_STRING], entry[MAX_STRING];    FILE *titles_fp;    int count = 0;    char *found, *title, *catalog;    mvprintw(Q_LINE, 0, "Enter a string to search for in CD titles: ");    get_string(match);    titles_fp = fopen(title_file, "r");    if(titles_fp){        while(fgets(entry, MAX_STRING, titles_fp)){            /* Skip past catalog number */            catalog = entry;            if(found == strstr(catalog, ",")){                *found == '\0';                title = found + 1;                /* Zap the next comma in the entry to reduce it to title only */                if(found == strstr(title, ",")){                    *found = '\0';                    /* Now see if the match substring is present */                    if(found == strstr(title, match)){                        count++;                        strcpy(current_cd, title);                        strcpy(current_cat, catalog);                    }                }            }        }        fclose(titles_fp);    }    if(count != 1){        if(count == 0){            mvprintw(ERROR_LINE, 0, "Sorry, no matching CD found. ");        }        if(count > 1){            mvprintw(ERROR_LINE, 0, "Sorry, match is ambiguous: %d CDs found. ", count);        }        current_cd[0] = '\0';        get_return();    }}void list_tracks(){    FILE *tracks_fp;    char entry[MAX_STRING];    int cat_length;    int lines_op = 0;    WINDOW *track_pad_ptr;    int tracks = 0;    int key;    int first_line = 0;    if(current_cd[0] == '\0'){        mvprintw(ERROR_LINE, 0, "You must select a CD first. ");        get_return();        return;    }    clear_all_screen();    cat_length = strlen(current_cat);    /* First count the number of tracks for the current CD */    tracks_fp = fopen(tracks_file, "r");    if(!tracks_fp)      return;    while(fgets(entry, MAX_STRING, tracks_fp)){        if(strncmp(current_cat, entry, cat_length) == 0);        tracks++;    }    fclose(tracks_fp);    /* Make a new pad, ensure that even if there is only a single track the PAD is        large enough so the later prefresh() is always valid. */    track_pad_ptr = newpad(tracks + 1 + BOXED_LINES, BOXED_ROWS + 1);    if(!track_pad_ptr)      return;    tracks_fp = fopen(tracks_file, "r");    if(!tracks_fp)      return;    mvprintw(4, 0, "CD track listing\n");    /* write the track information into the pad */    while(fgets(entry, MAX_STRING, tracks_fp)){        /* Compare catalog number and output rest of entry */        if(strncmp(current_cat, entry, cat_length) == 0){            mvwprintw(track_pad_ptr, lines_op++, 0, "%s",                       entry + cat_length + 1);        }    }    fclose(tracks_fp);    if(lines_op > BOXED_LINES){        mvprintw(MESSAGE_LINE, 0, "Cursor keys to scroll,                 RETURN or q to exit");    }    else{        mvprintw(MESSAGE_LINE, 0, "RETURN or q to exit");    }    wrefresh(stdscr);    keypad(stdscr, true);    cbreak();    noecho();    key = 0;    while(key != 'q' && key != KEY_ENTER && key != '\n'){        if(key == KEY_UP){            if(first_line > 0)              first_line--;        }        if(key == KEY_DOWN){            if(first_line + BOXED_LINES + 1 < tracks)              first_line++;        }        /* now draw the approptiate part of the pad on the screen */        prefresh(track_pad_ptr, first_line, 0,                 BOX_LINE_POS, BOX_ROW_POS,                 BOX_LINE_POS + BOXED_LINES,                 BOX_ROW_POS + BOXED_ROWS);        key = getch();    }    delwin(track_pad_ptr);    keypad(stdscr, false);    nocbreak();    echo();}void get_return(){    int ch;    mvprintw(23, 0, "%s", " Pass return ");    refresh();    while((ch = getchar()) != '\n' && ch != EOF);}
0 0