ncurses new_item() 失败

ncurses new_item() failure

我对 ncurses 很陌生,我一直在尝试按照文档 here 来实现滚动菜单。我的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ncurses.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <menu.h>

int NUMBER_OF_TRACKS = 0;

struct Track {
    char *title;
    char *pathName;
}; typedef struct Track Track;

/*
 * playSong is responsible for playing the selected track.
 * This function utilizes fork and execvp to accomplish this.
 * could maybe use popen again?
 */
void playSong(char *title) {
    FILE *fp;
    char cmdBuffer[1024];
    sprintf(cmdBuffer, "cd ~/Music/iTunes/\"iTunes Media\"/Music/ && afplay %s", title);

    fp = popen(cmdBuffer, "r");
    if(fp == NULL) {
        printf("Unable to play track.\n");
        exit(1);
    }

    // close the pipe
    if(pclose(fp) == -1) {
        printf("Error while closing pipe.\n");
        exit(1);
    }
}

/*
 * escapeString takes in a path name for a track, and adds escape characters
 * to it so that it later be played by the system.
 */
char * escapeString(char *pathName) {
    char * escape = (char *)malloc(1024 * sizeof(char));
    int index = 0;

    for(int i = 0; pathName[i] != '\n'; i++) {
        if(pathName[i] == '/') {
            if(i == 1) {
                // we don't need to doubly pad the first '/' with quotes
                escape[index++] = pathName[i];
                escape[index++] = '\"';
            }
            else {
                escape[index++] = '\"';
                escape[index++] = pathName[i];
                escape[index++] = '\"';
            }
        }
        else {
            escape[index++] = pathName[i];
        }
    }
    // don't forget to add last quote to end the song
    escape[index++] = '\"';
    // don't forget null!
    escape[index] = '[=11=]';

    return escape;
}

/*
 * getTrackTitle parses the escaped pathame and returns only the name of the
 * song.
 */
char * getTrackTitle(char *pathName) {
    char *title = (char *)malloc(1024*sizeof(char));
    int slashCount = 0;
    int index = 0;

    for(int i = 0; pathName[i] != '[=11=]'; i++) {
        if(slashCount == 3) {
            title[index++] = pathName[i];
        }
        else if(pathName[i] == '/') {
            slashCount++;
        }
    }

    title[index] = '[=11=]';
    return title;
}

Track ** getUserTracks() {
    FILE *fp;
    char pathName[300]; // may need to be longer?

    // use fp with popen
    char *cmd = "cd ~/Music/iTunes/\"iTunes Media\"/Music/ && find . -name \"*.m4a\"";
    fp = popen(cmd, "r");
    if(fp == NULL) {
        printf("Unable to find your tracks!\n");
        exit(1);
    }

    // create our list
    Track **trackList = (Track **)malloc(1024 * sizeof(Track *));

    // read the output (i.e) the tracks
    while(fgets(pathName, sizeof(pathName), fp) != NULL) {
        // allocate memory for track
        trackList[NUMBER_OF_TRACKS] = (Track *)malloc(sizeof(Track));
        // get the escaped path name
        char *escapedPath = escapeString(pathName);
        // store values in struct
        trackList[NUMBER_OF_TRACKS]->pathName = escapedPath;
        trackList[NUMBER_OF_TRACKS]->title = getTrackTitle(escapedPath);

        // update number of tracks
        NUMBER_OF_TRACKS++;
    }

    // null terminate list
    trackList[NUMBER_OF_TRACKS] = (Track *)NULL;

    // close the pipe
    if(pclose(fp) == -1) {
        printf("Error while closing pipe.\n");
        exit(1);
    }

    // return our list
    return trackList;
}

int main(int argc, char **argv) {

    /* START NCURSES */
    initscr(); // create screen
    curs_set(0); // hide cursor
    noecho(); // don't show the characters the user is typing
    cbreak(); // allow user to quit with ctrl-c; may want to remove this later

    // get screen size
    int yMax, xMax;
    getmaxyx(stdscr, yMax, xMax);

    ITEM **userTracks;
    MENU *trackMenu;
    WINDOW *trackMenuWindow;

    // create items for our menu
    Track **choices = getUserTracks();
    userTracks = (ITEM **)calloc(NUMBER_OF_TRACKS+1, sizeof(ITEM *));
    for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
        ITEM *trackItem = new_item(choices[i]->title, "");
        userTracks[i] = trackItem;
    }
    userTracks[NUMBER_OF_TRACKS] = (ITEM *)NULL;

    /* MENU STUFF */

    // create the menu
    trackMenu = new_menu((ITEM **)userTracks);
    // printf("Item count: %d\n", item_count(trackMenu));

    /* create the window associated with the menu */
    trackMenuWindow = newwin(yMax, xMax, 0, 0);
    box(trackMenuWindow, 0, 0); // adds border to window
    keypad(trackMenuWindow, TRUE); // enables keypd use on menu

    /* set main window and set sub windows */
    set_menu_win(trackMenu, trackMenuWindow);
    set_menu_sub(trackMenu, derwin(trackMenuWindow, yMax - 1, xMax - 1, 1, 1));
    set_menu_format(trackMenu, 5, 1); // sets number of items to be displayed

    /* Set menu mark to the string " * " */
    set_menu_mark(trackMenu, NULL);

    /* Post the menu */
    post_menu(trackMenu);
    wrefresh(trackMenuWindow);

    int keyPressed;
    while((keyPressed = wgetch(trackMenuWindow)) != 'q') {
        switch(keyPressed) {
            case KEY_DOWN:
                menu_driver(trackMenu, REQ_DOWN_ITEM);
                break;
            case KEY_UP:
                menu_driver(trackMenu, REQ_UP_ITEM);
                break;
            case KEY_NPAGE:
                menu_driver(trackMenu, REQ_SCR_DPAGE);
                break;
            case KEY_PPAGE:
                menu_driver(trackMenu, REQ_SCR_UPAGE);
                break;
        }
        wrefresh(trackMenuWindow);
    }

    unpost_menu(trackMenu);
    free_menu(trackMenu);
    for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
        free_item(userTracks[i]);
    }
    endwin();
    /* END NCURSES */

    return 0;
}

基本上,我有兴趣在他们的机器上获取所有用户 (iTunes) 歌曲的列表,并将它们显示在(可滚动的)菜单中。

能够检索所有歌曲并将它们存储在我的Track **choices数组中,但是,我不能能够从中创建任何菜单项。我推断我的问题出在以下片段中:

    // create items for our menu
    Track **choices = getUserTracks();
    userTracks = (ITEM **)calloc(NUMBER_OF_TRACKS+1, sizeof(ITEM *));
    for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
        ITEM *trackItem = new_item(choices[i]->title, "");
        userTracks[i] = trackItem;
    }
    userTracks[NUMBER_OF_TRACKS] = (ITEM *)NULL;

特别是行 ITEM *trackItem = new_item(choices[i]->title, "");

似乎 new_item() 在每次迭代中都返回 NULL。此外,errno 被设置为 E_BAD_ARGUMENT 暗示 choices[i]->title 不是有效字符串,但我无法想象为什么会这样。

如有任何帮助,我们将不胜感激!

您可以将上面的代码编译如下:gcc -I/usr/local/opt/ncurses/include -L/usr/local/opt/ncurses/lib <source_file>.c -lmenu -lncurses 您也许可以排除 -I/usr/local/opt/ncurses/include -L/usr/local/opt/ncurses/lib 但不幸的是这对我不起作用。

公平警告,由于文件结构和 iTunes 要求,此程序可能无法在 Windows/Linux 环境中运行,但我尚未对其进行测试。

原来我的结构中的字符串是无意义的。我需要在 getUserTracks() 函数中为它们在堆上分配内存。