cccd-0.3beta4.orig/0040700000175000017500000000000007416301460012576 5ustar uh1763uh1763cccd-0.3beta4.orig/cccd.c0100400000175000017500000007144507416154076013657 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: cccd.c,v 0.3.3.6 2002/01/06 23:05:18 jst Exp $ */ #include #include #include #include #include #include #include #include #include #include "general.h" #include "misc.h" #include "cd.h" #include "options.h" #include "cddb.h" #include "info.h" #include "cccd.h" #include "icons.h" /* event handlers */ static gint configure_event(GtkWidget * widget, GdkEventConfigure * event); static gint expose_event(GtkWidget * widget, GdkEventExpose * event); /* global vars */ static struct control cntl; static struct display disp; static struct options opt; static int startup_playing = 0; static int startup = 1; /* used only at startup, to add a timeout function if the cddrive is already playing */ /* entry point: main */ int main(int argc, char **argv) { struct sigaction sa; sa.sa_handler = SIG_IGN; /* just ignore the damn thing */ sa.sa_flags = SA_NOCLDSTOP; /* need know only when child is done */ /* necessary for compiling with kernel 2.1.1xx, where sa_mask is an array [Petteri] */ memset(&sa.sa_mask, 0, sizeof(sa.sa_mask)); sigaction(SIGCHLD, &sa, 0); /* activate */ options_clear(&opt); options_load(&opt); strcpy(cntl.devicename, "/dev/cdrom"); cntl.drivestatus = NO_STATUS; cntl.playmode = NORMAL; cntl.looking = 0; cntl.tray_open = 0; cd_clear(&cntl.cd); gtk_init(&argc, &argv); init_gui(); gtk_main(); return 0; } void init_gui(void) { GtkWidget *window, *vbox, *hbox, *tophbox, *button, *pixmap; GdkPixmap *dpixmap; GdkBitmap *dmask; GtkTooltips *tooltip; GdkColor tooltipcolor; GdkColormap *colormap; /* start with the control panel window */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_realize(window); /* allocate a yellowish color for the tooltips */ colormap = gdk_window_get_colormap(window->window); tooltip = gtk_tooltips_new(); tooltipcolor.red = 61669; tooltipcolor.green = 59113; tooltipcolor.blue = 35979; gdk_color_alloc(colormap, &tooltipcolor); gtk_tooltips_set_colors(tooltip, &tooltipcolor, &window->style->fg[GTK_STATE_NORMAL]); gtk_window_set_title(GTK_WINDOW(window), "cccd Panel"); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(destroy_callback), NULL); gtk_widget_set_usize(window, 5 * GUI_BUTTON_X, 2 * GUI_BUTTON_Y); gtk_widget_show(window); /* we do this here, to be able to show pixmaps */ tophbox = gtk_hbox_new(FALSE, 0); /* this hbox contains everything */ gtk_container_add(GTK_CONTAINER(window), tophbox); vbox = gtk_vbox_new(FALSE, 0); /* this vbox will only contain quit */ gtk_container_add(GTK_CONTAINER(tophbox), vbox); /* this is the quit button */ button = gtk_button_new(); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window)); dpixmap = gdk_pixmap_create_from_xpm_d(window->window, &dmask, &window->style->bg[GTK_STATE_NORMAL], exit_icon); pixmap = gtk_pixmap_new(dpixmap, dmask); gtk_widget_show(pixmap); gtk_container_add(GTK_CONTAINER(button), pixmap); gtk_widget_set_usize(button, GUI_BUTTON_X, 2 * GUI_BUTTON_Y); gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0); gtk_tooltips_set_tip(tooltip, button, "Quit", NULL); gtk_widget_show(button); gtk_widget_show(vbox); vbox = gtk_vbox_new(FALSE, 0); /* this vbox contains two hboxes with buttons */ gtk_container_add(GTK_CONTAINER(tophbox), vbox); hbox = gtk_hbox_new(FALSE, 0); /* top button row */ gtk_container_add(GTK_CONTAINER(vbox), hbox); /* this is the prev track button */ button = gtk_button_new(); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(prev_callback), NULL); dpixmap = gdk_pixmap_create_from_xpm_d(window->window, &dmask, &window->style->bg[GTK_STATE_NORMAL], prev_icon); pixmap = gtk_pixmap_new(dpixmap, dmask); gtk_widget_show(pixmap); gtk_container_add(GTK_CONTAINER(button), pixmap); gtk_widget_set_usize(button, GUI_BUTTON_X, GUI_BUTTON_Y); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_tooltips_set_tip(tooltip, button, "Previous", NULL); gtk_widget_show(button); /* this is the play button */ button = gtk_button_new(); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(play_callback), NULL); dpixmap = gdk_pixmap_create_from_xpm_d(window->window, &dmask, &window->style->bg[GTK_STATE_NORMAL], play_icon); pixmap = gtk_pixmap_new(dpixmap, dmask); gtk_widget_show(pixmap); gtk_container_add(GTK_CONTAINER(button), pixmap); gtk_widget_set_usize(button, GUI_BUTTON_X, GUI_BUTTON_Y); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_tooltips_set_tip(tooltip, button, "Play/Replay", NULL); gtk_widget_show(button); /* this is the next track button */ button = gtk_button_new(); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(next_callback), NULL); dpixmap = gdk_pixmap_create_from_xpm_d(window->window, &dmask, &window->style->bg[GTK_STATE_NORMAL], next_icon); pixmap = gtk_pixmap_new(dpixmap, dmask); gtk_widget_show(pixmap); gtk_container_add(GTK_CONTAINER(button), pixmap); gtk_widget_set_usize(button, GUI_BUTTON_X, GUI_BUTTON_Y); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_tooltips_set_tip(tooltip, button, "Next", NULL); gtk_widget_show(button); gtk_widget_show(hbox); hbox = gtk_hbox_new(FALSE, 0); /* bottom button row */ gtk_container_add(GTK_CONTAINER(vbox), hbox); /* this is the pause button */ button = gtk_button_new(); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(pause_callback), NULL); dpixmap = gdk_pixmap_create_from_xpm_d(window->window, &dmask, &window->style->bg[GTK_STATE_NORMAL], pause_icon); pixmap = gtk_pixmap_new(dpixmap, dmask); gtk_widget_show(pixmap); gtk_container_add(GTK_CONTAINER(button), pixmap); gtk_widget_set_usize(button, GUI_BUTTON_X, GUI_BUTTON_Y); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_tooltips_set_tip(tooltip, button, "Pause", NULL); gtk_widget_show(button); /* this is the stop button */ button = gtk_button_new(); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(stop_callback), NULL); dpixmap = gdk_pixmap_create_from_xpm_d(window->window, &dmask, &window->style->bg[GTK_STATE_NORMAL], stop_icon); pixmap = gtk_pixmap_new(dpixmap, dmask); gtk_widget_show(pixmap); gtk_container_add(GTK_CONTAINER(button), pixmap); gtk_widget_set_usize(button, GUI_BUTTON_X, GUI_BUTTON_Y); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_tooltips_set_tip(tooltip, button, "Stop", NULL); gtk_widget_show(button); /* this is the eject button */ button = gtk_button_new(); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(eject_callback), NULL); dpixmap = gdk_pixmap_create_from_xpm_d(window->window, &dmask, &window->style->bg[GTK_STATE_NORMAL], eject_icon); pixmap = gtk_pixmap_new(dpixmap, dmask); gtk_widget_show(pixmap); gtk_container_add(GTK_CONTAINER(button), pixmap); gtk_widget_set_usize(button, GUI_BUTTON_X, GUI_BUTTON_Y); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_tooltips_set_tip(tooltip, button, "Eject", NULL); gtk_widget_show(button); gtk_widget_show(hbox); gtk_widget_show(vbox); vbox = gtk_vbox_new(FALSE, 0); /* another vbox for the info button */ gtk_container_add(GTK_CONTAINER(tophbox), vbox); /* this is the info button */ button = gtk_button_new(); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(info_callback), NULL); dpixmap = gdk_pixmap_create_from_xpm_d(window->window, &dmask, &window->style->bg[GTK_STATE_NORMAL], info_icon); pixmap = gtk_pixmap_new(dpixmap, dmask); gtk_widget_show(pixmap); gtk_container_add(GTK_CONTAINER(button), pixmap); gtk_widget_set_usize(button, GUI_BUTTON_X, 2 * GUI_BUTTON_Y); gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0); gtk_tooltips_set_tip(tooltip, button, "CD info/CDDB settings", NULL); gtk_widget_show(button); gtk_widget_show(vbox); gtk_widget_show(tophbox); /* gtk_widget_set_usize(window, 32+3*24, 32 ); redundant, see line 81 */ /* init the displaying window */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "cccd Display"); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(destroy_callback), NULL); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(window), vbox); /* area where all text appears */ disp.drawing_area = gtk_drawing_area_new(); gtk_drawing_area_size(GTK_DRAWING_AREA(disp.drawing_area), 120, 60); gtk_widget_set_events(disp.drawing_area, gtk_widget_get_events(disp.drawing_area) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK); gtk_signal_connect(GTK_OBJECT(disp.drawing_area), "expose_event", (GtkSignalFunc) expose_event, NULL); gtk_signal_connect(GTK_OBJECT(disp.drawing_area), "configure_event", (GtkSignalFunc) configure_event, NULL); gtk_signal_connect(GTK_OBJECT(disp.drawing_area), "map_event", (GtkSignalFunc) init_display, NULL); gtk_signal_connect(GTK_OBJECT(disp.drawing_area), "button_press_event", (GtkSignalFunc) switch_timemode, NULL); gtk_box_pack_start(GTK_BOX(vbox), disp.drawing_area, TRUE, TRUE, 0); gtk_widget_show(disp.drawing_area); /* create the progress bar */ disp.progress_bar = gtk_progress_bar_new(); gtk_widget_set_events(disp.progress_bar, gtk_widget_get_events(disp.progress_bar) | GDK_BUTTON_PRESS_MASK); gtk_signal_connect(GTK_OBJECT(disp.progress_bar), "button_press_event", (GtkSignalFunc) switch_playmode, NULL); gtk_progress_bar_update(GTK_PROGRESS_BAR(disp.progress_bar), 0.5); gtk_box_pack_start(GTK_BOX(vbox), disp.progress_bar, TRUE, TRUE, 0); gtk_widget_show(disp.progress_bar); gtk_widget_show(vbox); gtk_widget_show(window); disp.pixmap = NULL; disp.gc = NULL; } int init_cd(int handle) { int status, stop_player = handle, i; pid_t pid; char *data = NULL; FILE *stream; if (handle < 0) { handle = cd_open_device(cntl.devicename); cntl.cd.id = NULL; } if (cntl.cd.id != NULL) free(cntl.cd.id); status = cd_init(handle, &cntl.cd); if (status < 0) { /* for example, no disc in drive on startup */ cntl.drivestatus = ERROR; update_status_string(); return status; } /* if the calling function passed a valid handle, the cd tray was ejected. to make sure that the drive wont start up playing, make it stop */ if (stop_player > -1) cd_audio_stop(cntl.cd.handle); status = cd_status(cntl.cd.handle); switch (status) { case CDROM_AUDIO_PLAY: cntl.drivestatus = PLAYING; break; case CDROM_AUDIO_PAUSED: cntl.drivestatus = PAUSED; break; case CDROM_AUDIO_COMPLETED: cntl.drivestatus = STOPPED; break; case CDROM_AUDIO_NO_STATUS: cntl.drivestatus = NO_STATUS; break; case CDROM_AUDIO_INVALID: case CDROM_AUDIO_ERROR: cntl.drivestatus = ERROR; break; } update_status_string(); if (status == CDROM_AUDIO_PLAY || status == CDROM_AUDIO_PAUSED) startup_playing = 1; /* we don't compute again, have already stored disk id */ /* FIXME: what does this mean? */ cntl.cd.current_track = cd_audio_current_track(cntl.cd.handle); /* spawn a child process for looking up the cd data */ cntl.looking = 1; if (disp.timeout == 0) /* set up everything to catch the data */ disp.timeout = gtk_timeout_add(500, update_display, NULL); pipe(cntl.pipeline); pid = fork(); if (pid == 0) { /* we are the child. lookup the data */ #if 0 printf("cccd lookup: %d\n", (int) getpid()); kill(getpid(), SIGSTOP); #endif i = cddb_lookup(&cntl.cd, &data, opt.lookup, opt.server, opt.port, opt.protocol, opt.path); stream = fdopen(cntl.pipeline[1], "w"); if (data != NULL) fprintf(stream, "%s", data); else fprintf(stream, "\r\n.\r\n"); fprintf(stream, "%d", i); /* pass status on to caller */ fputc(EOF, stream); fflush(stream); fclose(stream); free(data); _exit(0); } else /* parent and error */ return status; } int init_display(GtkWidget * widget, GdkEvent * event, gpointer callback_data) { #ifdef DEBUG_EV printf("init_display / map\n"); #endif /* TODO: implement a decent GC */ disp.time_string = (char *) malloc(18); disp.time_font = gdk_font_load("-Adobe-Helvetica-Medium-R-Normal--*-100-*-*-*-*-*-*"); disp.title_font = disp.status_font = disp.time_font; disp.gc = disp.drawing_area->style->bg_gc[GTK_STATE_NORMAL]; disp.mode_string = (char *) malloc(7); disp.status_string = (char *) malloc(STATUS_STRING_MAXLEN); strcpy(disp.mode_string, "Normal"); update_status_string(); disp.timeout = gtk_timeout_add(500, update_display, NULL); disp.timemode = REL_DONE; /* Need explicit call, since map_event does not seem to cause a configure_event */ configure_event(widget, NULL); return 0; } void destroy_callback(GtkWidget * widget, gpointer data) { if (cntl.cd.handle > 0) close(cntl.cd.handle); gtk_main_quit(); /* and we are done */ } void prev_callback(GtkWidget * widget, gpointer data) { int track = cntl.cd.current_track; do { track--; if (track < cntl.cd.first_track) track = cntl.cd.last_track; } while (track != cntl.cd.current_track && (!cntl.cd.toc[track].play || !cntl.cd.toc[track].audio)); cntl.cd.current_track = track; play_callback(NULL, NULL); #ifdef DEBUG printf("call from prev\n"); #endif } void play_callback(GtkWidget * widget, gpointer data) { #ifdef DEBUG printf("Enter play trk %d\n", cntl.cd.current_track); #endif /* first we need to check if tray is open */ if (cntl.tray_open) eject_callback(widget, NULL); /* then we need to check if cd is initialized */ if (cntl.cd.id == NULL) { cntl.drivestatus = ERROR; update_status_string(); g_warning("No CD in drive?\n"); return; } if (cntl.cd.toc[cntl.cd.current_track].audio && cntl.cd.toc[cntl.cd.current_track].play) { /* check to see if we can play the current track */ cntl.drivestatus = PLAYING; update_status_string(); if (cd_audio_play(cntl.cd.handle, cntl.cd.current_track) < 0) { g_warning("Error playing track %d!\n", cntl.cd.current_track); cntl.drivestatus = ERROR; update_status_string(); } else if (disp.timeout == 0) disp.timeout = gtk_timeout_add(500, update_display, NULL); } else /* otherwise play the next playable track */ next_callback(NULL, NULL); #ifdef DEBUG printf("Exit play trk %d\n", cntl.cd.current_track); #endif } void next_callback(GtkWidget * widget, gpointer data) { int track = cntl.cd.current_track; /* first we need to check if cd is initialized */ if (cntl.tray_open) eject_callback(widget, NULL); do { track++; if (track > cntl.cd.last_track) track = cntl.cd.first_track; } while (track != cntl.cd.current_track && (!cntl.cd.toc[track].play || !cntl.cd.toc[track].audio)); cntl.cd.current_track = track; #ifdef DEBUG printf("call from next\n"); #endif play_callback(NULL, NULL); } void pause_callback(GtkWidget * widget, gpointer data) { /* first we need to check if cd is initialized */ if (cntl.tray_open) return; if (cd_status(cntl.cd.handle) == CDROM_AUDIO_PAUSED) { cd_audio_resume(cntl.cd.handle); cntl.drivestatus = PLAYING; update_status_string(); } else { cd_audio_pause(cntl.cd.handle); cntl.drivestatus = PAUSED; update_status_string(); } } void stop_callback(GtkWidget * widget, gpointer data) { #ifdef DEBUG printf("Enter stop\n"); #endif /* first we need to check if cd is initialized */ if (cntl.tray_open) return; cntl.drivestatus = STOPPED; update_status_string(); if (cd_audio_stop(cntl.cd.handle) < 0) { g_warning("Error stopping the CD. This should get interesting...\n"); cntl.drivestatus = ERROR; update_status_string(); } /* here we assume that there is no cd with audio track 0 and misuse current_track = 0 for stop indicator */ cntl.cd.current_track = 0; #ifdef DEBUG printf("Exit stop\n"); #endif } void eject_callback(GtkWidget * widget, gpointer data) { if (!cntl.tray_open) { cd_audio_stop(cntl.cd.handle); cd_eject(cntl.cd.handle); cd_deinit(&cntl.cd); cntl.drivestatus = EJECTED; update_status_string(); } else { cd_close_tray(cntl.cd.handle); if (init_cd(cntl.cd.handle) < 0) { g_warning("Error initializing CD!\n"); cntl.drivestatus = ERROR; update_status_string(); } cntl.cd.current_track = cntl.cd.first_track; } #ifdef DEBUG_EJ printf("eject, status: %x\n", cd_status(cntl.cd.handle)); #endif update_display(NULL); cntl.tray_open ^= 1; } void info_callback(GtkWidget * widget, gpointer data) { info_dialog_init(&cntl.cd, &opt); } gint update_display(gpointer gdata) { /* UPD */ int cdtime, next_track, lookup_status, status; int old_track, track_changed = 0; char *status_string, *data = NULL, *buffer = NULL, *track_string = NULL; GdkRectangle update = {0, 0, disp.drawing_area->allocation.width, disp.drawing_area->allocation.height}; FILE *stream; struct timeval timeout; fd_set set; if (startup == 1) { status = init_cd(-1); if (status < 0) { g_warning("Error initializing the CD\n"); cntl.drivestatus = ERROR; update_status_string(); } startup = 0; } old_track = cntl.cd.current_track; /* Set the strings we want to display */ status = cd_read_subchannel(&cntl.cd); /* this also updates cntl.cd.track_time */ /* if we did not eject the CD recently, and an error occured * while reading the subchannel, set state to ERROR */ if ((cntl.drivestatus != EJECTED) && (status < 0)) { cntl.drivestatus = ERROR; update_status_string(); disp.timeout = 0; return FALSE; } /* all knowledge of track changes is consolidated into track_changed a track change has occured, when a) the current track returned from subchannel != track in display b) the time remaining of current track is 0 c) the CDROM says, audio playing is complete d) the CD is supposed to be playing, i.e. not ejected, stopped, etc */ track_changed = (old_track != cntl.cd.current_track) && (cntl.cd.current_track != 0); /* a) */ #ifdef DEBUG if (track_changed) printf("track changed!\n"); #endif /* handle playmodes and next tracks to play if we cross track boundary */ track_changed |= (cntl.cd.toc[cntl.cd.current_track].length - cntl.cd.track_time == 0); /* b) */ track_changed |= (status == CDROM_AUDIO_COMPLETED); /* c) */ track_changed &= (cntl.drivestatus == PLAYING); /* d) */ #ifdef DEBUG_TO cdtime = cntl.cd.toc[cntl.cd.current_track].length - cntl.cd.track_time; printf("cdtime: %d, status: %x, track: %d\n", cdtime, status, cntl.cd.current_track); #endif /* in this place it is necessary to detect if a track change takes place we have options: 1) status changes from CDROM_AUDIO_PLAY to CDROM_AUDIO_NO_STATUS we would need to keep track of the current and last state. if we only check for status == CDROM_AUDIO_NO_STATUS, stop button does not work and autoplay is always on bad idea, tracks are skipped and stop doesn't work either 2) cdtime == 0 we feel it is unreliable. there is some difference between what is on the display, what read_subchannel returns and what play used as start/end time 3) track returned by read_subchannel != track in display we remember what printed in the display now we have CD end not recognized any more 4) since we hopefully have sorted out the CDROM audio status changes, it should be as easy as checking for CDROM_AUDIO_COMPLETED turns out it is not that easy, but a combo of all that works */ if (track_changed) { #ifdef DEBUG printf("cdtime == 0, track_changed: %s, status: %x\n", track_changed ? "TRUE" : "FALSE", status); #endif cntl.cd.current_track = old_track; /* just in case, so we don't accidentally skip tracks */ /* now handle the playmodes */ switch (cntl.playmode) { case RANDOM: /* pseudo-random mode */ srandom(time(NULL)); do { next_track = random() % (cntl.cd.last_track - cntl.cd.first_track); } while (!cntl.cd.toc[next_track + cntl.cd.first_track].play || !cntl.cd.toc[next_track + cntl.cd.first_track].audio); cntl.cd.current_track = next_track + cntl.cd.first_track; play_callback(NULL, NULL); #ifdef DEBUG printf("call from random\n"); #endif break; case LOOP_TRACK: /* track repeat mode */ play_callback(NULL, NULL); #ifdef DEBUG printf("call from loop\n"); #endif break; case LOOP_CD: /* CD repeat mode */ if ((cntl.cd.current_track != cntl.cd.last_track) && (cntl.cd.current_track != 0) && (cntl.cd.toc[cntl.cd.current_track + 1].audio)) next_callback(NULL, NULL); /* then we have not reached end of disk */ else { #ifdef DEBUG printf("call from cd loop, disk end reached\n"); #endif cntl.cd.current_track = cntl.cd.first_track; play_callback(NULL, NULL); } break; default: /* normal play mode. play next track unless CD ended */ if ((cntl.cd.current_track != cntl.cd.last_track) && (cntl.cd.current_track != 0) && (cntl.cd.toc[cntl.cd.current_track + 1].audio)) next_callback(NULL, NULL); /* then we have not reached end of disk */ else { stop_callback(NULL, NULL); cntl.cd.current_track = 0 /* cntl.cd.first_track */ ; } break; } } status_string = (char *) malloc(strlen(disp.status_string) + strlen(disp.mode_string) + 7 + 2); sprintf(status_string, "%s %s", disp.status_string, disp.mode_string); if (cntl.looking == 1) strcat(status_string, " Query"); switch (disp.timemode) { case ABS_DONE: cdtime = cntl.cd.cd_time - (cntl.cd.toc[cntl.cd.first_track].offset / 75); break; case ABS_REMAIN: cdtime = cntl.cd.length - cntl.cd.cd_time; break; case REL_DONE: cdtime = cntl.cd.track_time; break; case REL_REMAIN: cdtime = cntl.cd.toc[cntl.cd.current_track].length - (cntl.cd.track_time /* - we don't need, its all relative cntl.cd.toc[cntl.cd.first_track].offset / 75 */ ); break; default: g_error("Timemode error!\n Please submit a bug report!\n"); exit(-1); } sprintf(disp.time_string, "%02d:%02d", cdtime / 60, cdtime % 60); switch (disp.timemode) { case ABS_DONE: strcat(disp.time_string, " CD"); break; case ABS_REMAIN: strcat(disp.time_string, " CD remain"); break; case REL_DONE: strcat(disp.time_string, " Track"); break; case REL_REMAIN: strcat(disp.time_string, " Track remain"); break; } /* update the progress bar. always in absolute mode */ if (cntl.cd.length > 0) gtk_progress_bar_update(GTK_PROGRESS_BAR(disp.progress_bar), (float) (cntl.cd.cd_time) / (float) (cntl.cd.length)); else gtk_progress_bar_update(GTK_PROGRESS_BAR(disp.progress_bar), 0.5); disp.cd_title = cntl.cd.title; disp.track_title = cntl.cd.toc[cntl.cd.current_track].title; /* print at least the track number, if no info is present */ if ((disp.track_title == NULL) || (strlen(disp.track_title) <= 0)) { track_string = (char *) malloc(10); sprintf(track_string, "Track %d", cntl.cd.current_track); } /* Clear the area */ gdk_draw_rectangle(disp.pixmap, disp.gc, TRUE, 0, 0, update.width, update.height); /* draw the strings */ gdk_draw_string(disp.pixmap, disp.title_font, disp.drawing_area->style->black_gc, 0, 10, (disp.cd_title == NULL) ? "Unknown" : disp.cd_title); gdk_draw_string(disp.pixmap, disp.title_font, disp.drawing_area->style->black_gc, 0, 25, (track_string != NULL) ? track_string : disp.track_title); gdk_draw_string(disp.pixmap, disp.time_font, disp.drawing_area->style->black_gc, 0, 40, disp.time_string); gdk_draw_string(disp.pixmap, disp.status_font, disp.drawing_area->style->black_gc, 0, 55, status_string); /* update the display */ gtk_widget_draw(disp.drawing_area, &update); free(status_string); if (track_string != NULL) free(track_string); /* this is checked only once: at startup. if the flag is set, the drive is already playing, so we'll install a timout here */ if (startup_playing == 1) { startup_playing = 0; disp.timeout = gtk_timeout_add(500, update_display, NULL); #ifdef DEBUG printf("startup\n"); #endif } /* the query has just finished. we need the extra step to ensure display window is updated */ if (cntl.looking == 2) cntl.looking = 0; if (cntl.looking == 1) { /* we're looking. now, check if search is finished */ timeout.tv_sec = 0; timeout.tv_usec = 100000; /* wait 0.1 seconds for data */ FD_ZERO(&set); /* set up the set of filedescribtors to check */ FD_SET(cntl.pipeline[0], &set); if (select(FD_SETSIZE, &set, NULL, NULL, &timeout) == 1) { /* we have data! jubelate! */ stream = fdopen(cntl.pipeline[0], "r"); do { buffer = get_string_piece(stream, '\n'); string_append(&data, buffer); free(buffer); } while (strstr(data, "\r\n.\r\n") == NULL); fscanf(stream, "%d", &lookup_status); /* we know now, where the data is from */ #ifdef DEBUG printf("lookup status: %d\n", lookup_status); #endif fclose(stream); cddb_handle_data(&cntl.cd, data); /*yes */ cntl.looking = 2; free(data); if (info_dialog_open() != 0) { /* if the info box is open, update it */ info_dialog_set_cd(&cntl.cd); cd_page_display(); } if ((opt.save != 0) && (lookup_status == REMOTE_OK)) /* we have new data from remote, so save it for later */ save_cd(&cntl.cd, opt.path); } } /* we need to check again, because an operation may have changed status */ if (track_changed) #ifdef DEBUG { #endif status = cd_status(cntl.cd.handle); #ifdef DEBUG printf("st:%x\n", status); } #endif if (cntl.drivestatus == EJECTED) { disp.timeout = 0; return FALSE; } if (status == CDROM_AUDIO_PLAY || status == CDROM_AUDIO_PAUSED || cntl.looking != 0) return TRUE; /* if CDROM reports audio play error, no longer update display */ disp.timeout = 0; return FALSE; } static gint configure_event(GtkWidget * widget, GdkEventConfigure * event) { #ifdef DEBUG_EV printf("configure_event\n"); #endif if (disp.pixmap != NULL) gdk_pixmap_unref(disp.pixmap); disp.pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1); gdk_draw_rectangle(disp.pixmap, widget->style->bg_gc[GTK_STATE_NORMAL], TRUE, 0, 0, widget->allocation.width, widget->allocation.height); return TRUE; } static gint expose_event(GtkWidget * widget, GdkEventExpose * event) { #ifdef DEBUG_EV printf("expose_event\n"); #endif gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], disp.pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } void switch_timemode(void) { disp.timemode++; disp.timemode %= 4; update_display(NULL); } void switch_playmode(void) { cntl.playmode++; cntl.playmode %= 4; switch (cntl.playmode) { case RANDOM: strcpy(disp.mode_string, "Random"); break; case LOOP_CD: strcpy(disp.mode_string, "CD"); break; case LOOP_TRACK: strcpy(disp.mode_string, "Track"); break; default: strcpy(disp.mode_string, "Normal"); } update_display(NULL); } void update_status_string(void) { switch (cntl.drivestatus) { case PLAYING: sprintf(disp.status_string, "%s", "Playing"); break; case PAUSED: sprintf(disp.status_string, "%s", "Paused"); break; case STOPPED: sprintf(disp.status_string, "%s", "Stopped"); break; case NO_STATUS: sprintf(disp.status_string, "%s", "No Status"); break; case ERROR: sprintf(disp.status_string, "%s", "Error"); break; case EJECTED: sprintf(disp.status_string, "%s", "Ejected"); break; } } cccd-0.3beta4.orig/cccd.h0100400000175000017500000000511306567045102013644 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: cccd.h,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ #ifndef CCCD_CCCD_H #define CCCD_CCCD_H enum Timemodes { ABS_DONE, ABS_REMAIN, REL_DONE, REL_REMAIN }; enum Playmodes { NORMAL, LOOP_TRACK, LOOP_CD, RANDOM }; enum Drivestatus { PLAYING, PAUSED, STOPPED, NO_STATUS, ERROR, EJECTED }; struct control { struct audio_cd cd; char devicename[40]; int pipeline[2]; int looking; /* 0: we are not looking for cddb data, 1: we are looking, 2: looking just finished */ enum Playmodes playmode; enum Drivestatus drivestatus; int tray_open; }; struct display { GtkWidget *progress_bar, *drawing_area; GdkPixmap *pixmap; GdkGC *gc; GdkFont *title_font, *status_font, *time_font; char *cd_title, *track_title, *status_string, *time_string, *mode_string; int timeout; enum Timemodes timemode; }; /* init functions */ void init_gui(void); int init_cd(int handle); int init_display(GtkWidget * widget, GdkEvent * event, gpointer callback_data); /* callback functions for the GUI */ void destroy_callback(GtkWidget *, gpointer); void prev_callback(GtkWidget *, gpointer); void play_callback(GtkWidget *, gpointer); void next_callback(GtkWidget *, gpointer); void pause_callback(GtkWidget *, gpointer); void stop_callback(GtkWidget *, gpointer); void eject_callback(GtkWidget *, gpointer); void info_callback(GtkWidget *, gpointer); /* event handlers */ gint update_display(gpointer data); /* misc functions */ void switch_timemode(void); void switch_playmode(void); void update_status_string(void); /* GUI button sizes */ #define GUI_BUTTON_X 28 #define GUI_BUTTON_Y 25 /* display.status_string magic number */ #define STATUS_STRING_MAXLEN 9 #endif /* CCCD_CCCD_H */ cccd-0.3beta4.orig/cd.c0100400000175000017500000003476007416141100013330 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: cd.c,v 0.3.3.2 1999/01/30 16:30:28 jochen Exp $ */ #include #include /* get rid of this one the whole thing is up and running */ #include #include #include #include #include "cd.h" #include "general.h" #include "cddb.h" /* here is a collection of function needed to control audio cd playing under linux */ int toc_entry_copy(struct toc_entry *dest, struct toc_entry *src) { if (dest == NULL || src == NULL) return -1; if (dest->title != NULL) free(dest->title); dest->title = NULL; if (src->title != NULL) { dest->title = strdup(src->title); } if (dest->comments != NULL) free(dest->comments); if (src->comments != NULL) { dest->comments = strdup(src->comments); } dest->play = src->play; dest->track = src->track; dest->length = src->length; dest->audio = src->audio; dest->offset = src->offset; return 0; } int audio_cd_copy(struct audio_cd *dest, struct audio_cd *src) { int counter; if (dest == NULL || src == NULL) return -1; if (dest->title != NULL) free(dest->title); dest->title = NULL; if (src->title != NULL) { dest->title = strdup(src->title); } if (dest->category != NULL) free(dest->category); dest->category = NULL; if (src->category != NULL) { dest->category = strdup(src->category); } if (dest->comments != NULL) free(dest->comments); if (src->comments != NULL) dest->comments = strdup(src->comments); if (dest->id != NULL) free(dest->id); if (src->id != NULL) dest->id = strdup(src->id); dest->handle = src->handle; dest->length = src->length; dest->rlength = src->rlength; dest->cd_time = src->cd_time; dest->track_time = src->track_time; dest->first_track = src->first_track; dest->last_track = src->last_track; dest->current_track = src->current_track; dest->revision = src->revision; for (counter = 0; counter < 100; counter++) { if (toc_entry_copy(&dest->toc[counter], &src->toc[counter]) < 0) return -1; } return 0; } int cd_open_device(char *devicename) { int handle, i=0; do { handle = open(devicename, O_RDONLY); } while (++i < 500 && handle < 0); return handle; } int cd_init(int handle, struct audio_cd *cd) { struct toc_entry *toc; struct cdrom_tochdr tocheader; struct cdrom_multisession ms; int status, i = 0, tracknum; long int offset[100]; /* first get the handle. repeat because this function also causes the open cd tray to close on most drives and reading a the header while the tray is open causes nonsense data */ cd->handle = handle; do { /* get the starting and ending track of the cd */ usleep(300000); /* try every 1/3 seconds for max. 30 secs */ status = ioctl(cd->handle, CDROMREADTOCHDR, &tocheader); if (status < 0) ioctl(cd->handle, CDROMRESET); } while (++i < 90 && status != 0); if (status != 0) { return -1; } cd->first_track = tocheader.cdth_trk0; cd->last_track = tocheader.cdth_trk1; /* this should not really happen with proper audio CDs */ if (cd->last_track > 99) { cd->last_track = 99; /* g_warning("Only 99 tracks will be played\n"); */ } /* read the toc entrys */ for (i = cd->first_track; i <= cd->last_track; i++) { toc = cd_toc_entry(cd->handle, i); if (toc == NULL) { return -1; } cd->toc[i] = *toc; free(toc); } /* here we need some code to fix the TOC for multisession CDs. the end of last audio track must be inside first session surprisingly the following code does not yield the correct result (as seen in cdparanoiaIII and cdda2wav. if you know why, tell me */ ms.addr_format = CDROM_LBA; status = ioctl(cd->handle, CDROMMULTISESSION, &ms); /* what is also unclear: if we have a CD with (say) 1 audio session and two data sessions, how will the CDROMMULTISESSION ioctl() give us the correct end of the audio session? */ /* FIXME: this calculation wrong for multisession/mixed mode CDs */ /* get the leadout track and compute the cd length from that */ toc = cd_toc_entry(cd->handle, CDROM_LEADOUT); if (toc == NULL) { return -1; } /* cd->length = ( toc->offset - cd->toc[cd->first_track].offset ) / 75; */ cd->rlength = cd->length = toc->offset / 75; free(toc); /* now we compute the CDDB disk id once and for all */ tracknum = cd->last_track - cd->first_track + 1; for (i = 0; i < tracknum; i++) offset[i] = cd->toc[i + cd->first_track].offset; cd->id = (char *) malloc(10); sprintf(cd->id, "%08lx", cddb_disk_id(cd->rlength, tracknum, offset)); /* now we can mangle up the track and disk length of multisession/mixed mode CD */ /* since the ioctl() does not work, we use heuristics */ for (i = cd->last_track; i > 0; i--) { if (i > 0 && !cd->toc[i].audio) cd->length -= cd->toc[i].length; if (i > 0 && !cd->toc[i].audio && cd->toc[i - 1].audio) { #ifdef DEUBG printf("last audio: %d first data: %d\n", i - 1, i); #endif cd->toc[i - 1].length -= 152; /* magic number, don't ask */ cd->length -= 152; break; } } cd->title = NULL; cd->comments = NULL; cd->revision = -1; /* cd->id = NULL; it already contains data */ return 0; } /* check if given track is last track on CD read the CD TOC, instead of carrying extra data around */ int is_last_track(int handle, int track) { struct cdrom_tochdr tocheader; int status; if ((status = ioctl(handle, CDROMREADTOCHDR, &tocheader))) return 0; #ifdef DEBUG_CD printf("is_last_track: %d vs %d (%d)\n", track, tocheader.cdth_trk1, status); #endif return (track == tocheader.cdth_trk1); } struct toc_entry *cd_toc_entry(int handle, int track) { struct toc_entry *toc = (struct toc_entry *) malloc(sizeof(struct toc_entry)); struct cdrom_tocentry tocentry; struct cdrom_tochdr tocheader; int status; tocentry.cdte_track = track; tocentry.cdte_format = CDROM_MSF; status = ioctl(handle, CDROMREADTOCENTRY, &tocentry); if (status != 0) { free(toc); return NULL; } toc->track = track; toc->offset = MSF_TO_FRAME(tocentry.cdte_addr.msf); /* set the flag to indicate if the track contains audio or data */ /* TODO: double check functionality here */ /* it's buggy. if track is data track, tocentry.cdte_ctrl is 0x04, ANDing with CDROM_DATA_TRACK still yields 4. XOR that with 1 gives 5. too bad we only check audio != 0 before playing. this is fixed now */ toc->audio = ((tocentry.cdte_ctrl & CDROM_DATA_TRACK) >> 2) ^ 1; /* calculate the track length, if we aren't the leadout track */ if (track != CDROM_LEADOUT) { status = ioctl(handle, CDROMREADTOCHDR, &tocheader); if (status != 0) { free(toc); return NULL; } /* if we're at the next track, we need to compare to the leadout track */ tocentry.cdte_track = (track == tocheader.cdth_trk1) ? CDROM_LEADOUT : track + 1; status = ioctl(handle, CDROMREADTOCENTRY, &tocentry); if (status != 0) { free(toc); return NULL; } /* subtract our tracks offset from the offset of the next track and then divide by 75 to get the tracklength in seconds */ toc->length = (MSF_TO_FRAME(tocentry.cdte_addr.msf) - toc->offset) / 75; } /* blank out the track title. to be filled in by the user */ toc->title = NULL; toc->comments = NULL; toc->play = toc->audio; return toc; } /* cd_status reports CDROM audio status from handle */ int cd_status(int handle) { struct cdrom_subchnl subchannel; struct cdrom_msf0 msf0, msf1; int status; subchannel.cdsc_format = CDROM_MSF; status = ioctl(handle, CDROMSUBCHNL, &subchannel); if (status != 0) { return CDROM_AUDIO_ERROR; } /* check for 'hidden playing', i.e. when the drive is playing through front panel commands, or still playing because of an earlier session */ if (subchannel.cdsc_audiostatus == CDROM_AUDIO_NO_STATUS) { subchannel.cdsc_format = CDROM_MSF; ioctl(handle, CDROMSUBCHNL, &subchannel); msf0 = subchannel.cdsc_absaddr.msf; usleep(40000); /* sleep for 3 frames[a 3/75 sec] */ ioctl(handle, CDROMSUBCHNL, &subchannel); msf1 = subchannel.cdsc_absaddr.msf; if (MSF_TO_FRAME(msf1) > MSF_TO_FRAME(msf0)) { return CDROM_AUDIO_PLAY; } } return (subchannel.cdsc_audiostatus); } void cd_clear(struct audio_cd *cd) { int i; for (i = 0; i < 100; i++) { cd->toc[i].track = 0; cd->toc[i].length = 0; cd->toc[i].audio = 0; cd->toc[i].offset = 0; cd->toc[i].title = NULL; cd->toc[i].comments = NULL; cd->toc[i].play = 1; } cd->handle = 0; cd->length = 0; cd->rlength = 0; cd->cd_time = 0; cd->track_time = 0; cd->first_track = 0; cd->last_track = 0; cd->current_track = 0; cd->revision = -1; cd->title = NULL; cd->category = NULL; cd->comments = NULL; cd->id = NULL; cd->toc[0].title = strdup(" "); /* show nothing when stopped */ } int cd_deinit(struct audio_cd *cd) { int i; for (i = cd->first_track; i <= cd->last_track; i++) if (cd->toc[i].title != NULL) free(cd->toc[i].title); if (cd->title != NULL) { free(cd->title); } if (cd->category != NULL) { free(cd->category); } i = cd->handle; cd_clear(cd); cd->handle = i; return 1; } int cd_reset(int handle) { int status = ioctl(handle, CDROMRESET); return status; } int cd_eject(int handle) { int status = ioctl(handle, CDROMEJECT); return status; } int cd_close_tray(int handle) { int status = ioctl(handle, CDROMCLOSETRAY); return status; } /* cd_read_subchannel this checks current track and track time and stores in cd structure NEW: we also return CDROM audio status when ioctl() was ok. otherwise some stati will be gobbled up */ int cd_read_subchannel(struct audio_cd *cd) { /* this function will read out the subchannel of the CD drive and set the cd_time, track_time and current_track accordingly */ struct cdrom_subchnl subchannel; int status; subchannel.cdsc_format = CDROM_MSF; status = ioctl(cd->handle, CDROMSUBCHNL, &subchannel); if (status < 0) return status; cd->cd_time = MSF_TO_SEC(subchannel.cdsc_absaddr.msf); cd->track_time = MSF_TO_SEC(subchannel.cdsc_reladdr.msf); cd->current_track = subchannel.cdsc_trk; #ifdef DEBUG_CD printf("subch: status %0x, current_track %d, track_time %d\n", subchannel.cdsc_audiostatus, cd->current_track, cd->track_time); #endif return subchannel.cdsc_audiostatus; } /* this subroutine knows nothing about the CD toc, it only gets passed a handle and track number. then it issues several calls to CDROM_TOCENTRY and CDROM_TOCHDR to get the information we already should know inside the cntl.cd structure. it should be evaluated if we can pass the whole cntl.cd to this one, just as cd_read_subchannel. */ int cd_audio_play(int handle, int track) { struct cdrom_ti trackinfo; struct cdrom_msf msf; struct toc_entry *toc; #ifdef DEBUG int l; #endif trackinfo.cdti_trk0 = track; trackinfo.cdti_ind0 = 0; trackinfo.cdti_trk1 = CDROM_LEADOUT; trackinfo.cdti_ind1 = 0; /* PLAYMSF probably nicer, at least if TRKIND not supported... it happens.. if(ioctl( handle, CDROMPLAYTRKIND, &trackinfo )==0) return 0; we comment this out, try to only use PLAYMSF */ /* ok, this drive doesn't support PLAYTRKIND, so we'll use PLAYMSF */ toc = cd_toc_entry(handle, track); msf.cdmsf_min0 = toc->offset / 4500; msf.cdmsf_sec0 = (toc->offset / 75) - (msf.cdmsf_min0 * 60); msf.cdmsf_frame0 = toc->offset - (msf.cdmsf_min0 * 60 + msf.cdmsf_sec0) * 75; #ifdef DEBUG l = toc->length; #endif toc = cd_toc_entry(handle, (is_last_track(handle, track)) ? CDROM_LEADOUT : track + 1); msf.cdmsf_min1 = toc->offset / 4500; msf.cdmsf_sec1 = (toc->offset / 75) - (msf.cdmsf_min1 * 60); msf.cdmsf_frame1 = toc->offset - (msf.cdmsf_min1 * 60 + msf.cdmsf_sec1) * 75; #ifdef DEBUG printf("Play: %d:%d:%d -> %d:%d:%d (%d seconds)\n", msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0, msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1, l); #endif if (ioctl(handle, CDROMPLAYMSF, &msf) == 0) return 0; return -1; } int cd_audio_stop(int handle) { return ioctl(handle, CDROMSTOP); } int cd_audio_pause(int handle) { return ioctl(handle, CDROMPAUSE); } int cd_audio_resume(int handle) { return ioctl(handle, CDROMRESUME); } /* the following functions are legacy functions. they are not used in cccd */ int cd_audio_position(int handle, int mode) { struct cdrom_subchnl subchannel; int status; subchannel.cdsc_format = CDROM_MSF; status = ioctl(handle, CDROMSUBCHNL, &subchannel); if (status != 0 || (mode != 0 && mode != 1)) return -1; if (mode == 0) { /* absolute audio position in seconds */ status = MSF_TO_SEC(subchannel.cdsc_absaddr.msf); } else { /* relative audio position in seconds */ status = MSF_TO_SEC(subchannel.cdsc_reladdr.msf); } return status; } int cd_audio_current_track(int handle) { struct cdrom_subchnl subchannel; int status; subchannel.cdsc_format = CDROM_MSF; status = ioctl(handle, CDROMSUBCHNL, &subchannel); if (status != 0) { return -1; } return subchannel.cdsc_trk; } /* the return value is in seconds */ int cd_audio_position_absolute_done(struct audio_cd *cd) { return cd_audio_position(cd->handle, 0); } int cd_audio_position_absolute_remain(struct audio_cd *cd) { return (cd->length - cd_audio_position(cd->handle, 0)); } int cd_audio_position_relative_done(struct audio_cd *cd, int track) { return (cd_audio_position(cd->handle, 1)); } int cd_audio_position_relative_remain(struct audio_cd *cd, int track) { return (cd->toc[track].length - cd_audio_position(cd->handle, 1) - cd->toc[cd->first_track].offset / 75); } cccd-0.3beta4.orig/cd.h0100400000175000017500000000635206567045103013345 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: cd.h,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ #ifndef CCCD_CD_H #define CCCD_CD_H #include #include #define MSF_TO_SEC(msf) ( msf.minute*60 + msf.second ) #define MSF_TO_FRAME(msf) ( MSF_TO_SEC(msf)*75 + msf.frame ) struct toc_entry { int track; int play; int length; /* in seconds */ int audio; /* flag to show if the track contains audio material 1=audio */ long int offset; /* offset in frames; max is about 360000 */ char *title; char *comments; }; struct audio_cd { int handle; /* for mixed mode CDs we may manipulate the toc directly, so times are correctly displayed. we must make sure, we have the original data somewhere */ int length; /* in seconds minus the lead in duration (for displaying) */ int rlength; /* in seconds minus the lead in duration (for CDDB) */ int cd_time; /* absolute passed time in seconds */ int track_time; /* time passed, relative to track */ char *title; char *category; char *comments; int first_track; int last_track; int current_track; int revision; /* for cddb */ char *id; /* dito */ struct toc_entry toc[100]; }; /* copy functions for the structs */ int toc_entry_copy(struct toc_entry *dest, struct toc_entry *src); int audio_cd_copy(struct audio_cd *dest, struct audio_cd *src); /* general functions */ int cd_open_device(char *devicename); int cd_init(int handle, struct audio_cd *cd); struct toc_entry *cd_toc_entry(int handle, int track); int cd_status(int handle); void cd_clear(struct audio_cd *cd); int cd_deinit(struct audio_cd *cd); int cd_reset(int handle); int cd_eject(int handle); int cd_close_tray(int handle); int cd_read_subchannel(struct audio_cd *cd); /* audio cd actions */ int cd_audio_play(int handle, int track); int cd_audio_stop(int handle); int cd_audio_pause(int handle); int cd_audio_resume(int handle); int cd_audio_position(int handle, int mode); /* return value in seconds mode = 0 absolute, 1 = relative */ int cd_audio_current_track(int handle); /* audio cd time conversion functions */ int cd_audio_position_absolute_done(struct audio_cd *cd); int cd_audio_position_absolute_remain(struct audio_cd *cd); int cd_audio_position_relative_done(struct audio_cd *cd, int track); int cd_audio_position_relative_remain(struct audio_cd *cd, int track); #endif /* CCCD_CD_H */ cccd-0.3beta4.orig/cddb.c0100600000175000017500000004244307416145102013643 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: cddb.c,v 0.3.3.2 1998/09/06 18:04:25 jochen Exp jst $ */ #include #include #include #include #include #include #include #include "general.h" #include "cd.h" #include "cddbp.h" #include "misc.h" #include "options.h" #include "cddb.h" unsigned long int cddb_disk_id(int length, int tracknum, long int offset[]) { int track; long int result = 0, tmp; for (track = 0; track < tracknum; track++) { tmp = offset[track] / 75; do { result += tmp % 10; tmp /= 10; } while (tmp != 0); } return (result % 0xff) << 24 | (length - (offset[0] / 75)) << 8 | tracknum; } int cddb_lookup(struct audio_cd *cd, char **data, int mode, const char *server, short int port, int protocol, const char *path) { int tracknum, i; long int offset[100]; enum lookup_protocol proto = protocol; enum lookup_order order = mode; enum errors status; tracknum = cd->last_track - cd->first_track + 1; for (i = 0; i < tracknum; i++) offset[i] = cd->toc[i + cd->first_track].offset; /* now do the lookup */ switch (order) { case REMOTE_LOCAL: if ((status = remote_lookup(cd->id, tracknum, offset, cd->rlength, server, port, proto, data)) == REMOTE_OK) break; status = local_lookup(cd->id, path, data); if (data != NULL) string_append(data, "\r\n.\r\n"); break; case LOCAL_REMOTE: if ((status = local_lookup(cd->id, path, data)) == LOCAL_OK) { if (data != NULL) string_append(data, "\r\n.\r\n"); break; } status = remote_lookup(cd->id, tracknum, offset, cd->rlength, server, port, proto, data); break; case REMOTE: status = remote_lookup(cd->id, tracknum, offset, cd->rlength, server, port, proto, data); break; case LOCAL: status = local_lookup(cd->id, path, data); if (data != NULL) string_append(data, "\r\n.\r\n"); break; default: return PARAM_ERROR; } return status; } enum errors remote_lookup(const char *cd_id, int tracknum, long int offset[tracknum], int duration, const char *server, short int port, enum lookup_protocol proto, char **result) { int matches, status, i; char **category = NULL, **title = NULL, **alt_id = NULL; char *final_cat, *final_id, *url, *wwwserver = NULL; FILE *sock = NULL; /* first lets connect to the server and perform a query to get all possible alternatives */ if (proto == CDDBP) { /* cddbp */ sock = socket_init(server, port); if (sock == NULL) return NO_CONNECT; status = cddbp_signon(sock); switch (status) { case 200: case 201: /* might want to differenciate when write() is supported on the server side */ break; case 432: case 433: case 434: fclose(sock); return CONNECT_REFUSED; default: fclose(sock); return SERVER_ERROR; } status = cddbp_handshake(sock, NAME, VERSION); switch (status) { case 200: case 402: break; case 431: fclose(sock); return CONNECT_REFUSED; default: fclose(sock); return SERVER_ERROR; } status = cddbp_query(sock, cd_id, tracknum, offset, duration, &matches, &category, &title, &alt_id); } else { /* http */ wwwserver = url = strdup(server); wwwserver = strsep(&url, "/"); status = http_query(wwwserver, port, url, cd_id, tracknum, offset, duration, &matches, &category, &title, &alt_id, NAME, VERSION); } switch (status) { case 200: case 211: break; case 202: if (proto == CDDBP) cddbp_signoff(sock); return NOT_FOUND; case 403: if (proto == CDDBP) cddbp_signoff(sock); return SERVER_ERROR; default: fclose(sock); return SERVER_ERROR; } final_cat = strdup(category[0]); final_id = strdup(alt_id[0]); for (i = 0; i < matches; i++) { free(category[i]); free(title[i]); free(alt_id[i]); } free(category); free(title); free(alt_id); /* now we have the alternative which we want? check to see if alternate_list can be called if and when gtk becomes thread safe. */ if (proto != CDDBP) { /* http */ status = http_read(wwwserver, port, url, final_cat, final_id, result, NAME, VERSION); } else { /* cddbp */ status = cddbp_read(sock, final_cat, final_id, result); } switch (status) { case 210: break; case 401: if (proto == CDDBP) cddbp_signoff(sock); return NOT_FOUND; case 403: if (proto == CDDBP) cddbp_signoff(sock); return SERVER_ERROR; default: fclose(sock); return SERVER_ERROR; } if (proto == CDDBP) cddbp_signoff(sock); free(final_cat); free(final_id); return REMOTE_OK; } enum errors local_lookup(const char *cd_id, const char *path, char **result) { int matches = 0, status = 0, subdir_num = 0, i; char **subdir = (char **) malloc(1000 * sizeof(char *)); char **title = (char **) malloc(1000 * sizeof(char *)); DIR *dirp, *subdirp; struct dirent *dir_contents, *subdir_contents; struct stat file_stats; char *filename, *subdirname, *buffer, *mark, *final_cat; FILE *file; /* change the working dir to path */ if (path == NULL || (dirp = opendir(path)) == NULL) return NO_CONNECT; while ((dir_contents = readdir(dirp)) != NULL) { if (strcmp(dir_contents->d_name, ".") != 0 && strcmp(dir_contents->d_name, "..") != 0) { subdirname = (char *) malloc(strlen(path) + strlen(dir_contents->d_name) + 2); sprintf(subdirname, "%s/%s", path, dir_contents->d_name); status = stat(subdirname, &file_stats); if (status == 0 && subdir_num < 1000) { if (S_ISDIR(file_stats.st_mode)) { /* is the file a directory? */ if ((subdirp = opendir(subdirname)) != NULL) { /*only enter if we may */ while ((subdir_contents = readdir(subdirp)) != NULL) { if (strcmp(subdir_contents->d_name, cd_id) == 0) { /* we found one */ filename = (char *) malloc(strlen(subdirname) + strlen(cd_id) + 2); sprintf(filename, "%s/%s", subdirname, cd_id); status = stat(filename, &file_stats); if (status < 0 || !S_ISREG(file_stats.st_mode)) continue; file = fopen(filename, "r"); if (file != NULL) { /* we can read the file. therefore it is a match. */ /* quick! take his licence number! or the subdir, name=category */ subdir[subdir_num] = strdup(dir_contents->d_name); buffer = get_ascii_file(file); fclose(file); mark = strstr(buffer, "DTITLE") + 7; i = strcspn(mark, "\r\n"); title[subdir_num] = (char *) malloc(i + 1); strncpy(title[subdir_num], mark, i); title[subdir_num][i] = 0; free(buffer); matches++; subdir_num++; } free(filename); } } closedir(subdirp); } } } free(subdirname); } } closedir(dirp); /* now we should have all possible matches in our buffers */ if (matches == 0) return NOT_FOUND; /* alternate handling */ final_cat = subdir[0]; /* get the result data and return */ filename = (char *) malloc(strlen(path) + 1 + strlen(final_cat) + 1 + strlen(cd_id) + 1); sprintf(filename, "%s/%s/%s", path, final_cat, cd_id); file = fopen(filename, "rt"); *result = get_ascii_file(file); fclose(file); free(filename); /* free memory in the subdir and title buffers */ for (i = 0; i < matches; i++) { free(title[i]); free(subdir[i]); } free(title); free(subdir); return LOCAL_OK; } int cddb_handle_data(struct audio_cd *cd, const char *data) { char *row, *mark; const char *tmp = data; int i, counter = 0, track, oldtrack = 0; if (strncmp(data, "# xmcd", 6) != 0) return -1; while (*tmp != 0) { /* check against end of string */ /* get the row */ i = strcspn(tmp, "\r\n"); while (tmp[i] == '\r' || tmp[i] == '\n') i++; row = (char *) malloc(i + 1); strncpy(row, tmp, i); row[i] = 0; tmp += i; /* eval the row */ if (strncmp(row, "TTITLE", 6) == 0) { /* Track TItle */ /* get the track number before going on */ /* skip the TTITLE */ mark = row + 6; counter = 0; /* convert ascii -> int */ while (*mark != '=') { counter *= 10; counter += *mark - 0x30; mark++; } mark++; /* and skip the '=' */ /* here we check for continuation lines */ track = counter + cd->first_track; if (track != oldtrack) { /* new track read */ /* copy the track title into the cd struct */ if (cd->toc[track].title != NULL) free(cd->toc[track].title); cd->toc[track].title = strdup(mark); /* FIXME: this is ugly when the continuation occurs at |: hello |world */ strip_trailing_space(&cd->toc[track].title); /* this gets rid of \r\n, \n or whatever */ strip_leading_space(&cd->toc[track].title); oldtrack = track; } else { /* its a continuation line ... just like old days */ cd->toc[track].title = string_append(&cd->toc[track].title, mark); strip_trailing_space(&cd->toc[track].title); /* this gets rid of \r\n, \n or whatever */ } } else if (strncmp(row, "DTITLE", 6) == 0) { /* CD Title */ i = strcspn(row, "="); i++; /* skip to the data */ mark = row + i; if (cd->title != NULL) free(cd->title); cd->title = strdup(mark); strip_trailing_space(&cd->title); /* this gets rid of \r\n, \n or whatever */ strip_leading_space(&cd->title); } else if (strncmp(row, "EXTD", 4) == 0) { /* CD comments */ mark = row + 5; /* move to the data */ string_append(&cd->comments, mark); strip_trailing_space(&cd->comments); /* this gets rid of \r\n, \n or whatever */ } else if (strncmp(row, "EXTT", 4) == 0) { /* Track comments */ mark = row + 4; /* move to the track number */ counter = 0; while (*mark != '=') { counter *= 10; counter += *mark - 0x30; mark++; } mark++; /* and skip the '=' */ track = counter + cd->first_track; string_append(&cd->toc[track].comments, mark); strip_trailing_space(&cd->toc[track].comments); /* this gets rid of \r\n, \n or whatever */ } else if (strncmp(row, "PLAYLIST", 8) == 0) { /* Playlist */ mark = strchr(row, '='); do { do { mark++; } while (isspace(*mark) && *mark != 0); counter = 0; while (isdigit(*mark)) { counter *= 10; counter += *mark - 0x30; mark++; } cd->toc[counter].play = 1; } while ((mark = strchr(mark, ',')) != NULL); } else if (strncmp(row, "# Revision", 10) == 0) { /* revision */ mark = strchr(row, ':') + 1; while (isspace(*mark)) mark++; counter = 0; while (isdigit(*mark)) { counter *= 10; counter += *mark - 0x30; mark++; } cd->revision = counter; } /* ignore any other results */ free(row); } return 0; } #define CDDB_MAX_LINELEN 80 /* here we convert our internal representation of CDDB data to the CDDB format. according to cddb.howto 1.23 98/05/12, App B, this means a line ends with \n only */ char *cddb_collate_data(struct audio_cd *cd, char **result) { char *tmp, *row, *mark; int i; if (*result != NULL) free(*result); row = (char *) malloc(81); /* one row of CDDB file */ /* first, print the header */ *result = strdup("# xmcd\n#\n# Track frame offsets:\n"); /* then add the track offsests */ for (i = cd->first_track; i < cd->last_track + 1; i++) { sprintf(row, "#\t%ld\n", cd->toc[i].offset); string_append(result, row); } /* disc length ... */ sprintf(row, "#\n# Disc length: %d seconds\n", cd->rlength); string_append(result, row); /* and revision */ sprintf(row, "#\n# Revision: %d\n# Submitted via: %s %s\n#\n", cd->revision, NAME, VERSION); string_append(result, row); /* discid */ sprintf(row, "DISCID=%s\n", cd->id); string_append(result, row); /* disc title */ if (cd->title != NULL) { tmp = (char *) malloc(strlen(cd->title) + strlen("DTITLE=\n") + 1); sprintf(tmp, "DTITLE=%s\n", cd->title); strncpy(row, tmp, CDDB_MAX_LINELEN); if (strlen(tmp) < CDDB_MAX_LINELEN) row[strlen(tmp)] = 0; else { /* FIXME: if DTITLE > 80 it is truncated */ row[CDDB_MAX_LINELEN-1] = '\n'; row[CDDB_MAX_LINELEN] = 0; } free(tmp); } else sprintf(row, "DTITLE=\n"); /* in case it's empty */ string_append(result, row); /* do all the track titles */ for (i = cd->first_track; i < cd->last_track + 1; i++) { if (cd->toc[i].title != NULL) { tmp = (char *) malloc(strlen(cd->toc[i].title) + strlen("TTITLE99=\n") + 1); sprintf(tmp, "TTITLE%d=%s\n", (i - cd->first_track), cd->toc[i].title); /* here we expand lines that are too long to multiple lines */ strncpy(row, tmp, CDDB_MAX_LINELEN); if (strlen(tmp) >= CDDB_MAX_LINELEN) { row[CDDB_MAX_LINELEN-1] = '\n'; row[CDDB_MAX_LINELEN] = 0; string_append(result, row); /* flush row to output */ mark = (char *) malloc(strlen(cd->toc[i].title) + strlen("TTITLE99=\n") + 1); sprintf(mark, "TTITLE%d=%s", (i - cd->first_track), tmp+CDDB_MAX_LINELEN-1); strncpy(row, mark, CDDB_MAX_LINELEN); row[(strlen(mark) < CDDB_MAX_LINELEN) ? strlen(mark) : CDDB_MAX_LINELEN] = 0; free(mark); } else row[strlen(tmp)] = 0; free(tmp); } else sprintf(row, "TTITLE%d=\n", (i - cd->first_track)); string_append(result, row); } /* extended data (comments) */ if (cd->comments != NULL) { tmp = cd->comments; do { strcpy(row, "EXTD="); strncat(row, tmp, 80 - strlen("EXTD=\n")); strcat(row, "\n"); string_append(result, row); tmp += strlen(row) - strlen("EXTD=\n"); } while (*tmp != 0); } else string_append(result, "EXTD=\n"); /* track comments */ for (i = cd->first_track; i < cd->last_track + 1; i++) { if (cd->toc[i].comments != NULL) { tmp = cd->toc[i].comments; do { sprintf(row, "EXTT%d=", i - cd->first_track); strncat(row, tmp, 80 - strlen("EXTT99=\n")); strcat(row, "\n"); string_append(result, row); tmp += strlen(row) - strlen("EXTT99=\n"); } while (*tmp != 0); } else { sprintf(row, "EXTT%d=\n", i - cd->first_track); string_append(result, row); } } /* the playlist */ strcpy(row, "PLAYORDER="); tmp = (char *) malloc(5); for (i = cd->first_track; i < cd->last_track + 1; i++) { if (strlen(row) > 75) { /* only 80 char/row allowed. check for overflow */ strcat(row, "\n"); string_append(result, row); strcpy(row, "PLAYORDER="); } if (cd->toc[i].play && cd->toc[i].audio) { sprintf(tmp, "%d", i - cd->first_track); if (i < cd->last_track) strcat(tmp, ","); strcat(row, tmp); } } string_append(result, row); free(row); /* and we are done */ return *result; } enum errors cddb_remote_categories(const char *server, short int port, enum lookup_protocol proto, char **category, int *cat_num) { int status, size; char *buffer = NULL, *tmp, *wwwserver, *url; FILE *socket; if (proto == CDDBP) { socket = socket_init(server, port); if (socket == NULL) return NO_CONNECT; status = cddbp_signon(socket); switch (status) { case 200: case 201: /* might want to differenciate when write() is supported on the server side */ break; case 432: case 433: case 434: fclose(socket); return CONNECT_REFUSED; default: fclose(socket); return SERVER_ERROR; } status = cddbp_handshake(socket, NAME, VERSION); switch (status) { case 200: case 402: break; case 431: fclose(socket); return CONNECT_REFUSED; default: fclose(socket); return SERVER_ERROR; } status = cddbp_stat(socket, &buffer); cddbp_signoff(socket); fclose(socket); } else { wwwserver = url = strdup(server); wwwserver = strsep(&url, "/"); status = http_stat(wwwserver, port, url, &buffer, NAME, VERSION); } if (status != 210) { /* we have a problem */ free(buffer); return SERVER_ERROR; } /* skip to the category data */ tmp = strstr(buffer, "Database entries by category:"); tmp = strchr(tmp, '\n') + 1; *cat_num = 0; while (isspace(*tmp) && *cat_num < 99) { size = strcspn(tmp, ":"); category[*cat_num] = (char *) malloc(size + 1); strncpy(category[*cat_num], tmp, size); category[*cat_num][size] = 0; strip_leading_space(&(category[*cat_num])); (*cat_num)++; tmp = strchr(tmp, '\n') + 1; } free(buffer); return REMOTE_OK; } cccd-0.3beta4.orig/cddb.h0100400000175000017500000000345206567045103013651 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: cddb.h,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ #ifndef CCCD_CDDB_H #define CCCD_CDDB_H /* cddb functions */ unsigned long int cddb_disk_id(int length, int tracknum, long int offset[]); int cddb_lookup(struct audio_cd *cd, char **data, int mode, const char *server, short int port, int protocol, const char *path); enum errors cddb_remote_categories(const char *server, short int port, enum lookup_protocol proto, char **category, int *cat_num); int cddb_handle_data(struct audio_cd *cd, const char *data); char *cddb_collate_data(struct audio_cd *cd, char **result); enum errors local_lookup(const char *cd_id, const char *path, char **result); enum errors remote_lookup(const char *cd_id, int tracknum, long int offset[tracknum], int duration, const char *server, short int port, enum lookup_protocol, char **result); #endif /* CCCD_CDDB_H */ cccd-0.3beta4.orig/cddbp.c0100400000175000017500000002606407416141100014014 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: cddbp.c,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ #include #include #include #include "misc.h" #include "cddbp.h" int cddbp_signon(FILE * socket) { char *buffer; int code; buffer = get_string_piece(socket, '\n'); #ifdef DEBUG printf("<= %s\n", buffer); #endif /* convert the answer code to int */ code = ((buffer[0] - 0x30) * 10 + (buffer[1] - 0x30)) * 10 + (buffer[2] - 0x30); free(buffer); return code; } int cddbp_handshake(FILE * socket, const char *clientname, const char *version) { char *buffer; int code; /* Greetings! */ #ifdef DEBUG printf("=> cddb hello %s %s %s %s\n", getenv("USER"), getenv("HOSTNAME"), clientname, version); #endif fprintf(socket, "cddb hello %s %s %s %s\n", getenv("USER"), getenv("HOSTNAME"), clientname, version); buffer = get_string_piece(socket, '\n'); #ifdef DEBUG printf("<= %s\n", buffer); #endif code = ((buffer[0] - 0x30) * 10 + (buffer[1] - 0x30)) * 10 + (buffer[2] - 0x30); free(buffer); return code; } int cddbp_query(FILE * socket, const char *disk_id, int tracknum, long int offset[tracknum], int duration, int *matches, char ***category_buffer, char ***title_buffer, char ***id_buffer) { char *buffer, *tmp; int code, i; buffer = (char *) malloc(strlen("cddb query ") + strlen(disk_id) + 1 + 3 + tracknum * 10 + 6 + 1); sprintf(buffer, "cddb query %s %d ", disk_id, tracknum); tmp = (char *) malloc(10); for (i = 0; i < tracknum; i++) { sprintf(tmp, "%ld ", offset[i]); strcat(buffer, tmp); } sprintf(tmp, "%d\n", duration); strcat(buffer, tmp); free(tmp); #ifdef DEBUG printf("=> %s\n", buffer); #endif fprintf(socket, buffer); free(buffer); tmp = buffer = get_string_piece(socket, '\n'); #ifdef DEBUG printf("<= %s\n", buffer); #endif code = ((buffer[0] - 0x30) * 10 + (buffer[1] - 0x30)) * 10 + (buffer[2] - 0x30); *matches = 0; if (code == 200) { buffer = tmp + 4; } else if (code == 211) { free(buffer); tmp = buffer = get_string_piece(socket, '\n'); } else return code; /* get the alternavtives */ *title_buffer = (char **) malloc(sizeof(char *) * 1000); *category_buffer = (char **) malloc(sizeof(char *) * 1000); *id_buffer = (char **) malloc(sizeof(char *) * 1000); do { if (strcmp(buffer, ".\r\n") == 0) break; /* if we get more than 1000 matches, the char*[] will be full, and we will skip the remaining few million matches */ if (*matches == 999) { free(tmp); continue; } i = strcspn(buffer, " "); (*category_buffer)[*matches] = (char *) malloc(i + 1); strncpy((*category_buffer)[*matches], buffer, i); (*category_buffer)[*matches][i] = 0; /*terminate the string */ buffer += i + 1; /* skip to the disk id */ (*id_buffer)[*matches] = (char *) malloc(9); /* the disk id is always 8 char long */ strncpy((*id_buffer)[*matches], buffer, 8); (*id_buffer)[*matches][8] = 0; /*terminate the string */ buffer += 9; i = strlen(buffer) - 2; /*strip the remaining '\r\n' from the end of the line */ (*title_buffer)[*matches] = (char *) malloc(i + 1); strncpy((*title_buffer)[*matches], buffer, i); (*title_buffer)[*matches][i] = 0; /*terminate the string */ (*matches)++; free(tmp); } while (code != 200 && (tmp = buffer = get_string_piece(socket, '\n')) != NULL); return code; } int cddbp_read(FILE * socket, const char *category, const char *disk_id, char **result_buffer) { char *buffer; int code; /* int i; /* debugging aid */ #ifdef DEBUG printf("=> cddb read %s %s\n", category, disk_id); #endif fprintf(socket, "cddb read %s %s\n", category, disk_id); buffer = get_string_piece(socket, '\n'); #ifdef DEBUG printf("<= %s\n", buffer); #endif code = ((buffer[0] - 0x30) * 10 + (buffer[1] - 0x30)) * 10 + (buffer[2] - 0x30); free(buffer); if (code > 210) return code; /* skip the first row, and copy the data that follows into the result buffer */ *result_buffer = (char *) malloc(1); strcpy(*result_buffer, ""); do { buffer = get_string_piece(socket, '\n'); #ifdef DEBUG printf("<= %s", buffer); /* for (i=0; buffer[i] != '\0'; i++) printf("%x ",buffer[i]); /* we want hex */ #endif string_append(result_buffer, buffer); free(buffer); } while (strstr(*result_buffer, "\r\n.\r\n") == NULL); return code; } int cddbp_stat(FILE * socket, char **result) { int code; char *buffer; #ifdef DEBUG printf("=> stat\n"); #endif fprintf(socket, "stat\n"); buffer = get_string_piece(socket, '\n'); #ifdef DEBUG printf("<= %s\n", buffer); #endif code = ((buffer[0] - 0x30) * 10 + (buffer[1] - 0x30)) * 10 + (buffer[2] - 0x30); free(buffer); if (code > 210) return code; do { buffer = get_string_piece(socket, '\n'); #ifdef DEBUG printf("<= %s", buffer); #endif string_append(result, buffer); free(buffer); } while (strstr(*result, "\r\n.\r\n") == NULL); return code; } void cddbp_signoff(FILE * socket) { char *buffer; fprintf(socket, "quit\n"); buffer = get_string_piece(socket, '\n'); free(buffer); fclose(socket); #ifdef DEBUG printf("Signing off\n"); #endif } int http_query(const char *server, int port, const char *URL, const char *cd_id, int tracknum, long int offset[tracknum], int duration, int *matches, char ***category_buffer, char ***title_buffer, char ***id_buffer, const char *client, const char *version) { char *buffer = (char *) malloc(1024), *tmp = (char *) malloc(1024); int i, code; FILE *sock; sprintf(buffer, "GET /%s?cmd=cddb+query+%s+%d", URL, cd_id, tracknum); for (i = 0; i < tracknum; i++) { sprintf(tmp, "+%ld", offset[i]); strcat(buffer, tmp); strcpy(tmp, ""); } sprintf(tmp, "+%d&hello=%s+%s+%s+%s&proto=1 HTTP/1.0\r\n\r\n", duration, getenv("USER"), getenv("HOST"), client, version); strcat(buffer, tmp); free(tmp); #ifdef DEBUG printf("=> %s\n", buffer); #endif sock = socket_init(server, port); fprintf(sock, "%s", buffer); free(buffer); buffer = get_ascii_file(sock); #ifdef DEBUG printf("<= %s\n", buffer); #endif /* strip the http header of the answer */ tmp = strstr(buffer, "\r\n\r\n"); if (tmp == NULL) { /* uhoh, no empty row anywhere.... */ free(buffer); return -1; } tmp += 4; tmp = strdup(tmp); free(buffer); buffer = tmp; *matches = 0; code = ((tmp[0] - 0x30) * 10 + (tmp[1] - 0x30)) * 10 + (tmp[2] - 0x30); if (code == 200) { buffer += 4; } else if (code == 211) { buffer = strchr(buffer, '\n'); } else { free(tmp); return code; } *title_buffer = (char **) malloc(sizeof(char *) * 1000); *category_buffer = (char **) malloc(sizeof(char *) * 1000); *id_buffer = (char **) malloc(sizeof(char *) * 1000); do { if (strcmp(buffer, ".\r\n") == 0) break; /* if we get more than 1000 matches, the char*[] will be full, and we will skip the remaining few million matches */ if (*matches == 999) { continue; } i = strcspn(buffer, " "); (*category_buffer)[*matches] = (char *) malloc(i + 1); strncpy((*category_buffer)[*matches], buffer, i); (*category_buffer)[*matches][i] = 0; /*terminate the string */ buffer += i + 1; /* skip to the disk id */ (*id_buffer)[*matches] = (char *) malloc(9); /* the disk id is always 8 char long */ strncpy((*id_buffer)[*matches], buffer, 8); (*id_buffer)[*matches][8] = 0; /*terminate the string */ buffer += 9; i = strcspn(buffer, "\r\n"); (*title_buffer)[*matches] = (char *) malloc(i + 1); strncpy((*title_buffer)[*matches], buffer, i); (*title_buffer)[*matches][i] = 0; /*terminate the string */ (*matches)++; buffer += i + 2; /* skip the \r\n */ } while ((buffer = strchr(buffer, '\n')) != NULL); free(tmp); return code; } int http_read(const char *server, int port, const char *URL, const char *category, const char *disk_id, char **result, const char *client, const char *version) { int code; char *buffer, *tmp; FILE *sock; #ifdef DEBUG printf("=> GET /%s?cmd=cddb+read+%s+%s&hello=%s+%s+%s+%s&proto=1 HTTP/1.0\r\n\r\n", URL, category, disk_id, getenv("USER"), getenv("HOST"), client, version); #endif sock = socket_init(server, port); fprintf(sock, "GET /%s?cmd=cddb+read+%s+%s&hello=%s+%s+%s+%s&proto=1 HTTP/1.0\r\n\r\n", URL, category, disk_id, getenv("USER"), getenv("HOST"), client, version); buffer = get_ascii_file(sock); #ifdef DEBUG printf("<= %s\n", buffer); #endif /* strip the http header of the answer */ tmp = strstr(buffer, "\r\n\r\n"); if (tmp == NULL) { /* uhoh, no empty row anywhere.... */ free(buffer); return -1; } tmp += 4; tmp = strdup(tmp); free(buffer); buffer = tmp; code = ((buffer[0] - 0x30) * 10 + (buffer[1] - 0x30)) * 10 + (buffer[2] - 0x30); if (code > 210) { free(buffer); return code; } /* skip the first row, and copy the data that follows into the result buffer */ tmp = strchr(buffer, '\n') + 1; *result = strdup(tmp); free(buffer); return code; } int http_stat(const char *server, int port, const char *URL, char **result, const char *client, const char *version) { int code; char *buffer, *tmp; FILE *sock; #ifdef DEBUG printf("=> GET /%s?cmd=stat&hello=%s+%s+%s+%s&proto=1 HTTP/1.0\r\n\r\n", URL, getenv("USER"), getenv("HOST"), client, version); #endif sock = socket_init(server, port); fprintf(sock, "GET /%s?cmd=stat&hello=%s+%s+%s+%s&proto=1 HTTP/1.0\r\n\r\n", URL, getenv("USER"), getenv("HOST"), client, version); buffer = get_ascii_file(sock); #ifdef DEBUG printf("<= %s\n", buffer); #endif /* strip the http header of the answer */ tmp = strstr(buffer, "\r\n\r\n"); if (tmp == NULL) { /* uhoh, no empty row anywhere.... */ free(buffer); return -1; } tmp += 4; tmp = strdup(tmp); free(buffer); buffer = tmp; code = ((buffer[0] - 0x30) * 10 + (buffer[1] - 0x30)) * 10 + (buffer[2] - 0x30); if (code > 210) { free(buffer); return code; } /* skip the first row, and copy the data that follows into the result buffer */ tmp = strchr(buffer, '\n') + 1; *result = strdup(tmp); free(buffer); return code; } cccd-0.3beta4.orig/cddbp.h0100400000175000017500000000436306567045104014034 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: cddbp.h,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ #ifndef CCCD_CDDBP_H #define CCCD_CDDBP_H /* the following functions are active, meaning that they themselves will communicate directly to the server through the FILE* which is given */ #if 0 #define DEBUG #endif int cddbp_signon(FILE *); int cddbp_handshake(FILE *, const char *clientname, const char *version); int cddbp_query(FILE *, const char *disk_id, int tracknum, long int offsets[tracknum], int duration, int *matches, char ***category_buffer, char ***title_buffer, char ***id_buffer); int cddbp_read(FILE *, const char *category, const char *disk_id, char **result_buffer); int cddbp_stat(FILE *, char **result); int http_query(const char *server, int port, const char *URL, const char *disk_cd, int tracknum, long int offsets[tracknum], int duration, int *matches, char ***category_buffer, char ***title_buffer, char ***id_buffer, const char *client, const char *version); int http_read(const char *server, int port, const char *URL, const char *category, const char *disk_id, char **result_buffer, const char *client, const char *version); int http_stat(const char *server, int port, const char *URL, char **result, const char *client, const char *version); void cddbp_signoff(FILE *); #endif /* CCCD_CDDBP_H */ cccd-0.3beta4.orig/general.h0100400000175000017500000000275507416151554014401 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: general.h,v 0.3.3.3 2002/01/06 22:44:52 jst Exp $ */ #ifndef CCCD_GENERAL_H #define CCCD_GENERAL_H #define NAME "cccd" #define VERSION "0.3beta4" #define RCFILE ".cccdrc" #define AUTHOR "Jochen Stein" #define EMAIL "jst@writeme.com" /* #define HOMEPAGE "http://home.pages.de/~jst/cccd/" */ #define HOMEPAGE "currently none" enum errors { OK, REMOTE_OK, LOCAL_OK, NOT_FOUND, NO_CONNECT, CONNECT_REFUSED, SERVER_ERROR, PARAM_ERROR }; enum lookup_protocol { CDDBP, HTTP }; enum lookup_order { NONE, LOCAL, REMOTE, LOCAL_REMOTE, REMOTE_LOCAL }; #endif /* CCCD_GENERAL_H */ cccd-0.3beta4.orig/icons.h0100400000175000017500000001440106567045105014066 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: icons.h,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ /* XPM */ static char *eject_icon[] = { /* columns rows colors chars-per-pixel */ "24 16 2 1", " c Gray0", ". c None", /* pixels */ "........................", "........................", "............ ..........", "........... .........", ".......... ........", "......... .......", "......... .......", "........ ......", "....... .....", "........................", ".... ...", ".... ...", ".... ...", "........................", "........................", "........................" }; /* XPM */ static char *next_icon[] = { /* columns rows colors chars-per-pixel */ "24 16 2 1", " c Gray0", ". c None", /* pixels */ "........................", "........................", ".. ........ ..........", ".. ...... ........", ".. .... ......", ".. ... .....", ".. . ...", ".. .", ".. .", ".. . ...", ".. ... .....", ".. .... ......", ".. ...... ........", ".. ........ ..........", "........................", "........................" }; /* XPM */ static char *pause_icon[] = { /* columns rows colors chars-per-pixel */ "24 16 2 1", " c Gray0", ". c None", /* pixels */ "........................", "........................", "........................", ".... ...... ....", ".... ...... ....", ".... ...... ....", ".... ...... ....", ".... ...... ....", ".... ...... ....", ".... ...... ....", ".... ...... ....", ".... ...... ....", ".... ...... ....", "........................", "........................", "........................" }; /* XPM */ static char *play_icon[] = { /* columns rows colors chars-per-pixel */ "24 16 2 1", " c Gray0", ". c None", /* pixels */ "........................", "........................", ".. ...................", ".. ................", ".. ............", ".. .........", ".. ......", ".. ..", ".. ..", ".. ......", ".. .........", ".. ............", ".. ................", ".. ...................", "........................", "........................" }; /* XPM */ static char *prev_icon[] = { /* columns rows colors chars-per-pixel */ "24 16 2 1", " c Gray0", ". c None", /* pixels */ "........................", "........................", ".......... ........ ..", "........ ...... ..", "...... .... ..", "..... ... ..", "... . ..", ". ..", ". ..", "... . ..", "..... ... ..", "...... .... ..", "........ ...... ..", ".......... ........ ..", "........................", "........................" }; /* XPM */ static char *stop_icon[] = { /* columns rows colors chars-per-pixel */ "24 16 2 1", " c Gray0", ". c None", /* pixels */ "........................", "........................", "........................", "...... ......", "...... ......", "...... ......", "...... ......", "...... ......", "...... ......", "...... ......", "...... ......", "...... ......", "...... ......", "........................", "........................", "........................" }; /* XPM */ static char *info_icon[] = { /* columns rows colors chars-per-pixel */ "16 32 2 1", " c Gray0", ". c None", /* pixels */ "................", "................", "................", "................", "....... .....", "...... ....", "...... ...", "...... ...", "...... ....", "....... ....", "........ .....", "..... ....", "..... ....", "..... ....", "...... ....", "....... ....", "....... ....", "....... ....", "....... ....", "....... ....", "....... ....", "....... ....", "....... ....", "....... ....", "...... ....", "..... ..", ".... ..", ".... ..", "................", "................", "................", "................" }; /* XPM */ static char *exit_icon[] = { /* columns rows colors chars-per-pixel */ "16 32 2 1", " c Gray0", ". c None", /* pixels */ "................", "................", "................", "................", "................", "................", "................", "................", "................", "................", "..... .....", "... ...", "... ...", ".. ..", ".. ..", ".. ..", ".. ..", ".. ..", ".. ..", "... ...", "... ...", "..... .....", "................", "................", "................", "................", "................", "................", "................", "................", "................", "................" }; cccd-0.3beta4.orig/info.c0100400000175000017500000006403307416151574013711 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: info.c,v 0.3.3.4 2002/01/06 22:44:52 jst Exp $ */ #include #include #include #include #include #include #include "general.h" #include "misc.h" #include "cd.h" #include "cddb.h" #include "options.h" #include "cccd.h" #include "info.h" /* global vars */ static struct info_dialog info; /* XX is pointer to original data, tmp_XX is local working copy */ static struct audio_cd *cd, tmp_cd; static struct options *opt, tmp_opt; static int info_changed; /* code for the info/options dialog */ int info_dialog_init(struct audio_cd *given_cd, struct options *given_opt) { GtkWidget *window, *mainbox, *notebook, *frame, *label, *button; if (info.open) /* only allow one info dialog to be open at a time */ return -1; info_changed = FALSE; /* first initialize the global vars */ opt = given_opt; /*save the current cd state for undos */ cd_clear(&tmp_cd); options_clear(&tmp_opt); options_copy(&tmp_opt, opt); info_dialog_set_cd(given_cd); info.selected_track = -1; info.open = 1; info.colormap = gdk_colormap_new(gdk_visual_get_best(), FALSE); info.playcolor = (GdkColor *) malloc(sizeof(GdkColor)); info.playcolor->red = info.playcolor->blue = info.playcolor->green = 58000; gdk_color_alloc(info.colormap, info.playcolor); info.notplaycolor = (GdkColor *) malloc(sizeof(GdkColor)); info.notplaycolor->red = info.notplaycolor->blue = info.notplaycolor->green = 35000; gdk_color_alloc(info.colormap, info.notplaycolor); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "cccd Infos"); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(info_dialog_destroy), NULL); mainbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(window), mainbox); notebook = gtk_notebook_new(); frame = init_cd_page(); label = gtk_label_new("CD"); gtk_widget_show(frame); gtk_widget_show(label); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label); frame = init_cddb_page(); label = gtk_label_new("Cddb"); gtk_widget_show(frame); gtk_widget_show(label); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label); frame = init_about_page(); label = gtk_label_new("About"); gtk_widget_show(frame); gtk_widget_show(label); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label); gtk_box_pack_start(GTK_BOX(mainbox), notebook, TRUE, TRUE, 0); gtk_widget_set_usize(notebook, 310, 390); gtk_widget_show(notebook); button = gtk_button_new_with_label("Close"); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window)); gtk_box_pack_start(GTK_BOX(mainbox), button, FALSE, TRUE, 0); gtk_widget_show(button); gtk_widget_show(mainbox); gtk_widget_set_usize(window, 320, 400); gtk_widget_show(window); return 1; } GtkWidget *init_cd_page(void) { GtkWidget *frame, *box, *clist, *entry, *button, *hbox; gchar *titles[2] = {"Track", "Title"}; frame = gtk_frame_new(""); box = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), box); entry = gtk_entry_new_with_max_length(80); info.title_entry = entry; gtk_signal_connect(GTK_OBJECT(entry), "activate", (GtkSignalFunc) title_changed, NULL); gtk_box_pack_start(GTK_BOX(box), entry, FALSE, TRUE, 0); gtk_widget_show(entry); clist = gtk_clist_new_with_titles(2, titles); gtk_clist_column_titles_passive(GTK_CLIST(clist)); info.clist = clist; gtk_widget_set_events(clist, gtk_widget_get_events(clist) | GDK_BUTTON_PRESS_MASK); gtk_signal_connect(GTK_OBJECT(clist), "select_row", (GtkSignalFunc) track_selected, NULL); gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_SINGLE); gtk_clist_set_column_width(GTK_CLIST(clist), 0, 40); gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER); gtk_clist_set_column_width(GTK_CLIST(clist), 1, 250); gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_LEFT); gtk_box_pack_start(GTK_BOX(box), clist, TRUE, TRUE, 0); gtk_widget_set_usize(clist, 310, 290); gtk_widget_show(clist); entry = gtk_entry_new_with_max_length(80); info.track_entry = entry; gtk_signal_connect(GTK_OBJECT(entry), "activate", (GtkSignalFunc) track_changed, NULL); gtk_box_pack_start(GTK_BOX(box), entry, FALSE, TRUE, 0); gtk_widget_show(entry); hbox = gtk_hbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); button = gtk_button_new_with_label("Save"); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(cd_save), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_widget_show(button); info.send_button = button = gtk_button_new_with_label("Send"); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(cd_send), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_widget_set_sensitive(button, FALSE); gtk_widget_show(button); button = gtk_button_new_with_label("Undo"); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(cd_undo), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_widget_show(button); gtk_widget_show(hbox); cd_page_display(); gtk_widget_show(box); return frame; } GtkWidget *init_cddb_page(void) { GtkWidget *frame, *box, *section, *section_box, *hbox, *entry, *label, *radiobutton, *checkbutton, *button; GSList *group; frame = gtk_frame_new(""); box = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), box); /* Remote section */ section = gtk_frame_new("Remote"); section_box = gtk_vbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(section), section_box); hbox = gtk_hbox_new(FALSE, 0); label = gtk_label_new("Server:"); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 5); gtk_widget_show(label); info.server_entry = entry = gtk_entry_new(); gtk_signal_connect(GTK_OBJECT(entry), "focus_out_event", (GtkSignalFunc) server_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 2); gtk_widget_show(entry); label = gtk_label_new("Port:"); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 5); gtk_widget_show(label); info.port_entry = entry = gtk_entry_new(); gtk_signal_connect(GTK_OBJECT(entry), "focus_out_event", (GtkSignalFunc) port_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 2); /* gtk_widget_set_usize( entry, 100, info.server_entry->allocation.height ); */ gtk_widget_show(entry); gtk_box_pack_start(GTK_BOX(section_box), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); hbox = gtk_hbox_new(FALSE, 0); label = gtk_label_new("Submit email address:"); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 5); gtk_widget_show(label); info.remote_submit_entry = entry = gtk_entry_new(); gtk_signal_connect(GTK_OBJECT(entry), "focus_out_event", (GtkSignalFunc) remote_submit_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 2); gtk_widget_show(entry); gtk_box_pack_start(GTK_BOX(section_box), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); hbox = gtk_hbox_new(TRUE, 0); info.cddbp_radio = radiobutton = gtk_radio_button_new_with_label(NULL, "CDDB"); gtk_signal_connect(GTK_OBJECT(radiobutton), "toggled", (GtkSignalFunc) protocol_changed, NULL); group = gtk_radio_button_group(GTK_RADIO_BUTTON(radiobutton)); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, TRUE, 0); gtk_widget_show(radiobutton); info.http_radio = radiobutton = gtk_radio_button_new_with_label(group, "HTTP"); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, TRUE, 0); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(section_box), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); gtk_widget_show(section_box); gtk_box_pack_start(GTK_BOX(box), section, TRUE, TRUE, 0); gtk_widget_show(section); /* Local section */ section = gtk_frame_new("Local"); section_box = gtk_vbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(section), section_box); hbox = gtk_hbox_new(FALSE, 0); label = gtk_label_new("Path:"); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 5); gtk_widget_show(label); info.path_entry = entry = gtk_entry_new(); gtk_signal_connect(GTK_OBJECT(entry), "focus_out_event", (GtkSignalFunc) path_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 2); gtk_widget_show(entry); gtk_box_pack_start(GTK_BOX(section_box), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); gtk_widget_show(section_box); gtk_box_pack_start(GTK_BOX(box), section, TRUE, TRUE, 0); gtk_widget_show(section); /* Global Section */ section = gtk_frame_new("Global"); section_box = gtk_vbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(section), section_box); hbox = gtk_hbox_new(TRUE, 0); label = gtk_label_new("Lookup order"); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(section_box), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); /* add signal connects to lookup_changed */ hbox = gtk_hbox_new(FALSE, 0); info.lr_radio = radiobutton = gtk_radio_button_new_with_label(NULL, "Local, Remote"); group = gtk_radio_button_group(GTK_RADIO_BUTTON(radiobutton)); gtk_signal_connect(GTK_OBJECT(radiobutton), "toggled", (GtkSignalFunc) lookup_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, TRUE, 0); gtk_widget_show(radiobutton); info.rl_radio = radiobutton = gtk_radio_button_new_with_label(group, "Remote, Local"); group = gtk_radio_button_group(GTK_RADIO_BUTTON(radiobutton)); gtk_signal_connect(GTK_OBJECT(radiobutton), "toggled", (GtkSignalFunc) lookup_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, TRUE, 0); gtk_widget_show(radiobutton); info.n_radio = radiobutton = gtk_radio_button_new_with_label(group, "None"); group = gtk_radio_button_group(GTK_RADIO_BUTTON(radiobutton)); gtk_signal_connect(GTK_OBJECT(radiobutton), "toggled", (GtkSignalFunc) lookup_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, TRUE, 0); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(section_box), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); hbox = gtk_hbox_new(FALSE, 0); info.l_radio = radiobutton = gtk_radio_button_new_with_label(group, "Local"); group = gtk_radio_button_group(GTK_RADIO_BUTTON(radiobutton)); gtk_signal_connect(GTK_OBJECT(radiobutton), "toggled", (GtkSignalFunc) lookup_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, TRUE, 0); gtk_widget_show(radiobutton); info.r_radio = radiobutton = gtk_radio_button_new_with_label(group, "Remote"); gtk_signal_connect(GTK_OBJECT(radiobutton), "toggled", (GtkSignalFunc) lookup_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), radiobutton, FALSE, TRUE, 0); gtk_widget_show(radiobutton); gtk_box_pack_start(GTK_BOX(section_box), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); hbox = gtk_hbox_new(TRUE, 0); info.local_save_button = checkbutton = gtk_check_button_new_with_label("Save results of remote lookup?"); gtk_signal_connect(GTK_OBJECT(checkbutton), "toggled", (GtkSignalFunc) save_changed, NULL); gtk_box_pack_start(GTK_BOX(hbox), checkbutton, FALSE, TRUE, 0); gtk_widget_show(checkbutton); gtk_box_pack_start(GTK_BOX(section_box), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); gtk_widget_show(section_box); gtk_box_pack_start(GTK_BOX(box), section, TRUE, TRUE, 0); gtk_widget_show(section); hbox = gtk_hbox_new(TRUE, 0); button = gtk_button_new_with_label("Save"); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(cddb_save), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_widget_show(button); button = gtk_button_new_with_label("Undo"); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(cddb_undo), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); /* set the values of the widgets */ cddb_page_display(); gtk_widget_show(box); return frame; } GtkWidget *init_about_page(void) { GtkWidget *frame, *box, *label; char versionstring[256]; frame = gtk_frame_new(""); box = gtk_vbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), box); sprintf(versionstring, "%s %s\nA CDDB capable CD player\n\n(C) 1998 %s\n%s\n%s", NAME, VERSION, AUTHOR, EMAIL, HOMEPAGE); label = gtk_label_new(versionstring); gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 5); gtk_widget_show(label); gtk_widget_show(box); gtk_widget_show(frame); return frame; } void info_dialog_destroy(GtkWidget * widget, gpointer data) { gtk_widget_hide(widget); /* free the color sometime */ gdk_colormap_unref(info.colormap); info.open = 0; } void info_dialog_set_cd(struct audio_cd *given_cd) { cd = given_cd; audio_cd_copy(&tmp_cd, cd); } int info_dialog_open(void) { return info.open; } void track_selected(GtkCList * clist, gint row, gint column, GdkEventButton * event) { info.selected_track = row + tmp_cd.first_track; if (event != NULL && event->type == GDK_2BUTTON_PRESS) { cd->current_track = info.selected_track; play_callback(NULL, NULL); } else { /* handle playlist stuff */ if (!info.enter_pressed) { if (tmp_cd.toc[row + tmp_cd.first_track].play) gtk_clist_set_background(clist, row, info.notplaycolor); else gtk_clist_set_background(clist, row, info.playcolor); tmp_cd.toc[row + tmp_cd.first_track].play ^= 1; } info.enter_pressed = 0; } if (tmp_cd.toc[info.selected_track].title != NULL) gtk_entry_set_text(GTK_ENTRY(info.track_entry), (gchar *) (tmp_cd.toc[info.selected_track].title)); else gtk_entry_set_text(GTK_ENTRY(info.track_entry), ""); } void track_changed(GtkEntry * entry) { int row = info.selected_track - tmp_cd.first_track; char *title = tmp_cd.toc[info.selected_track].title, *newtitle; newtitle = gtk_entry_get_text(entry); info.enter_pressed = 1; if (newtitle != NULL && title != NULL) { if (strcmp(newtitle, title) == 0) { /* ignore unneccessary changes */ /* but still jump to next row */ gtk_clist_select_row(GTK_CLIST(info.clist), row + 1, 1); return; } } if (title != NULL) free(title); title = (newtitle == NULL) ? strdup("") : strdup(newtitle); tmp_cd.toc[info.selected_track].title = title; /* after taking the changes, update the clist */ gtk_clist_set_text(GTK_CLIST(info.clist), row, 1, title); /* and jump to next track to make entering cd data easier */ gtk_clist_select_row(GTK_CLIST(info.clist), row + 1, 1); info_changed = TRUE; } void title_changed(GtkEntry * entry) { char *newtitle; newtitle = gtk_entry_get_text(entry); if (tmp_cd.title != NULL && newtitle != NULL) if (strcmp(tmp_cd.title, newtitle) == 0) return; if (tmp_cd.title != NULL) free(tmp_cd.title); tmp_cd.title = (newtitle == NULL) ? strdup("") : strdup(newtitle); info_changed = TRUE; } void server_changed(GtkEntry * entry) { char *text; if (tmp_opt.server != NULL) free(tmp_opt.server); text = gtk_entry_get_text(entry); if (strcmp(text, "") != 0) tmp_opt.server = strdup(text); } void port_changed(GtkEntry * entry) { char *text; text = gtk_entry_get_text(entry); if (strcmp(text, "") != 0) { tmp_opt.port = 0; while (*text != 0 && isdigit(*text)) { tmp_opt.port *= 10; tmp_opt.port += *text - 0x30; text++; } } } void remote_submit_changed(GtkEntry * entry) { char *text; text = gtk_entry_get_text(entry); if (strcmp(text, "") != 0) { if (tmp_opt.remote_submit != NULL) { free(tmp_opt.remote_submit); } tmp_opt.remote_submit = strdup(text); } } void path_changed(GtkEntry * entry) { char *text; if (tmp_opt.path != NULL) free(tmp_opt.path); text = gtk_entry_get_text(entry); if (strcmp(text, "") != 0) tmp_opt.path = strdup(text); } void protocol_changed(GtkToggleButton * toggle_button) { if (toggle_button->active) { tmp_opt.protocol = CDDBP; tmp_opt.port = 888; gtk_entry_set_text(GTK_ENTRY(info.port_entry), "888"); } else { tmp_opt.protocol = HTTP; tmp_opt.port = 80; gtk_entry_set_text(GTK_ENTRY(info.port_entry), "80"); } } void lookup_changed(GtkToggleButton * toggle_button) { if (toggle_button == GTK_TOGGLE_BUTTON(info.rl_radio)) tmp_opt.lookup = REMOTE_LOCAL; else if (toggle_button == GTK_TOGGLE_BUTTON(info.lr_radio)) tmp_opt.lookup = LOCAL_REMOTE; else if (toggle_button == GTK_TOGGLE_BUTTON(info.l_radio)) tmp_opt.lookup = LOCAL; else if (toggle_button == GTK_TOGGLE_BUTTON(info.r_radio)) tmp_opt.lookup = REMOTE; else tmp_opt.lookup = NONE; } void save_changed(GtkToggleButton * toggle_button) { if (toggle_button->active) tmp_opt.save = TRUE; else tmp_opt.save = FALSE; } void cd_undo(GtkWidget * widget, gpointer data) { /* undo the changes in the struct */ audio_cd_copy(&tmp_cd, cd); /* and now in the widgets as well */ cd_page_display(); info.selected_track = -1; } void cd_save(GtkWidget * widget, gpointer data) { audio_cd_copy(cd, &tmp_cd); save_cd(cd, opt->path); } void save_cd(struct audio_cd *given_cd, const char *path) { int cat_num, i; char *buffer = NULL, *final_cat = NULL, *filename, **category; FILE *file; if (path == NULL || given_cd == NULL) return; given_cd->revision++; cddb_collate_data(given_cd, &buffer); /* take the opt info, as this is the last confirmed data entry */ category = (char **) malloc(1000 * sizeof(char *)); if ((cat_num = get_subdirs(path, category)) == 0) { free(category); return; } i = alternate_list(category, cat_num); if (i != cat_num) { /* user wants to save */ final_cat = category[i]; filename = (char *) malloc(strlen(final_cat) + 8 + 2); sprintf(filename, "%s/%s", final_cat, given_cd->id); file = fopen(filename, "wt"); if (file == NULL) { g_warning("Error opening file %s\n", filename); return; } fputs(buffer, file); fflush(file); fclose(file); } /* end of saving */ /* clean up */ for (i = 0; i < cat_num; i++) { free(category[i]); } free(category); free(buffer); /* last thing: check to see if the cd may be submitted via email or not */ /* it is required to have proper title */ if (given_cd->title == NULL || strlen(given_cd->title) < 1 || info_changed == FALSE) return; /* it is required to have at least one track name */ for (i = given_cd->first_track; i < given_cd->last_track + 1; i++) { if (given_cd->toc[i].title != NULL && strlen(given_cd->toc[i].title) > 0) { gtk_widget_set_sensitive(info.send_button, TRUE); return; } } /* if control reaches here, the entry may not be submitted */ gtk_widget_set_sensitive(info.send_button, FALSE); } void cd_send(GtkWidget * widget, gpointer sdata) { int matches, i; char *command, *buffer = NULL, *subject = NULL, *final_cat; char **category = (char **) malloc(sizeof(char *) * 1000); FILE *stream; /* check is CDDB submit address given */ if ((opt->remote_submit == NULL) || (strlen(opt->remote_submit) < 1)) { g_warning("Please enter CDDB submit address in config, send aborted."); return; } /* get the remote databases categories */ i = cddb_remote_categories(opt->server, opt->port, opt->protocol, category, &matches); if (i != REMOTE_OK) { g_warning("Cannot determine remote categories, send aborted."); return; } i = alternate_list(category, matches); if (i != matches) { /* user wants to send */ final_cat = category[i]; /* don't compute the id again. we compute it once after disk logged in */ /* prepare the mail */ cddb_collate_data(cd, &buffer); command = strdup(MAILPROG); subject = (char *) malloc(strlen(final_cat) + strlen(cd->id) + 20); sprintf(subject, " -s\"cddb %s %s\" ", final_cat, cd->id); string_append(&command, subject); free(subject); string_append(&command, opt->remote_submit); stream = popen(command, "w"); fprintf(stream, "%s\r\n", buffer); pclose(stream); free(buffer); } /* end of sending */ /* clean up */ for (i = 0; i < matches; i++) { if (category[i] != NULL) free(category[i]); } free(category); gtk_widget_set_sensitive(info.send_button, FALSE); } void cd_page_display(void) { char *track_string = (char *) malloc(4), *row[2]; int i, max_track = tmp_cd.last_track - tmp_cd.first_track + 1, track; if (tmp_cd.title != NULL) { gtk_entry_set_text(GTK_ENTRY(info.title_entry), (gchar *) tmp_cd.title); gtk_entry_set_position(GTK_ENTRY(info.title_entry), 0); } else gtk_entry_set_text(GTK_ENTRY(info.title_entry), ""); row[0] = track_string; gtk_clist_clear(GTK_CLIST(info.clist)); for (i = 0; i < max_track; i++) { track = i + tmp_cd.first_track; sprintf(row[0], "%02d", track); if (tmp_cd.toc[track].title != NULL) row[1] = (gchar *) strdup(tmp_cd.toc[track].title); else row[1] = strdup(""); gtk_clist_append(GTK_CLIST(info.clist), row); free(row[1]); if (tmp_cd.toc[track].play) gtk_clist_set_background(GTK_CLIST(info.clist), i, info.playcolor); else gtk_clist_set_background(GTK_CLIST(info.clist), i, info.notplaycolor); } free(row[0]); gtk_entry_set_text(GTK_ENTRY(info.track_entry), ""); } void cddb_undo(GtkWidget * widget, gpointer data) { options_copy(&tmp_opt, opt); cddb_page_display(); } void cddb_save(GtkWidget * widget, gpointer data) { options_copy(opt, &tmp_opt); options_save(opt); } void cddb_page_display(void) { char *dud = (char *) malloc(20); if (tmp_opt.server != NULL) { gtk_entry_set_text(GTK_ENTRY(info.server_entry), tmp_opt.server); gtk_entry_set_position(GTK_ENTRY(info.server_entry), 0); } else gtk_entry_set_text(GTK_ENTRY(info.server_entry), ""); sprintf(dud, "%d", tmp_opt.port); gtk_entry_set_text(GTK_ENTRY(info.port_entry), dud); gtk_entry_set_position(GTK_ENTRY(info.port_entry), 0); if (tmp_opt.remote_submit != NULL) { gtk_entry_set_text(GTK_ENTRY(info.remote_submit_entry), tmp_opt.remote_submit); gtk_entry_set_position(GTK_ENTRY(info.remote_submit_entry), 0); } if (tmp_opt.protocol == CDDBP) gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.cddbp_radio), TRUE); else gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.http_radio), TRUE); if (tmp_opt.path != NULL) { gtk_entry_set_text(GTK_ENTRY(info.path_entry), tmp_opt.path); gtk_entry_set_position(GTK_ENTRY(info.path_entry), 0); } else gtk_entry_set_text(GTK_ENTRY(info.path_entry), ""); if (tmp_opt.save) gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.local_save_button), TRUE); else gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.local_save_button), FALSE); switch (tmp_opt.lookup) { case REMOTE_LOCAL: gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.rl_radio), TRUE); break; case LOCAL_REMOTE: gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.lr_radio), TRUE); break; case LOCAL: gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.l_radio), TRUE); break; case REMOTE: gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.r_radio), TRUE); break; default: gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(info.n_radio), TRUE); break; } free(dud); } int alternate_selected; /* the alternative selected by the user */ int alternate_list(char **title, int matches) /* returns matches, i.e. out of bounds value if no selection made */ { int i; char *row; GtkWidget *window, *box, *clist, *button; alternate_selected = matches; window = gtk_window_new(GTK_WINDOW_DIALOG); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(alternate_quit), NULL); gtk_window_set_title(GTK_WINDOW(window), "Select Category"); gtk_widget_set_usize(window, 360, 250); box = gtk_vbox_new(FALSE, 0); clist = gtk_clist_new(1); gtk_signal_connect(GTK_OBJECT(clist), "select_row", (GtkSignalFunc) alternate_select, NULL); for (i = 0; i < matches; i++) { row = (char *) malloc(strlen(title[i]) + 4); sprintf(row, "%d %s", i + 1, title[i]); gtk_clist_append(GTK_CLIST(clist), &row); free(row); } gtk_box_pack_start(GTK_BOX(box), clist, TRUE, TRUE, 0); gtk_widget_show(clist); button = gtk_button_new_with_label("OK"); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window)); gtk_box_pack_start(GTK_BOX(box), button, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(window), box); gtk_widget_show(button); gtk_widget_show(box); gtk_widget_show(window); gtk_grab_add(window); gtk_main(); return alternate_selected; } void alternate_select(GtkCList * clist, gint row, gint column, GdkEventButton * event) { alternate_selected = (int) row; } void alternate_quit(GtkWidget * widget, gpointer data) { /* it is necessary to call gtk_grab_remove() before gtk_main_quit(). * if not, the widgets are already destroyed and we segfault */ gtk_grab_remove(widget); gtk_main_quit(); } cccd-0.3beta4.orig/info.h0100400000175000017500000000533306637252062013712 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: info.h,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ #ifndef CCCD_INFO_H #define CCCD_INFO_H struct info_dialog { GtkWidget *window, *send_button, *clist, *track_entry, *title_entry; int selected_track, open, enter_pressed; GtkWidget *server_entry, *port_entry, *remote_submit_entry, *path_entry, *local_save_button, *cddbp_radio, *http_radio, *rl_radio, *lr_radio, *l_radio, *r_radio, *n_radio; GdkColormap *colormap; GdkColor *notplaycolor, *playcolor; }; int info_dialog_init(struct audio_cd *cd, struct options *opt); GtkWidget *init_cd_page(void); GtkWidget *init_cddb_page(void); GtkWidget *init_about_page(void); void info_dialog_destroy(GtkWidget *, gpointer); int info_dialog_open(void); void info_dialog_set_cd(struct audio_cd *cd); void title_changed(GtkEntry * entry); void track_selected(GtkCList * clist, gint row, gint column, GdkEventButton * event); void track_changed(GtkEntry * entry); void server_changed(GtkEntry * entry); void port_changed(GtkEntry * entry); void remote_submit_changed(GtkEntry * entry); void path_changed(GtkEntry * entry); void protocol_changed(GtkToggleButton * toggle_button); void lookup_changed(GtkToggleButton * toggle_button); void save_changed(GtkToggleButton * toggle_button); void cd_undo(GtkWidget * widget, gpointer data); void cd_save(GtkWidget * widget, gpointer data); void save_cd(struct audio_cd *cd, const char *path); void cd_send(GtkWidget * widget, gpointer data); void cd_page_display(void); void cddb_undo(GtkWidget * widget, gpointer data); void cddb_save(GtkWidget * widget, gpointer data); void cddb_page_display(void); int alternate_list(char **title, int matches); void alternate_select(GtkCList * clist, gint row, gint column, GdkEventButton * event); void alternate_quit(GtkWidget * windget, gpointer data); #endif /* CCCD_INFO_H */ cccd-0.3beta4.orig/misc.c0100600000175000017500000001433607416145203013704 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: misc.c,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp jst $ */ #include #include #include #include #include #include #include #include #include #include char *get_string_piece(FILE * file, int delim) { /* gets one complete row from 'file' and save it in 'buffer'. buffer's memory will be freed and allocated to fit the stringsize automatically. */ char *buffer1 = (char *) malloc(1), *buffer2 = (char *) malloc(1), *tmp = (char *) malloc(1024); char **active, **inactive = NULL; int i = 0; strcpy(buffer1, ""); strcpy(buffer2, ""); strcpy(tmp, ""); do { /*switch the frames */ if (inactive == &buffer1) { active = &buffer1; inactive = &buffer2; } else { active = &buffer2; inactive = &buffer1; } /*get the next part, and handle EOF */ if (fgets(tmp, 1024, file) == NULL) { /* ok, so we reached the end of the file w/o finding the delimiter */ free(*active); return NULL; } free(*active); *active = (char *) malloc((++i) * 1024); sprintf(*active, "%s%s", *inactive, tmp); } while (strchr(*active, delim) == NULL); free(*inactive); return *active; } char *get_ascii_file(FILE * file) { /* gets one complete row from 'file' and save it in 'buffer'. buffer's memory will be freed and allocated to fit the stringsize automatically. */ char *buffer1 = (char *) malloc(1), *buffer2 = (char *) malloc(1), *tmp = (char *) malloc(1024); char **active, **inactive = NULL; int i = 0; strcpy(buffer1, ""); strcpy(buffer2, ""); strcpy(tmp, ""); do { /*switch the frames */ if (inactive == &buffer1) { active = &buffer1; inactive = &buffer2; } else { active = &buffer2; inactive = &buffer1; } /*get the next part, and handle EOF */ if (fgets(tmp, 1024, file) == NULL) { free(*active); return *inactive; } free(*active); *active = (char *) malloc((++i) * 1024); sprintf(*active, "%s%s", *inactive, tmp); } while (1); } void strip_trailing_space(char **string) { int i = strlen(*string) - 1; char *return_string; if (string == NULL || *string == NULL) return; while (isspace((*string)[i])) i--; i++; return_string = (char *) malloc(i + 1); strncpy(return_string, *string, i); return_string[i] = 0; free(*string); *string = return_string; } void strip_leading_space(char **string) { char *tmp = *string, *return_string; if (string == NULL || *string == NULL) return; while (isspace(*tmp)) tmp++; return_string = strdup(tmp); free(*string); *string = return_string; } char *string_append(char **dest, char *appendage) { char *holder; if (dest == NULL || appendage == NULL) return NULL; if (*dest != NULL) { holder = *dest; *dest = (char *) malloc(strlen(holder) + strlen(appendage) + 1); sprintf(*dest, "%s%s", holder, appendage); free(holder); } else *dest = strdup(appendage); return *dest; } void charpp_to_charp(char **dest, char **src, int num, char *separator) { int i, size = 0, sep_size = 0; if (src == NULL || num == 0) return; if (separator == NULL) separator = strdup(""); sep_size = strlen(separator); for (i = 0; i < num; i++) size += strlen(src[i]) + sep_size; if (*dest != NULL) free(*dest); *dest = (char *) malloc(size + 1); strcpy(*dest, ""); for (i = 0; i < num - 1; i++) { strcat(*dest, src[i]); strcat(*dest, separator); } /* do it this way, so no separator will get in as the last part */ strcat(*dest, src[num - 1]); } FILE *socket_init(const char *server, short int port) { struct hostent *host; struct sockaddr_in socket_address; int hsocket; /* see if some default values have been set */ if (server == NULL) { return NULL; } host = gethostbyname(server); if (host == NULL) return NULL; /* we don't know the host */ /* set socket address */ memset(&socket_address, 0, sizeof(socket_address)); memcpy((char *) &socket_address.sin_addr, host->h_addr, host->h_length); socket_address.sin_family = host->h_addrtype; socket_address.sin_port = htons(port); /* get the actual socket handle */ hsocket = socket(host->h_addrtype, SOCK_STREAM, 0); if (hsocket < 0) /* we couldn't grab the socket */ return NULL; if (connect(hsocket, (struct sockaddr *) &socket_address, sizeof(socket_address)) < 0) return NULL; return fdopen(hsocket, "r+"); /* we made it */ } int get_subdirs(const char *path, char **buffer) { int hits = 0, status; char *subdirname; DIR *dirp; struct dirent *dir_contents; struct stat file_stats; if (path == NULL || (dirp = opendir(path)) == NULL) { return hits; } while ((dir_contents = readdir(dirp)) != NULL) { if (strcmp(dir_contents->d_name, ".") != 0 && strcmp(dir_contents->d_name, "..") != 0) { subdirname = (char *) malloc(strlen(path) + strlen(dir_contents->d_name) + 2); sprintf(subdirname, "%s/%s", path, dir_contents->d_name); status = stat(subdirname, &file_stats); if (status == 0 && hits < 1000) { if (S_ISDIR(file_stats.st_mode)) { /* we are a subdir */ buffer[hits] = strdup(subdirname); hits++; } } free(subdirname); } } return hits; } cccd-0.3beta4.orig/misc.h0100400000175000017500000000265506567045106013717 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: misc.h,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ #ifndef CCCD_MISC_H #define CCCD_MISC_H char *get_string_piece(FILE * file, int delim); char *get_ascii_file(FILE * file); void strip_trailing_space(char **string); void strip_leading_space(char **string); char *string_append(char **dest, char *appendage); void charpp_to_charp(char **dest, char **src, int num, char *separator); FILE *socket_init(const char *server, short int port); int get_subdirs(const char *path, char **buffer); #endif /* CCCD_MISC_H */ cccd-0.3beta4.orig/options.c0100400000175000017500000001557707416151615014456 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: options.c,v 0.3.3.2 2002/01/06 22:44:52 jst Exp $ */ #include #include #include #include #include "general.h" #include "misc.h" #include "options.h" int options_copy(struct options *dest, struct options *src) { if (dest == NULL || src == NULL) return -1; /* copy the server */ if (dest->server != NULL) free(dest->server); dest->server = NULL; if (src->server != NULL) { dest->server = strdup(src->server); } dest->port = src->port; dest->save = src->save; /* path */ if (dest->path != NULL) free(dest->path); dest->path = NULL; if (src->path != NULL) { dest->path = strdup(src->path); } if (dest->remote_submit != NULL) free(dest->remote_submit); dest->remote_submit = NULL; if (src->remote_submit != NULL) dest->remote_submit = strdup(src->remote_submit); dest->protocol = src->protocol; dest->lookup = src->lookup; return 1; } int options_clear(struct options *o) { o->server = NULL; o->remote_submit = NULL; o->path = NULL; o->port = 0; o->save = 0; o->protocol = CDDBP; o->lookup = NONE; return 1; } int options_load(struct options *o) { char *filepath, *string, *tmp; FILE *file; filepath = (char *) malloc(strlen(getenv("HOME")) + strlen(RCFILE) + 2); sprintf(filepath, "%s/%s", getenv("HOME"), RCFILE); file = fopen(filepath, "rt"); free(filepath); options_clear(o); if (file == NULL) { /* we couldn't open the config file. improvise */ printf("Couldn't open ~/%s! \n", RCFILE); o->server = (char *) malloc(strlen("freedb.freedb.org") + 1); strcpy(o->server, "freedb.freedb.org"); o->port = 8880; o->protocol = CDDBP; o->remote_submit = NULL; o->path = NULL; o->save = 0; o->lookup = NONE; } else { /* we have a rc file. lets parse it (ugh) */ while ((string = get_string_piece(file, '\n')) != NULL) { strip_trailing_space(&string); strip_leading_space(&string); /* got a row */ /* the keywords in the rc file are: SERVER: the cddb server PORT: the port number for the server PROTOCOL=[CDDB,HTTP]: the lookup protocol REMOTECAT: list of categories applicable for the remote database, separated by ';' PATH: path to the root dir of the local database LOOKUPORDER=[NONE, LOCAL, REMOTE, REMOTELOCAL, LOCALREMOTE] : the order of the databases to check SAVE=[TRUE, FALSE]: should remote looked up databases be saved locally? */ if (strncmp(string, "SERVER", 6) == 0) { /* we have a server! */ tmp = string + strcspn(string, "=") + 1; /* skip to the value part */ while (isspace(*tmp) && *tmp != 0) tmp++; o->server = strdup(tmp); } else if (strncmp(string, "PORT", 4) == 0) { /* port number */ tmp = string + strcspn(string, "=") + 1; /* skip to the value part */ while (!isdigit(*tmp) && *tmp != 0) tmp++; o->port = 0; while (isdigit(*tmp)) { o->port *= 10; o->port += *tmp - 0x30; tmp++; } } else if (strncmp(string, "PROTOCOL", 8) == 0) { /* port number */ tmp = string + strcspn(string, "=") + 1; /* skip to the value part */ while (isspace(*tmp) && *tmp != 0) tmp++; if (strncmp(tmp, "HTTP", 4) == 0) o->protocol = HTTP; else o->protocol = CDDBP; } else if (strncmp(string, "REMOTESUBMIT", 9) == 0) { /* port number */ tmp = string + strcspn(string, "=") + 1; /* skip to the value part */ while (isspace(*tmp) && *tmp != 0) tmp++; o->remote_submit = strdup(tmp); } else if (strncmp(string, "PATH", 4) == 0) { /* port number */ tmp = string + strcspn(string, "=") + 1; /* skip to the value part */ while (isspace(*tmp) && *tmp != 0) tmp++; o->path = strdup(tmp); } else if (strncmp(string, "LOOKUPORDER", 11) == 0) { /* port number */ tmp = string + strcspn(string, "=") + 1; /* skip to the value part */ while (isspace(*tmp) && *tmp != 0) tmp++; if (strncmp(tmp, "REMOTELOCAL", 11) == 0) o->lookup = REMOTE_LOCAL; else if (strncmp(tmp, "LOCALREMOTE", 11) == 0) o->lookup = LOCAL_REMOTE; else if (strncmp(tmp, "REMOTE", 6) == 0) o->lookup = REMOTE; else if (strncmp(tmp, "LOCAL", 5) == 0) o->lookup = LOCAL; else o->lookup = NONE; } else if (strncmp(string, "SAVE", 4) == 0) { /* port number */ tmp = string + strcspn(string, "=") + 1; /* skip to the value part */ while (isspace(*tmp) && *tmp != 0) tmp++; if (strncmp(tmp, "TRUE", 4) == 0) o->save = 1; else o->save = 0; } free(string); } fclose(file); options_check(o); /* check for balant mistakes */ } return 1; } int options_save(struct options *o) { char *filepath; FILE *file; filepath = (char *) malloc(strlen(getenv("HOME")) + strlen(RCFILE) + 2); sprintf(filepath, "%s/%s", getenv("HOME"), RCFILE); file = fopen(filepath, "wt"); free(filepath); if (file == NULL) { printf("Could not open ~/%s for writing! \n", RCFILE); return -1; } if (o->server != NULL) fprintf(file, "SERVER=%s\n", o->server); if (o->port != 0) fprintf(file, "PORT=%d\n", o->port); if (o->protocol == HTTP) fprintf(file, "PROTOCOL=HTTP\n"); else fprintf(file, "PROTOCOL=CDDBP\n"); if (o->remote_submit != NULL) fprintf(file, "REMOTESUBMIT=%s\n", o->remote_submit); if (o->path != NULL) fprintf(file, "PATH=%s\n", o->path); switch (o->lookup) { case LOCAL_REMOTE: fprintf(file, "LOOKUPORDER=LOCALREMOTE\n"); break; case REMOTE_LOCAL: fprintf(file, "LOOKUPORDER=REMOTELOCAL\n"); break; case LOCAL: fprintf(file, "LOOKUPORDER=LOCAL\n"); break; case REMOTE: fprintf(file, "LOOKUPORDER=REMOTE\n"); break; default: fprintf(file, "LOOKUPORDER=NONE\n"); break; } if (o->save) fprintf(file, "SAVE=TRUE\n"); else fprintf(file, "SAVE=FALSE\n"); fflush(file); fclose(file); return 1; } int options_check(struct options *o) { /* TODO: check for mistakes/inconsistencies in the structure */ return 1; } cccd-0.3beta4.orig/options.h0100400000175000017500000000271006567045107014450 0ustar uh1763uh1763/* This file is part of cccd - a CDDB capable CD player Copyright (c) 1998 by Jochen Stein, jst@writeme.com Copyright (c) 1998 by Sven Riedel, Sven.Riedel@heim8.tu-clausthal.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (See the included file COPYING / GPL-2.0) $Id: options.h,v 0.3.3.1 1998/08/20 16:09:09 jochen Exp $ */ #ifndef CCCD_OPTIONS_H #define CCCD_OPTIONS_H struct options { char *server, *remote_submit, *path; int port, save; enum lookup_protocol protocol; enum lookup_order lookup; }; int options_copy(struct options *dest, struct options *src); int options_clear(struct options *o); int options_load(struct options *o); int options_save(struct options *o); int options_check(struct options *o); void parse_categories(char *string, char ***cat, int *num); #endif /* CCCD_OPTIONS_H */ cccd-0.3beta4.orig/README0100600000175000017500000000265107416152052013462 0ustar uh1763uh1763CCCD 0.3beta4 This is a maintenance release. Sven Riedel, the original author has passed cccd over to me, and I have started working on it. Please refer to README.original for usage hints. Installation for RPM based systems: Use rpm -tb cccd-0.3beta4.tar.gz to build a binary RPM for cccd. Use rpm -i or your favourite tool to install. Installation without RPM: 1. Review the Makefile. Check the location of the "mail" command and choose the CFLAGS to suite your taste. 2. A different installation directory can be specified by using make install prefix=/your/dir Additional hints: 1. Make sure you have read permissions for the CD drive, not only for /dev/cdrom (usually a link) 2. If you want to store CDDB entries on your local disk, configure a path location via the CDDB tab of the info dialog (e.g. /home/yourname/.cddb). Create this path and some subdirectories, which will act as category folders. You do not need to follow the original CDDB categories. Limitations: o CDDB related GUI elements need checking (done) o spits out tons of ugly debug messages (switched off in Makefile) o handling of multisession CDs needs more testing o no CDDB through a proxy/firewall Known bugs: Yes, there are some. They used to be on http://home.pages.de/~jst/cccd/bugs.html. If you find a bug or problem, please report it to me using the form found in BUGREPORT. Also coding help is welcome. Jochen A. Stein jst@writeme.com cccd-0.3beta4.orig/README.original0100600000175000017500000000643006560677737015311 0ustar uh1763uh1763CCCD (formerly tcd) 0.3alpha A Word from me: You may wonder why I changed the name from tcd to cccd. This is because I found that another cd player also had the name tcd. My appologies to the other of the original tcd. Furthermore, I'd like to thank all the people on byxnet's #gimp channel for their help and patience with me. Installation: 'make' and then copy the executable into a directory which is in your PATH statement. Also make sure that the device /dev/cdrom links to is world readable. Requirements: - Gtk 0.99.3 (maybe higher?), available from ftp.gimp.org/pub/gtk - Linux 2.0 or higher - a CD ROM drive linked to /dev/cdrom Features: - CDDB database support for (local and remote) lookups - CDDB submissions (remote) and saving (locally) - Normal, CD Loop, Track Look, Random Playmodes - Absolute and Track time remaining and passed Timemodes - Local Saving of a CD - background lookup of a CD Planned Features: - Interactive alternative handling - nicer GUI and buttons (anyone feel responsible? :) - general code cleanups Known Bugs: Save Remote Lookups likes to save after a local lookup as well. "Manual": Upon starting cccd two windows will pop up: a smaller control panel and a status window. The Control Panel: Here are the buttons for controlling the CD drive. The main action will take place here. The left most button is the exit button, which will cause (guess?) CCCD to quit. The right most button will cause the Info window (q.v.) to pop up. Buttons in the top row (left to right): Previous Track, Play, Next Track, and in the bottom row: Pause, Stop and Eject. Their functions should be self- explanitory. The Status Window: You will see four rows of text here, and a progress bar below that. The top most row shows the Artist and Title of the CD, if known. Below that is the track title. Then comes the time display and then a status row. You can toggle the time display between - CD time passed - CD time remaining, - Track time passed, - Track time remaining by clicking on the window above the progress bar. The status information will show the current status and the play mode. You can toggle the playmodes (Normal, Loop Track, Loop CD, Random) by clicking on the progress bar. The progress bar will always show the elapsed CD time. The Info Window: This window is divided into two sections: - CD: Here you can see (and later enter) the CD title and Artist, followed by a list of the Track titles. Below this there are button that (will) allow you to Save the current CD data, submit it to a remote CDDB database, and Undo any changes you have made since the last saving. You can realize a playlist of the tracks you want to hear, by clicking on the track row. Darker tracks will not be played. (Note: Data tracks will by 'unselected' automatically). -CDDB: You can enter the relevant data for remote and local cddb databases here, and the lookup order. If the remote databases lookup protocol is http, you will need to enter the URL in addidion to the hostname, without the leading "http://" (e.g. "example.url.com/cddb/database/lookup.cgi". The category section is not used, and will be omitted in future versions. "Save" will save the info to "~/.cccdrc". I'll leave to your imagination what Undo does. Comments and suggestions welcome. Sven Riedel Sven.Riedel@heim8.tu-clausthal.de cccd-0.3beta4.orig/CHANGES0100600000175000017500000000404707416150012013570 0ustar uh1763uh17630.3beta4: (not announced) - fixed initialisation of progress bar - got rid of some warnings - changed default to use freedb.org - wrote a kind of manpage 0.3beta3: (posted on freshmeat.net) - fixed compiling on kernel 2.1.1xx/glibc (thanks to Petteri Kangaslampi) - longer track titles are now supported - no more GTK+-warnings - some bugs fixed - RPM spec file supplied 0.3alpha2: (posted on freshmeat.net) - cleaned up dead childs (no more zombies) - more robust when started without CD in drive - made saved CDDB entries comply with CDDB file format - Track remain display was off by about 2sec - fixed playmodes, cleaned up in update_display() - minor Makefile changes - added stuff for handling of mixed mode CDs (CD-Extra, CD-Plus) - cleaned up CDDB disk id calculation, tray open detection 0.3alpha1: (posted on freshmeat.net) - version was chosen badly for last release - mistyped URL in about tab - rudimentary install target in Makefile - program crashed when closing display window, fixed - CDDB submit and local save can be aborted by closing category select window - display window was not updated after CDDB lookup completed - program would submit bad entry for CDDB, sanity checks - fixed hangs after ejecting CD and then trying to play 0.3pre1: (not widely released) - took over from Sven Riedel - cleaned up Makefile - playing without PLAYTRKIND was broken, rudimentary fixes (needs more work) - display window not properly initialized at startup - lost stop functionality somehow (needs more work) - better look and more info for control and display windows - about-tab added to info dialog 0.3alpha: - Fixed playing for drives that don't support PLAYTRKIND - Fixed glitches that made cccd not work with the 2.1 kernel tree (thanks go to chexum for the diff) - Fixed bug in local lookups, that caused a lock - Fixed bug in the Save Dialog - Added Sending/Submit to Remote Database Support - Fixed bug in eject code - Changed name to "cccd" (CDDB Capable CD player) (ok, so you can see a cccp in there too. kill me) cccd-0.3beta4.orig/COPYING0100600000175000017500000004312706560677737013665 0ustar uh1763uh1763 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. cccd-0.3beta4.orig/Makefile0100600000175000017500000000331607416152517014247 0ustar uh1763uh1763SOURCES = cccd.c cd.c cddb.c cddbp.c info.c options.c misc.c HEADERS = $(SOURCES:.c=.h) general.h icons.h OBJECTS = cccd.o cd.o cddb.o cddbp.o info.o options.o misc.o OTHERS = Makefile CHANGES README TODO README.original BUGREPORT COPYING TARGET = cccd CC = gcc # you may need to change this MAILPROG = "/bin/mail -i" # If $(TARGET) spits out too many debug messages, remove -DDEBUG from # next line CFLAGS = -g -Wall `gtk-config --cflags` -DDEBUG #LDFLAGS = -lefence `gtk-config --libs` LDFLAGS = `gtk-config --libs` # production flags (no debugging) #CFLAGS = -O2 -Wall `gtk-config --cflags` #LDFLAGS = -s `gtk-config --libs` prefix=/usr/local bindir=$(prefix)/bin mandir=$(prefix)/man/man1 CFLAGS += -DMAILPROG="\"/bin/mail -i\"" .SUFFIXES: .c .c.o: $(CC) -c $(CFLAGS) $< all: $(TARGET) $(TARGET): $(OBJECTS) gcc -o $(TARGET) $(OBJECTS) $(CFLAGS) $(LDFLAGS) install: $(TARGET) mkdir -p $(bindir) install -c -s -m 0755 cccd $(bindir) mkdir -p $(mandir) install -c -m 0755 cccd.1 $(mandir) @echo "Please read the README for additional installation hints" static: $(OBJECTS) # gcc -o $(TARGET).static $(OBJECTS) -L/usr/lib -L/usr/X11R6/lib -Wl,-Bstatic -lgtk -lgdk -lglib -Wl,-Bdynamic -lXext -lX11 -lm gcc -static -o $(TARGET).static $(OBJECTS) -L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -lglib -lXext -lX11 -lm clean: rm -rf core *.o $(TARGET) # dependancies, gcc `gtk-config --cflags` -MM *.c cccd.o: cccd.c general.h misc.h cd.h options.h cddb.h info.h cccd.h \ icons.h cd.o: cd.c cd.h cddb.o: cddb.c general.h cd.h cddbp.h misc.h options.h cddb.h cddbp.o: cddbp.c misc.h cddbp.h info.o: info.c general.h misc.h cd.h cddb.h options.h cccd.h info.h misc.o: misc.c options.o: options.c general.h misc.h options.h cccd-0.3beta4.orig/BUGREPORT0100600000175000017500000000171606560704363014106 0ustar uh1763uh1763Please use this form to report a problem to me. Thanks. Bug number: [assigned for you] State: open Abstract: [Short description, one line only please] Your name: Your email: Detailed description: Component: [ ] CD control [ ] User interface [ ] CDDB functionality [ ] Documentation [ ] I don't know Severity: (1: Crashes X 2: Crashes 3: Something is broken, but workaround is available 4: Cosmetic fault 5: Suggestion) Software used: cccd version: Linux Kernel version: libc version: GTK+ version: Drive hardware used: [ ] SCSI CD [ ] ATAPI CD [ ] other SCSI device (CD-R, ...) [ ] other ATAPI device (CD-R, ...) Mail the completed form to jst@writeme.com. If the problem only occurs on a specific CD, please insert the CDDB record into the description field. Remarks: [will be filled in later by me] cccd-0.3beta4.orig/cccd.spec0100600000175000017500000000121507416151656014356 0ustar uh1763uh1763Summary: a CDDB capable CD player for X11/GTK+ Name: cccd Version: 0.3beta3 Release: 1 Copyright: GPL Group: X11/Amusements Source: http://home.pages.de/~jst/cccd/cccd-0.3beta3.tar.gz BuildRoot: /var/tmp/cccd-root Requires: gtk+ Vendor: Jochen Stein %description This program plays audio CDs on your cdrom drive. It queries CD info from CDDB, uses GTK+ as user interface and takes up very little screen space. %prep %setup %build make %install make install prefix=$RPM_BUILD_ROOT/usr/local %clean rm -fr $RPM_BUILD_ROOT %files %doc README COPYING CHANGES BUGREPORT README.original /usr/local/bin/cccd /usr/local/man/man1/cccd.1 cccd-0.3beta4.orig/cccd.10100600000175000017500000000516407416150160013560 0ustar uh1763uh1763.\" Hey, EMACS: -*- nroff -*- .TH CCCD 1 "January 6, 2002" .SH NAME cccd \- yet another CD player with CDDB support .SH SYNOPSIS .B cccd .SH DESCRIPTION cccd is a small GTK CD player program with many features: * Works with SCSI devices * CDDB lookups (local and remote) and local storing * Uses very little screen space * Handles CD Extras (mixed mode CDs) .SH OPTIONS none .SH USAGE Upon starting cccd two windows will pop up: a smaller control panel and a status window. .SS The Control Panel .PP Here are the buttons for controlling the CD drive. The main action will take place here. The left most button is the exit button, which will cause .B cccd to quit. The right most button will cause the Info window (q.v.) to pop up. Buttons in the top row (left to right): Previous Track, Play, Next Track, and in the bottom row: Pause, Stop and Eject. Their functions should be self- explanitory. .SS The Status Window .PP You will see four rows of text here, and a progress bar below that. The top most row shows the Artist and Title of the CD, if known. Below that is the track title. Then comes the time display and then a status row. You can toggle the time display between - CD time passed - CD time remaining, - Track time passed, - Track time remaining by clicking on the window above the progress bar. The status information will show the current status and the play mode. You can toggle the playmodes (Normal, Loop Track, Loop CD, Random) by clicking on the progress bar. The progress bar will always show the elapsed CD time. .SS The Info Window .PP This window is divided into two sections: .SS CD .PP Here you can see (and later enter) the CD title and Artist, followed by a list of the Track titles. Below this there are button that (will) allow you to Save the current CD data, submit it to a remote CDDB database, and Undo any changes you have made since the last saving. You can realize a playlist of the tracks you want to hear, by clicking on the track row. Darker tracks will not be played. (Note: Data tracks will by \'unselected' automatically). .SS CDDB .PP You can enter the relevant data for remote and local cddb databases here, and the lookup order. If the remote databases lookup protocol is http, you will need to enter the URL in addidion to the hostname, without the leading "http://" (e.g. "example.url.com/cddb/database/lookup.cgi". The category section is not used, and will be omitted in future versions. "Save" will save the info to "~/.cccdrc". I'll leave to your imagination what Undo does. .SH AUTHOR .B cccd was originally written by Sven Riedel, modified by Jochen Stein.