C语言 ncurses new_item() failure



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

#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] = '';
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] != ''; i++) {
if(slashCount == 3) {
title[index++] = pathName[i];
}
else if(pathName[i] == '/') {
slashCount++;
}
}
title[index] = '';
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: %dn", 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()函数中为它们在堆上分配内存。

最新更新