/* Copyright (C) Bram Avontuur (bram@avontuur.org) * * 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. * * This file contains global functions that are globally useful for the * mp3blaster binary */ /* Change history * 29 oct 2000: Added is_wav() */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mp3blaster.h" #include "id3parse.h" #include #include NCURSES_HEADER extern _globalopts globalopts; void debug(const char *fmt, ...); /* get homedir of user 'username' (username == NULL => user running this prog) * it returns a malloc'd string which you should free(), or NULL if user * does not exist */ char * get_homedir(const char *username) { uid_t userid = 0; int match_uid = 0; struct passwd *pwdentry; short foundluser = 0; char *homedir = NULL; if (!username) // this user's homedir { userid = getuid(); match_uid = 1; } setpwent(); //rewind passwd file while (!foundluser && (pwdentry = getpwent())) { if (match_uid) { if (userid == pwdentry->pw_uid) { foundluser = 1; homedir = strdup(pwdentry->pw_dir); } } else { if (!strcmp(username, pwdentry->pw_name)) { foundluser = 1; homedir = strdup(pwdentry->pw_dir); } } } endpwent(); if (!foundluser) return NULL; else return homedir; } /* Replace 'org_path' with a new path, in which ~ is expanded. * (if there's already a standard (ANSI) C function that does this, * send me a wall so I can bang my head against it. ;) * the return value = NULL if ~ can't be expanded, or else a malloc()'d * string containing the new path (which equals org_path if it didn't contain * a ~ as first char) */ char * expand_path(const char *org_path) { char *new_path = NULL; int mode = 0; if (!org_path) return NULL; if (!strlen(org_path)) return strdup(org_path); if (!strncmp(org_path, "~/", 2) || (!strcmp(org_path, "~")) ) mode = 1; else if (org_path[0] == '~') mode = 2; else /* no ~ to expand */ return strdup(org_path); if (mode == 1) { char *hd = get_homedir(NULL); if (!hd) hd = strdup(""); new_path = (char*)malloc((strlen(hd) + strlen(org_path) + 2) * sizeof(char)); strcpy(new_path, hd); // org_path has at least chars (since it must have a string length) */ if (org_path[1] != '/') strcat(new_path, "/"); strcat(new_path, org_path+1); free(hd); return new_path; } else if (mode == 2) /* ~username/path or ~file */ { char user[strlen(org_path)+1], rest_path[strlen(org_path)+1], *hd = NULL; int scanval = sscanf(org_path, "~%[^/]/%s", user, rest_path); if (!scanval) return NULL; if (!(hd = get_homedir(user))) //perhaps a filename was meant.. { return strdup(org_path); } if (scanval == 2) { new_path = (char*)malloc((strlen(hd)+1+strlen(rest_path)+1) * sizeof(char)); strcpy(new_path, hd); strcat(new_path, "/"); strcat(new_path, rest_path); } else { new_path = (char*)malloc((strlen(hd)+2) * sizeof(char)); strcpy(new_path, hd); strcat(new_path, "/"); } free(hd); return new_path; } //should never get here; return NULL; } void debug(const char *fmt, ... ) { va_list ap; char buf[1024]; static int debug_init = 1; static FILE *debug_info = NULL; if (!globalopts.debug) return; if (debug_init) { debug_init = 0; char *homedir = get_homedir(NULL); if (!homedir) return; const char flnam[] = ".mp3blaster"; char to_open[strlen(homedir) + strlen(flnam) + 2]; sprintf(to_open, "%s/%s", homedir, flnam); if (!(debug_info = fopen(to_open, "a"))) return; //debug("Debugging messages enabled. Hang on to yer helmet!\n"); } va_start(ap, fmt); vsnprintf(buf,1024,fmt,ap); va_end(ap); if (debug_info) { fwrite(buf, sizeof(char), strlen(buf), debug_info); fflush(debug_info); } } /* PRE: f is open * Function returns NULL when EOF(f) else the next line read from f, terminated * by \n. The line is malloc()'d so don't forget to free it .. */ char * readline(FILE *f) { short not_found_endl = 1; char * line = NULL; while (not_found_endl) { char tmp[256]; char *index; if ( !(fgets(tmp, 255, f)) ) break; index = (char*)rindex(tmp, '\n'); if (index && strlen(index) == 1) /* terminating \n found */ not_found_endl = 0; if (!line) { line = (char*)malloc(sizeof(char)); line[0] = '\0'; } line = (char*)realloc(line, (strlen(line) + strlen(tmp) + 1) * sizeof(char)); strcat(line, tmp); } if (line && line[strlen(line) - 1] != '\n') /* no termin. \n! */ { line = (char*)realloc(line, (strlen(line) + 2) * sizeof(char)); strcat(line, "\n"); } return line; } /* returns a malloc()'d string */ char * crop_whitespace(const char *string) { char *orgstring, *tmpstring, *newstring; int index = 0; if (!string) return NULL; tmpstring = strdup(string); orgstring = tmpstring; while (tmpstring[0] != '\0' && isspace((int)tmpstring[0])) tmpstring++; while ( (index = strlen(tmpstring)-1) > -1 && isspace((int)tmpstring[index])) tmpstring[index]='\0'; newstring = (char*)malloc((strlen(tmpstring)+1)*sizeof(char)); strcpy(newstring, tmpstring); free(orgstring); return newstring; } /* checks if a path is a dir we can access. (hence diff. from fstat, which * doesn't check access) */ short is_dir(const char *path) { DIR *dir2 = opendir(path); if (dir2) { closedir(dir2); return 1; } return 0; } const char * chop_path(const char *a) { const char *last_slash = strrchr(a, '/'); int begin; if (!last_slash) /* no '/' in this->items[item_index] */ begin = 0; else begin = strlen(a) - strlen(last_slash) + 1; return a + begin; } /* delete[] string after use */ char * chop_file(const char *a) { const char *last_slash = strrchr(a, '/'); char *new_path = NULL; if (!last_slash) /* no '/' in this->items[item_index] */ { new_path = new char[1]; new_path[0] = '\0'; return new_path; } new_path = new char[strlen(a) + 1]; strcpy(new_path, a); new_path[(strlen(new_path) - strlen(last_slash)) + 1] = '\0'; return new_path; } int is_mp3(const char *filename) { int len; if (!filename || (len = strlen(filename)) < 5) return 0; if (fnmatch(".[mM][pP][23]", (filename + (len - 4)), 0)) return 0; //if (strcasecmp(filename + (len - 4), ".mp3")) // return 0; return 1; } int is_wav(const char *filename) { int len; if (!filename || (len = strlen(filename)) < 5) return 0; if (fnmatch(".[wW][aA][vV]", (filename + (len - 4)), 0)) return 0; //if (strcasecmp(filename + (len - 4), ".mp3")) // return 0; return 1; } int is_ogg(const char *filename) { int len; if (!filename || (len = strlen(filename)) < 5) return 0; if (fnmatch(".[oO][gG][gG]", (filename + (len - 4)), 0)) return 0; return 1; } int is_sid(const char *filename) { #ifdef HAVE_SIDPLAYER char *ext = strrchr(filename, '.'); if (ext) { if (!strcasecmp(ext, ".psid")) return 1; if (!strcasecmp(ext, ".sid")) return 1; if (!strcasecmp(ext, ".dat")) return 1; if (!strcasecmp(ext, ".inf")) return 1; if (!strcasecmp(ext, ".info")) return 1; } #else if(filename); //prevent warning #endif return 0; } int is_httpstream(const char *filename) { if (!strncasecmp(filename, "http://", 7)) return 1; return 0; } int is_audiofile(const char *filename) { if (globalopts.extensions) { int i = 0; int regflags = REG_EXTENDED | REG_ICASE; while (globalopts.extensions[i] != NULL) { int doesmatch = 0; regex_t dum; regcomp(&dum, globalopts.extensions[i], regflags); doesmatch = regexec(&dum, filename, 0, 0, 0); regfree(&dum); if (!doesmatch) //regexec returns 0 for a match. return 1; i++; } return 0; } return (is_mp3(filename) || is_sid(filename) || is_httpstream(filename) || is_ogg(filename) || is_wav(filename)); } int is_playlist(const char *filename) { int i = 0, loop = 1; while (loop) { const char *ext; int doesmatch; if (!globalopts.plist_exts || (!i && !globalopts.plist_exts[i])) { if (!i) { ext = "\\.lst$"; i++; } else { ext = "\\.m3u$"; loop = 0; } } else { ext = globalopts.plist_exts[i]; loop = (globalopts.plist_exts[++i] != NULL); } regex_t dum; regcomp(&dum, ext, REG_EXTENDED | REG_ICASE); doesmatch = regexec(&dum, filename, 0, 0, 0); regfree(&dum); if (!doesmatch) //regexec returns 0 for a match. return 1; } return 0; } /* crunch_string takes a string 'flname' and shortens it (if necessary) to * length 'length', by chopping of the begin of the string, and replacing * the first 3 characters of the new strings with dots. * E.g. crunch_string("foobar", 5); will return "...ar". * * If the input string is shorter than 4 characters, a copy of the (unmodified) * string will be returned. */ char * crunch_string(const char *flname, unsigned int length) { char *filename = NULL; if (!flname) return NULL; if (!strlen(flname) || length < 4) { //too short, return the thing itself. filename = new char[strlen(flname) + 1]; strcpy(filename, flname); return filename; } if (strlen(flname) > length) { filename = new char[length+1]; strcpy(filename, flname+(strlen(flname)-length)); filename[0] = filename[1] = filename[2] = '.'; } else { filename = new char[strlen(flname)+1]; strcpy(filename,flname); } return filename; } short read_file(const char *filename, char ***lines, int *linecount) { char *line = NULL; FILE *f = fopen(filename, "r"); if (!f) return 0; *lines = NULL; *linecount = 0; while ( (line = readline(f)) ) { *lines = (char**)realloc(*lines, (*linecount + 1) * sizeof(char**)); if (!lines) return 0; (*lines)[(*linecount)++] = line; } return 1; } //charset stuff void recode_string(char *string) { int c; if (string != NULL) { for( ; *string != '\000'; string++ ) { c=((int)*string)&0xFF; *string = globalopts.recode_table[c]; } } } //returns a filename built from id3tag info. Returned string is malloc()'d! char * id3_filename(const char *filename) { if (!filename) return NULL; char *id3name = NULL; id3Parse *id3 = new id3Parse(filename); id3header *hdr; if (!id3) return NULL; if (!(hdr = id3->parseID3())) { delete id3; return NULL; } if (!strlen(hdr->artist) && !strlen(hdr->songname)) //not enough info { delete id3; return NULL; } char *artist = NULL, *song = NULL; if (!strlen(hdr->artist)) artist = strdup("Unknown Artist"); else artist = strdup(hdr->artist); if (!strlen(hdr->songname)) song = strdup("Unknown Title"); else song = strdup(hdr->songname); artist = crop_whitespace(artist); song = crop_whitespace(song); if (globalopts.recode_table) { recode_string(artist); recode_string(song); } id3name = (char*)malloc( (strlen(artist) + strlen(song) + 20) * sizeof(char)); sprintf(id3name, "[ID3] %s - %s", artist, song); delete id3; return id3name; } /* same as above, only this function always returns a non-NULL char* pointer, * which must be delete[]'d instead of free()'d. Also, the argument is * a void* pointer. * The reason for this seamingly weird behaviour is because it is fed to * changeItem() in the scrollWin class. */ char * id3_filename(void *filename) { char *result = id3_filename((char*)filename); if (!result) { result = new char[strlen((char*)filename) + 1]; strcpy(result, (char*)filename); } else { //it must be allocated using new. char * result2 = new char[strlen(result) + 1]; strcpy(result2, result); free(result); result = result2; } return result; } /* Function : lowercase * Description: Transforms uppercase characters in a string to lowercase. * Parameters : str : (char*)string to be lowercased * Returns : Nothing. * SideEffects: If str is not 0-terminated, probably many. */ void lowercase(char *str) { unsigned int i = 0; if (!str) return; while (str[i]) { if (isupper(str[i])) str[i] = tolower(str[i]); i++; } } /* Function : get_input * Description: Gets input from a user. Predefined input value may be given, * : making it easy for the user to alter that. * Parameters : win : window to get input from * : defval: default input value (optional) * : maxlen: Maximum length of input. * : y : initial cursor y position in window * : x : "" "" x "" "" "" * Returns : Input given by user. Free with delete[] after use. * SideEffects: Sets several curses input and output options. You will want * : to reset them back to the values they were before calling * : this function, as the current settings cannot be read by * : this function. */ #if 0 char * get_input(WINDOW *win, const char*defval, int maxlen, int y, int x) { char *input = new char[maxlen + 1]; int curlen = 0, cursor_pos = 0, //offset from beginning cursor pos. Also index in input. i = 0, c = 0; short insert_mode = 1; //0 -> replace i/o insert memset(input, 0, maxlen + 1); if (defval) { strncpy(input, defval, maxlen); curlen = strlen(defval); mvwaddstr(win, y, x, input); if (strlen(defval) > (unsigned int)maxlen) curlen = maxlen; } noecho(); cbreak(); leaveok(win, FALSE); nodelay(win, FALSE); wmove(win, y,x); wrefresh(win); while ((c = getch()) != 13) { switch(c) { case KEY_LEFT: if (cursor_pos > 0) { cursor_pos--; } break; case KEY_RIGHT: if (cursor_pos < curlen) cursor_pos++; break; case KEY_BACKSPACE: if (cursor_pos > 0) { for (i = cursor_pos - 1; i < curlen - 1; i++) { input[i] = input[i + 1]; mvwaddch(win, y, x + i, input[i]); } input[--curlen] = '\0'; mvwaddch(win, y, x + curlen, ' '); cursor_pos--; } break; default: if (cursor_pos < maxlen && isprint(c)) { mvwaddch(win, y, x + cursor_pos, c); input[cursor_pos] = c; if (cursor_pos == curlen) curlen++; if (cursor_pos < maxlen - 1) cursor_pos++; } } //debug("Cursor_pos: %d\n", cursor_pos); wmove(win, y, x + cursor_pos); wrefresh(win); } return input; }; #endif int sort_files_fun(const void *a, const void *b) { sortmodes_t smode = globalopts.fw_sortingmode; const char *fa = chop_path(*(char**)a), *fb = chop_path(*(char**)b); if (smode == FW_SORT_ALPHA) //case-insenstive alphabetical sort return (strcasecmp(fa, fb)); if (smode == FW_SORT_ALPHA_CASE) //case-sensitive "" "" return (strcmp(fa, fb)); if (smode == FW_SORT_NONE) //don't sort return 0; struct stat sa, sb; if (stat(fa, &sa) < 0 || stat(fb, &sb) < 0) //can't stat one o/t files return 0; if (smode == FW_SORT_MODIFY_NEW || smode == FW_SORT_MODIFY_OLD) { time_t time_a = sa.st_mtime, time_b = sb.st_mtime; if (time_a < time_b) return (smode == FW_SORT_MODIFY_OLD ? -1 : 1); else if (time_a > time_b) return (smode == FW_SORT_MODIFY_OLD ? 1 : -1); return 0; } if (smode == FW_SORT_SIZE_SMALL || smode == FW_SORT_SIZE_BIG) { off_t size_a = sa.st_size, size_b = sb.st_size; if (size_a < size_b) return (smode == FW_SORT_SIZE_SMALL ? -1 : 1); else if (size_a > size_b) return (smode == FW_SORT_SIZE_SMALL ? 1 : -1); return 0; } return 0; } /* Function : sort_files * Description: Sorts files. Sort order is determined by globalopts.fw_sortmode * Parameters : files: Array to sort. Its elements can be shuffled. * : Paths in filenames are not taken into account when * : sorting! * : nrfiles: Number of files in files array. * Returns : Nothing. * SideEffects: Mangles 'files'. */ void sort_files(char **files, int nrfiles) { qsort(files, nrfiles, sizeof(char*), sort_files_fun); }