/* Copyright (c) 2006 by Arkkra Enterprises */ /* All rights reserved */ // This file contains code for miscellaneous things that don't seem to really // belong with any particular menu. #include #include #include #include #include "globals.H" #include "utils.H" #include #include #ifdef OS_LIKE_WIN32 #include #include #include #include #include #include #endif // The FLTK Fl_Int_Input is almost what we want, but it allows // octal and hex input via leading 0 and 0x respectively. // So the Int_Input derived class intercepts the input and throws away // characters from the set [xX] and leading zeros. // Sometimes we also want to restrict to positive numbers, // so the Positive_Int_Input class discards the - character as well. Int_Input::Int_Input(int x, int y, int w, int h, const char * label) : Fl_Int_Input(x, y, w, h, label) { allow_negative = true; } Positive_Int_Input::Positive_Int_Input(int x, int y, int w, int h, const char * label) : Int_Input(x, y, w, h, label) { allow_negative = false; } int Int_Input::handle(int event) { if (event == FL_KEYBOARD) { int key = Fl::event_key(); if (key == 'x' || key == 'X') { return(1); } if (key == '0' && position() == 0) { return(1); } if (key == '-' && ! allow_negative) { return(1); } } return(Fl_Int_Input::handle(event)); } // Character used to separate items in $PATH and to separate directory names #ifdef OS_LIKE_WIN32 static const char path_sep = ';'; static const char dir_sep = '\\'; #else static const char path_sep = ':'; static const char dir_sep = '/'; #endif // Return the native OS's directory separator character char dir_separator(void) { return(dir_sep); } // Return the native OS's path separator character char path_separator(void) { return(path_sep); } // Set the value of $MUPPATH. We "new" space for it and save a static // pointer to that space. // If it had been set before, we delete the old space. void set_muppath(const char * new_muppath) { static char * muppath = 0; if (muppath != 0) { // The +8 is to skip past the MUPPATH= part if (strcmp(muppath + 8, new_muppath) == 0) { // Setting to existing value, so nothing to do return; } delete muppath; } muppath = new char [strlen(new_muppath) + 9]; (void) sprintf(muppath, "MUPPATH=%s", new_muppath); (void) putenv(muppath); } // Given a path to a file in "location", and the length of that path, // and a suffix, see if the location with the suffix added is an // executable file. If so, return true, with the suffixed name left // in location. Otherwise return false with location as it came in. // Could also return false if path would be longer than FL_PATH_MAX, // and therefore will not fit. (Better to fail than core dump.) static bool access_with_suffix(char * location, int length, const char * suffix) { if (length + strlen(suffix) + 1 > FL_PATH_MAX) { // Too long to store return(false); } // Add suffix and see if it is an executable file (void) strcpy(location + length, suffix); if(access(location, X_OK) == 0) { return(true); } else { // This suffix didn't work. Remove it before returning location[length] = '\0'; return(false); } } // Given a file location, see if it exists as an executable file, // taking into account the DOS/Windows strangeness of implicit suffixes. static bool check_access(char * location) { int len = strlen(location); #ifdef __WIN32 // If doesn't have a suffix, try with .com, .exe, and .bat suffix if (len < 5 || strchr(location + len - 4, '.') == 0) { // This is the precedence order for executable suffixes if (access_with_suffix(location, len, ".com")) { return(true); } if (access_with_suffix(location, len, ".exe")) { return(true); } if (access_with_suffix(location, len, ".bat")) { return(true); } return(false); } // If did have a suffix, go ahead and try name as is #endif return access_with_suffix(location, len, ""); } // Find the value of PATH. First try in third arg of main() // since that seems more reliable on some OSs. Failing that, try getenv(). // We cache the value so we only have to search for it one time. // This also rescues us in case env_p becomes invalid due to setting // new environment variable values. static const char * Path = 0; void get_path(const char ** const env_p) { if (Path != 0) { // Already did it before return; } if (env_p != 0) { // Find $PATH in the environment variable list int e; for (e = 0; env_p[e] != 0; e++) { if (strncmp(env_p[e], "PATH=", 5) == 0) { Path = strdup(env_p[e] + 5); break; } } } if (Path == 0) { // Not found in the arge, so try looking up directly Path = getenv("PATH"); } } // Return true if given path is an absoluate path bool is_absolute(const char * const path) { #ifdef OS_LIKE_WIN32 if ((path[0] != '\0' && path[1] == ':') || path[0] == dir_sep) { #else if (path[0] == dir_sep) { #endif return(true); } return(false); } // Given the name of a executable program, find the directory from // which it comes, and put the full path into "location," // which is expected to be at least FL_PATH_MAX bytes long. // The incoming pgm_name is expected to be no more than FL_PATH_MAX long. // It uses the components of PATH to try to find the executable. // For Windows, if the program name doesn't have a suffix, // it tries to find a .com, .exe, or .bat file with the pgm_name. // It returns true on success. On failure, it returns false, // and the contents of location are not defined. bool find_executable(const char * const pgm_name, char * location) { // If pgm_name is already absolute path, // just check if it exists and is executable if (is_absolute(pgm_name)) { (void) strcpy(location, pgm_name); return (check_access(location)); } if (Path == 0) { // Should have already looked up $PATH, // but make another attempt, just in case... get_path(0); if (Path == 0) { return(false); } } // We'll try the program name added to each PATH component // until we find it or have to give up. #ifdef OS_LIKE_WIN32 // DOS/Windows implicitly adds current working directory first bool add_implicit_cwd = true; #else bool add_implicit_cwd = false; #endif const char * component; // current component of PATH const char * next_component; // next component of PATH const char * sep_p; // location of PATH separator int len; // length of component for (component = Path; *component != '\0'; component = next_component) { if (add_implicit_cwd) { // DOS/Windows implicitly adds current directory // as first PATH component. len = 0; next_component = component; add_implicit_cwd = false; } else if ((sep_p = strchr(component, path_sep)) != 0) { // Not the last component in the PATH len = sep_p - component; next_component = sep_p + 1; } else { // Is the last component in the PATH len = strlen(component); next_component = component + len; } if (len == 0) { // Empty path component means current directory. // Allow enough room for directory separator, // pgm_name, suffix, and null terminator if (getcwd(location, FL_PATH_MAX - strlen(pgm_name) - 6) == 0) { // Current directory unobtainable or too long return(false); } len = strlen(location); } else { strncpy(location, component, len); } // If PATH component didn't already add a directory // separator, we add one. In some OSs it doesn't hurt // to add another, but no reason to use an extra byte. if (location[len-1] != dir_sep) { location[len++] = dir_sep; } // Now add the progam name itself and see if it exists. // The check_access() will add implied suffix if necessary. (void) strcpy(location + len, pgm_name); if (check_access(location)) { return(true); } } return(false); } // Returns location of magic file that lets us know user agreed // to the license. The value is saved so subsequent calls can just return // the saved value. If something goes wrong, a null string is returned. #ifdef OS_LIKE_WIN32 #define MAGIC_FILE_NAME "mup.ok" #else #if defined(OS_LIKE_UNIX) || defined(VMS) || defined(AMIGA) || defined(EMX) #define MAGIC_FILE_NAME ".mup" #else #ifdef Mac_BBEdit #define MAGIC_FILE_NAME (char *)(MupRegFileName + 1) #endif #endif #endif #ifndef MAGIC_FILE_NAME #error OS not supported #endif const char * magic_file(const char * pname, const char ** env_p) { char * home; char * fname = MAGIC_FILE_NAME; static char * magicpath = 0; // Path is saved here if (magicpath != 0) { // Must have figured it out on previous call return(magicpath); } // First check current directory. This will only work // for the case where user has already created the file, // and is of somewhat questionable use, since if the user // changes directories to somewhere without the magic file, // Mup will print the watermark. if (access(fname, F_OK) == 0) { magicpath = new char[strlen(fname) + 1]; strcpy(magicpath, fname); return(magicpath); } #ifdef OS_LIKE_WIN32 // Construct pathname to magic file in the directory where // mup.exe came from if (pname == 0) { // Shouldn't happen; We should get called once with valid // pname, and return cached value after that fl_alert("Unable to determine magic file name."); return(""); } char location[FL_PATH_MAX]; if (find_executable(pname, location)) { int baselength = strlen(location) - strlen(fl_filename_name(location)); magicpath = new char[baselength + strlen(fname) + 1]; // Copy pname up to last backslash (void) strncpy(magicpath, location, baselength); // add magic file name (void) strcpy(magicpath + baselength, fname); } #else // Construct pathname to magic file if it is in $HOME if ((home = getenv("HOME")) != (char *) 0) { magicpath = new char[strlen(home) + strlen(fname) + 2]; #ifdef VMS (void) sprintf(magicpath, "%s%s", home, fname); #else (void) sprintf(magicpath, "%s/%s", home, fname); #endif } #endif #ifdef Mac_BBEdit #pragma unused(pname) // Check for file in Preferences folder inside System folder magicpath = 0; home = 0; { short vRefNum; long dirID; FSSpec fsSpec; if (FindFolder(kOnSystemDisk, kPreferencesFolderType, false, &vRefNum, &dirID) == noErr) { // Preferences folder exists if (FSMakeFSSpec(vRefNum, dirID, (StringPtr) MupRegFileName, &fsSpec) == noErr) { // File exists short old_vRefNum; long old_dirID; if (HGetVol((StringPtr) 0, &old_vRefNum, &old_dirID) != noErr) { return; } if (HSetVol((StringPtr) 0, vRefNum, dirID) != noErr) { return; } HSetVol((StringPtr) 0, old_vRefNum, old_dirID); magicpath = new char[strlen(MAGIC_FILE_NAME) + 1]; (void) sprintf(magicpath, MAGIC_FILE_NAME); } } } #endif if (magicpath == 0) { // Not sure what to really do here... Punt. fl_alert("Unable to find magic file path."); magicpath = ""; } return(magicpath); } #ifdef OS_LIKE_WIN32 // On Windows, we read the registry to try to determine the proper // program to use for a given file type, like .mid or .ps files. // This function will return the path to the appropriate file, // if found, in a static area that may get overwritten on next call, // so caller needs to make its own copy. If program is not found, // returns null. char * lookup_pgm_for_file_suffix(const char * file_suffix) { static char data[512]; char name[512]; long len = sizeof(data); // First find entry for file suffix mapping to a class (void) sprintf(name, "Software\\Classes\\%s", file_suffix); HRESULT result = RegQueryValue(HKEY_LOCAL_MACHINE, name, data, &len); if (result != ERROR_SUCCESS) { return(0); } // Next look up the program associated with that class (void) sprintf(name, "Software\\Classes\\%s\\shell\\open\\command", data); len = sizeof(data); result = RegQueryValue(HKEY_LOCAL_MACHINE, name, data, &len); if (result != ERROR_SUCCESS) { return(0); } // We might get multiple strings back, // giving the program itself plus arguments. // We only want the program itself. So if the first string is quoted, // strip the quotes and anything after it. char * d; if (*data == '"') { for (d = data + 1; *d != '\0'; d++) { if (*d == '"') { *(d-1) = '\0'; break; } *(d-1) = *d; } } if (access(data, X_OK) == 0) { return(data); } return(0); } // Look up the given name in the CURRENT_USER area of registry // and if found, fill in the data and return true, except return false. static bool reg_dir_found(char * name, char * data, DWORD len) { HRESULT result = RegQueryValueEx(HKEY_CURRENT_USER, name, 0, 0, (LPBYTE)data, &len); if (result == ERROR_SUCCESS) { struct stat info; if (stat(data, &info) == 0 && (info.st_mode & S_IFDIR)) { return(true); } } return(false); } // Look for likely default folder for Mup files. // Use the current user's "My Music" folder if there is one, // otherwise try their "Personal" folder. // Returns path in static area or null on failure. char * find_music_folder(void) { static char best_value[FL_PATH_MAX]; // Get the registry info about folders HKEY key = 0; // Win'98 uses "User Shell Folders" but newer versions use just // "Shell Folders," so we check for both, newer first, since that // is probably more likely to work. if ((RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_READ, &key) == ERROR_SUCCESS) || (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", 0, KEY_READ, &key) == ERROR_SUCCESS)) { DWORD max_key_length; DWORD count; DWORD max_name_length; DWORD max_value_length; // Find out how many subkeys there are, and max lengths. if (RegQueryInfoKey(key, 0, 0, 0, 0, &max_key_length, 0, &count, &max_name_length, &max_value_length, 0, 0) == ERROR_SUCCESS) { TCHAR name[max_name_length + 1]; DWORD name_length; DWORD value_type; BYTE value[max_value_length + 1]; DWORD value_length; int i; best_value[0] = '\0'; // Look for "My Music" and "Personal" subkeys. // There's probably a better way to query for specific // subkey than linear search, but this works... for (i = 0; i < count; i++) { name_length = sizeof(name); value_length = sizeof(value); if (RegEnumValue(key, i, name, &name_length, 0, &value_type, value, &value_length) == ERROR_SUCCESS) { if (value_type != REG_SZ) { continue; } if (strcasecmp(name, "My Music") == 0) { // Found the ones we want. strcpy(best_value, (char *) value); break; } if (strcasecmp(name, "Personal") == 0) { // This is our second choice. // Save as best so far. strcpy(best_value, (char *) value); } } } } } if (key != 0) { RegCloseKey(key); } return(best_value[0] == '\0' ? 0 : best_value); } #endif