chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mupmate / utils.C
CommitLineData
69695f33
MW
1/* Copyright (c) 2006 by Arkkra Enterprises */
2/* All rights reserved */
3
4// This file contains code for miscellaneous things that don't seem to really
5// belong with any particular menu.
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11#include "globals.H"
12#include "utils.H"
13#include <FL/fl_ask.H>
14#include <FL/filename.H>
15#ifdef OS_LIKE_WIN32
16#include <windef.h>
17#include <winbase.h>
18#include <winreg.h>
19#include <ctype.h>
20#include <unistd.h>
21#include <sys/stat.h>
22#endif
23
24
25// The FLTK Fl_Int_Input is almost what we want, but it allows
26// octal and hex input via leading 0 and 0x respectively.
27// So the Int_Input derived class intercepts the input and throws away
28// characters from the set [xX] and leading zeros.
29// Sometimes we also want to restrict to positive numbers,
30// so the Positive_Int_Input class discards the - character as well.
31
32Int_Input::Int_Input(int x, int y, int w, int h, const char * label)
33 : Fl_Int_Input(x, y, w, h, label)
34{
35 allow_negative = true;
36}
37
38
39Positive_Int_Input::Positive_Int_Input(int x, int y, int w, int h, const char * label)
40 : Int_Input(x, y, w, h, label)
41{
42 allow_negative = false;
43}
44
45int
46Int_Input::handle(int event)
47{
48 if (event == FL_KEYBOARD) {
49 int key = Fl::event_key();
50 if (key == 'x' || key == 'X') {
51 return(1);
52 }
53 if (key == '0' && position() == 0) {
54 return(1);
55 }
56 if (key == '-' && ! allow_negative) {
57 return(1);
58 }
59 }
60 return(Fl_Int_Input::handle(event));
61}
62
63
64
65// Character used to separate items in $PATH and to separate directory names
66#ifdef OS_LIKE_WIN32
67 static const char path_sep = ';';
68 static const char dir_sep = '\\';
69#else
70 static const char path_sep = ':';
71 static const char dir_sep = '/';
72#endif
73
74
75// Return the native OS's directory separator character
76
77char
78dir_separator(void)
79{
80 return(dir_sep);
81}
82
83// Return the native OS's path separator character
84char
85path_separator(void)
86{
87 return(path_sep);
88}
89
90// Set the value of $MUPPATH. We "new" space for it and save a static
91// pointer to that space.
92// If it had been set before, we delete the old space.
93
94void
95set_muppath(const char * new_muppath)
96{
97 static char * muppath = 0;
98
99 if (muppath != 0) {
100 // The +8 is to skip past the MUPPATH= part
101 if (strcmp(muppath + 8, new_muppath) == 0) {
102 // Setting to existing value, so nothing to do
103 return;
104 }
105 delete muppath;
106 }
107 muppath = new char [strlen(new_muppath) + 9];
108 (void) sprintf(muppath, "MUPPATH=%s", new_muppath);
109 (void) putenv(muppath);
110}
111
112
113
114// Given a path to a file in "location", and the length of that path,
115// and a suffix, see if the location with the suffix added is an
116// executable file. If so, return true, with the suffixed name left
117// in location. Otherwise return false with location as it came in.
118// Could also return false if path would be longer than FL_PATH_MAX,
119// and therefore will not fit. (Better to fail than core dump.)
120
121static bool
122access_with_suffix(char * location, int length, const char * suffix)
123{
124 if (length + strlen(suffix) + 1 > FL_PATH_MAX) {
125 // Too long to store
126 return(false);
127 }
128
129 // Add suffix and see if it is an executable file
130 (void) strcpy(location + length, suffix);
131 if(access(location, X_OK) == 0) {
132 return(true);
133 }
134 else {
135 // This suffix didn't work. Remove it before returning
136 location[length] = '\0';
137 return(false);
138 }
139}
140
141
142// Given a file location, see if it exists as an executable file,
143// taking into account the DOS/Windows strangeness of implicit suffixes.
144
145static bool
146check_access(char * location)
147{
148 int len = strlen(location);
149#ifdef __WIN32
150 // If doesn't have a suffix, try with .com, .exe, and .bat suffix
151 if (len < 5 || strchr(location + len - 4, '.') == 0) {
152 // This is the precedence order for executable suffixes
153 if (access_with_suffix(location, len, ".com")) {
154 return(true);
155 }
156 if (access_with_suffix(location, len, ".exe")) {
157 return(true);
158 }
159 if (access_with_suffix(location, len, ".bat")) {
160 return(true);
161 }
162 return(false);
163 }
164 // If did have a suffix, go ahead and try name as is
165#endif
166 return access_with_suffix(location, len, "");
167}
168
169
170// Find the value of PATH. First try in third arg of main()
171// since that seems more reliable on some OSs. Failing that, try getenv().
172
173// We cache the value so we only have to search for it one time.
174// This also rescues us in case env_p becomes invalid due to setting
175// new environment variable values.
176
177static const char * Path = 0;
178
179void
180get_path(const char ** const env_p)
181{
182 if (Path != 0) {
183 // Already did it before
184 return;
185 }
186
187 if (env_p != 0) {
188 // Find $PATH in the environment variable list
189 int e;
190 for (e = 0; env_p[e] != 0; e++) {
191 if (strncmp(env_p[e], "PATH=", 5) == 0) {
192 Path = strdup(env_p[e] + 5);
193 break;
194 }
195 }
196 }
197 if (Path == 0) {
198 // Not found in the arge, so try looking up directly
199 Path = getenv("PATH");
200 }
201}
202
203
204// Return true if given path is an absoluate path
205
206bool
207is_absolute(const char * const path)
208{
209#ifdef OS_LIKE_WIN32
210 if ((path[0] != '\0' && path[1] == ':') || path[0] == dir_sep) {
211#else
212 if (path[0] == dir_sep) {
213#endif
214 return(true);
215 }
216 return(false);
217}
218
219
220// Given the name of a executable program, find the directory from
221// which it comes, and put the full path into "location,"
222// which is expected to be at least FL_PATH_MAX bytes long.
223// The incoming pgm_name is expected to be no more than FL_PATH_MAX long.
224// It uses the components of PATH to try to find the executable.
225// For Windows, if the program name doesn't have a suffix,
226// it tries to find a .com, .exe, or .bat file with the pgm_name.
227// It returns true on success. On failure, it returns false,
228// and the contents of location are not defined.
229
230bool
231find_executable(const char * const pgm_name, char * location)
232{
233 // If pgm_name is already absolute path,
234 // just check if it exists and is executable
235 if (is_absolute(pgm_name)) {
236 (void) strcpy(location, pgm_name);
237 return (check_access(location));
238 }
239
240 if (Path == 0) {
241 // Should have already looked up $PATH,
242 // but make another attempt, just in case...
243 get_path(0);
244 if (Path == 0) {
245 return(false);
246 }
247 }
248
249 // We'll try the program name added to each PATH component
250 // until we find it or have to give up.
251#ifdef OS_LIKE_WIN32
252 // DOS/Windows implicitly adds current working directory first
253 bool add_implicit_cwd = true;
254#else
255 bool add_implicit_cwd = false;
256#endif
257 const char * component; // current component of PATH
258 const char * next_component; // next component of PATH
259 const char * sep_p; // location of PATH separator
260 int len; // length of component
261 for (component = Path; *component != '\0'; component = next_component) {
262 if (add_implicit_cwd) {
263 // DOS/Windows implicitly adds current directory
264 // as first PATH component.
265 len = 0;
266 next_component = component;
267 add_implicit_cwd = false;
268 }
269
270 else if ((sep_p = strchr(component, path_sep)) != 0) {
271 // Not the last component in the PATH
272 len = sep_p - component;
273 next_component = sep_p + 1;
274 }
275 else {
276 // Is the last component in the PATH
277 len = strlen(component);
278 next_component = component + len;
279 }
280
281 if (len == 0) {
282 // Empty path component means current directory.
283 // Allow enough room for directory separator,
284 // pgm_name, suffix, and null terminator
285 if (getcwd(location, FL_PATH_MAX
286 - strlen(pgm_name) - 6) == 0) {
287 // Current directory unobtainable or too long
288 return(false);
289 }
290 len = strlen(location);
291 }
292 else {
293 strncpy(location, component, len);
294 }
295
296 // If PATH component didn't already add a directory
297 // separator, we add one. In some OSs it doesn't hurt
298 // to add another, but no reason to use an extra byte.
299 if (location[len-1] != dir_sep) {
300 location[len++] = dir_sep;
301 }
302
303 // Now add the progam name itself and see if it exists.
304 // The check_access() will add implied suffix if necessary.
305 (void) strcpy(location + len, pgm_name);
306 if (check_access(location)) {
307 return(true);
308 }
309 }
310 return(false);
311}
312
313
314// Returns location of magic file that lets us know user agreed
315// to the license. The value is saved so subsequent calls can just return
316// the saved value. If something goes wrong, a null string is returned.
317
318#ifdef OS_LIKE_WIN32
319 #define MAGIC_FILE_NAME "mup.ok"
320#else
321 #if defined(OS_LIKE_UNIX) || defined(VMS) || defined(AMIGA) || defined(EMX)
322 #define MAGIC_FILE_NAME ".mup"
323 #else
324 #ifdef Mac_BBEdit
325 #define MAGIC_FILE_NAME (char *)(MupRegFileName + 1)
326 #endif
327 #endif
328#endif
329
330#ifndef MAGIC_FILE_NAME
331#error OS not supported
332#endif
333
334const char *
335magic_file(const char * pname, const char ** env_p)
336{
337 char * home;
338 char * fname = MAGIC_FILE_NAME;
339 static char * magicpath = 0; // Path is saved here
340
341 if (magicpath != 0) {
342 // Must have figured it out on previous call
343 return(magicpath);
344 }
345
346 // First check current directory. This will only work
347 // for the case where user has already created the file,
348 // and is of somewhat questionable use, since if the user
349 // changes directories to somewhere without the magic file,
350 // Mup will print the watermark.
351 if (access(fname, F_OK) == 0) {
352 magicpath = new char[strlen(fname) + 1];
353 strcpy(magicpath, fname);
354 return(magicpath);
355 }
356
357#ifdef OS_LIKE_WIN32
358 // Construct pathname to magic file in the directory where
359 // mup.exe came from
360 if (pname == 0) {
361 // Shouldn't happen; We should get called once with valid
362 // pname, and return cached value after that
363 fl_alert("Unable to determine magic file name.");
364 return("");
365 }
366 char location[FL_PATH_MAX];
367 if (find_executable(pname, location)) {
368 int baselength = strlen(location)
369 - strlen(fl_filename_name(location));
370 magicpath = new char[baselength + strlen(fname) + 1];
371 // Copy pname up to last backslash
372 (void) strncpy(magicpath, location, baselength);
373 // add magic file name
374 (void) strcpy(magicpath + baselength, fname);
375 }
376#else
377 // Construct pathname to magic file if it is in $HOME
378 if ((home = getenv("HOME")) != (char *) 0) {
379 magicpath = new char[strlen(home) + strlen(fname) + 2];
380#ifdef VMS
381 (void) sprintf(magicpath, "%s%s", home, fname);
382#else
383 (void) sprintf(magicpath, "%s/%s", home, fname);
384#endif
385 }
386
387#endif
388#ifdef Mac_BBEdit
389#pragma unused(pname)
390 // Check for file in Preferences folder inside System folder
391 magicpath = 0;
392 home = 0;
393 {
394 short vRefNum;
395 long dirID;
396 FSSpec fsSpec;
397
398 if (FindFolder(kOnSystemDisk, kPreferencesFolderType, false,
399 &vRefNum, &dirID) == noErr) {
400 // Preferences folder exists
401 if (FSMakeFSSpec(vRefNum, dirID,
402 (StringPtr) MupRegFileName, &fsSpec)
403 == noErr) {
404 // File exists
405 short old_vRefNum;
406 long old_dirID;
407 if (HGetVol((StringPtr) 0, &old_vRefNum,
408 &old_dirID) != noErr) {
409 return;
410 }
411
412 if (HSetVol((StringPtr) 0, vRefNum, dirID)
413 != noErr) {
414 return;
415 }
416 HSetVol((StringPtr) 0, old_vRefNum, old_dirID);
417 magicpath = new char[strlen(MAGIC_FILE_NAME) + 1];
418 (void) sprintf(magicpath, MAGIC_FILE_NAME);
419 }
420 }
421 }
422#endif
423
424 if (magicpath == 0) {
425 // Not sure what to really do here... Punt.
426 fl_alert("Unable to find magic file path.");
427 magicpath = "";
428 }
429
430 return(magicpath);
431}
432
433
434#ifdef OS_LIKE_WIN32
435
436// On Windows, we read the registry to try to determine the proper
437// program to use for a given file type, like .mid or .ps files.
438// This function will return the path to the appropriate file,
439// if found, in a static area that may get overwritten on next call,
440// so caller needs to make its own copy. If program is not found,
441// returns null.
442
443
444char *
445lookup_pgm_for_file_suffix(const char * file_suffix)
446{
447 static char data[512];
448 char name[512];
449 long len = sizeof(data);
450 // First find entry for file suffix mapping to a class
451 (void) sprintf(name, "Software\\Classes\\%s", file_suffix);
452 HRESULT result = RegQueryValue(HKEY_LOCAL_MACHINE, name, data, &len);
453 if (result != ERROR_SUCCESS) {
454 return(0);
455 }
456
457 // Next look up the program associated with that class
458 (void) sprintf(name, "Software\\Classes\\%s\\shell\\open\\command", data);
459 len = sizeof(data);
460 result = RegQueryValue(HKEY_LOCAL_MACHINE, name, data, &len);
461 if (result != ERROR_SUCCESS) {
462 return(0);
463 }
464
465 // We might get multiple strings back,
466 // giving the program itself plus arguments.
467 // We only want the program itself. So if the first string is quoted,
468 // strip the quotes and anything after it.
469 char * d;
470 if (*data == '"') {
471 for (d = data + 1; *d != '\0'; d++) {
472 if (*d == '"') {
473 *(d-1) = '\0';
474 break;
475 }
476 *(d-1) = *d;
477 }
478 }
479 if (access(data, X_OK) == 0) {
480 return(data);
481 }
482 return(0);
483}
484
485
486// Look up the given name in the CURRENT_USER area of registry
487// and if found, fill in the data and return true, except return false.
488
489static bool
490reg_dir_found(char * name, char * data, DWORD len)
491{
492 HRESULT result = RegQueryValueEx(HKEY_CURRENT_USER, name, 0, 0,
493 (LPBYTE)data, &len);
494 if (result == ERROR_SUCCESS) {
495 struct stat info;
496 if (stat(data, &info) == 0 && (info.st_mode & S_IFDIR)) {
497 return(true);
498 }
499 }
500 return(false);
501}
502
503
504// Look for likely default folder for Mup files.
505// Use the current user's "My Music" folder if there is one,
506// otherwise try their "Personal" folder.
507// Returns path in static area or null on failure.
508
509char *
510find_music_folder(void)
511{
512 static char best_value[FL_PATH_MAX];
513
514 // Get the registry info about folders
515 HKEY key = 0;
516 // Win'98 uses "User Shell Folders" but newer versions use just
517 // "Shell Folders," so we check for both, newer first, since that
518 // is probably more likely to work.
519 if ((RegOpenKeyEx(HKEY_CURRENT_USER,
520 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
521 0, KEY_READ, &key) == ERROR_SUCCESS) ||
522 (RegOpenKeyEx(HKEY_CURRENT_USER,
523 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders",
524 0, KEY_READ, &key) == ERROR_SUCCESS)) {
525 DWORD max_key_length;
526 DWORD count;
527 DWORD max_name_length;
528 DWORD max_value_length;
529 // Find out how many subkeys there are, and max lengths.
530 if (RegQueryInfoKey(key, 0, 0, 0, 0, &max_key_length, 0, &count,
531 &max_name_length, &max_value_length, 0, 0)
532 == ERROR_SUCCESS) {
533 TCHAR name[max_name_length + 1];
534 DWORD name_length;
535 DWORD value_type;
536 BYTE value[max_value_length + 1];
537 DWORD value_length;
538 int i;
539 best_value[0] = '\0';
540 // Look for "My Music" and "Personal" subkeys.
541 // There's probably a better way to query for specific
542 // subkey than linear search, but this works...
543 for (i = 0; i < count; i++) {
544 name_length = sizeof(name);
545 value_length = sizeof(value);
546 if (RegEnumValue(key, i, name, &name_length, 0,
547 &value_type, value,
548 &value_length)
549 == ERROR_SUCCESS) {
550 if (value_type != REG_SZ) {
551 continue;
552 }
553 if (strcasecmp(name, "My Music") == 0) {
554 // Found the ones we want.
555 strcpy(best_value, (char *) value);
556 break;
557 }
558 if (strcasecmp(name, "Personal") == 0) {
559 // This is our second choice.
560 // Save as best so far.
561 strcpy(best_value, (char *) value);
562 }
563 }
564 }
565 }
566 }
567 if (key != 0) {
568 RegCloseKey(key);
569 }
570 return(best_value[0] == '\0' ? 0 : best_value);
571}
572
573#endif