1 /* filesel.c: File selection dialog box
2    Copyright (c) 2001-2015 Matan Ziv-Av, Philip Kendall, Russell Marks,
3 			   Marek Januszewski
4    Copyright (c) 2015 Sergio Baldoví
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20    Author contact information:
21 
22    E-mail: philip-fuse@shadowmagic.org.uk
23 
24 */
25 
26 #include <config.h>
27 
28 #include <sys/types.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #ifdef HAVE_STRINGS_STRCASECMP
34 #include <strings.h>
35 #endif      /* #ifdef HAVE_STRINGS_STRCASECMP */
36 #include <sys/stat.h>
37 #include <unistd.h>
38 
39 #ifdef WIN32
40 #include <windows.h>
41 #include <direct.h>
42 #include <ctype.h>
43 #endif				/* #ifdef WIN32 */
44 
45 #include "fuse.h"
46 #include "ui/ui.h"
47 #include "utils.h"
48 #include "widget_internals.h"
49 
50 #if defined AMIGA || defined __MORPHOS__
51 #include <proto/asl.h>
52 #include <proto/dos.h>
53 #include <proto/exec.h>
54 
55 struct Library *AslBase;
56 
57 #ifndef __MORPHOS__
58 struct AslIFace *IAsl;
59 struct Library *DOSBase;
60 #endif				/* #ifndef __MORPHOS__ */
61 
62 #ifndef __MORPHOS__
63 struct DOSIFace *IDOS;
64 struct Library *ExecBase;
65 #endif				/* #ifndef __MORPHOS__ */
66 
67 
68 int err = 0;
69 
70 char *amiga_asl( char *title, BOOL is_saving );
71 
72 #endif /* ifdef AMIGA */
73 
74 struct widget_dirent **widget_filenames; /* Filenames in the current
75 					    directory */
76 size_t widget_numfiles;	  /* The number of files in the current
77 			     directory */
78 
79 static const char *title;
80 static int is_saving;
81 
82 #ifdef WIN32
83 static int is_drivesel = 0;
84 static int is_rootdir;
85 #endif				/* #ifdef WIN32 */
86 
87 #define ENTRIES_PER_SCREEN (is_saving ? 32 : 36)
88 
89 /* The number of the filename in the top-left corner of the current
90    display, that of the filename which the `cursor' is on, and that
91    which it will be on after this keypress */
92 static size_t top_left_file, current_file, new_current_file;
93 
94 static char *widget_get_filename( const char *title, int saving );
95 
96 static int widget_add_filename( int *allocated, int *number,
97                                 struct widget_dirent ***namelist,
98                                 const char *name );
99 static void widget_scan( char *dir );
100 static int widget_select_file( const char *name );
101 static int widget_scan_compare( const widget_dirent **a,
102 				const widget_dirent **b );
103 
104 #if !defined AMIGA && !defined __MORPHOS__
105 static char* widget_getcwd( void );
106 #endif /* ifndef AMIGA */
107 static int widget_print_all_filenames( struct widget_dirent **filenames, int n,
108 				       int top_left, int current,
109 				       const char *dir );
110 static int widget_print_filename( struct widget_dirent *filename, int position,
111 				  int inverted );
112 #ifdef WIN32
113 static void widget_filesel_chdrv( void );
114 static void widget_filesel_drvlist( void );
115 #endif				/* #ifdef WIN32 */
116 static int widget_filesel_chdir( void );
117 
118 /* The filename to return */
119 char* widget_filesel_name;
120 
121 /* Should we exit all widgets when we're done with this selector? */
122 static int exit_all_widgets;
123 
124 static char *
widget_get_filename(const char * title,int saving)125 widget_get_filename( const char *title, int saving )
126 {
127   char *filename = NULL;
128 
129   widget_filesel_data data;
130 
131   data.exit_all_widgets = 1;
132   data.title = title;
133 
134   if( saving ) {
135     widget_do_fileselector_save( &data );
136   } else {
137     widget_do_fileselector( &data );
138   }
139   if( widget_filesel_name )
140     filename = utils_safe_strdup( widget_filesel_name );
141 
142   return filename;
143 
144 }
145 
146 char *
ui_get_open_filename(const char * title)147 ui_get_open_filename( const char *title )
148 {
149 #if !defined AMIGA && !defined __MORPHOS__
150   return widget_get_filename( title, 0 );
151 #else
152   return amiga_asl( title, FALSE );
153 #endif
154 }
155 
156 char *
ui_get_save_filename(const char * title)157 ui_get_save_filename( const char *title )
158 {
159 #if !defined AMIGA && !defined __MORPHOS__
160   return widget_get_filename( title, 1 );
161 #else
162   return amiga_asl( title, TRUE );
163 #endif
164 }
165 
widget_add_filename(int * allocated,int * number,struct widget_dirent *** namelist,const char * name)166 static int widget_add_filename( int *allocated, int *number,
167                                 struct widget_dirent ***namelist,
168                                 const char *name ) {
169   int i; size_t length;
170 
171   if( ++*number > *allocated ) {
172     struct widget_dirent **oldptr = *namelist;
173 
174     *namelist = realloc( (*namelist), 2 * *allocated * sizeof(**namelist) );
175     if( *namelist == NULL ) {
176       for( i=0; i<*number-1; i++ ) {
177 	free( oldptr[i]->name );
178 	free( oldptr[i] );
179       }
180       free( oldptr );
181       return -1;
182     }
183     *allocated *= 2;
184   }
185 
186   (*namelist)[*number-1] = malloc( sizeof(***namelist) );
187   if( !(*namelist)[*number-1] ) {
188     for( i=0; i<*number-1; i++ ) {
189       free( (*namelist)[i]->name );
190       free( (*namelist)[i] );
191     }
192     free( *namelist );
193     *namelist = NULL;
194     return -1;
195   }
196 
197   length = strlen( name ) + 1;
198   if( length < 16 ) length = 16;
199 
200   (*namelist)[*number-1]->name = malloc( length );
201   if( !(*namelist)[*number-1]->name ) {
202     free( (*namelist)[*number-1] );
203     for( i=0; i<*number-1; i++ ) {
204       free( (*namelist)[i]->name );
205       free( (*namelist)[i] );
206     }
207     free( *namelist );
208     *namelist = NULL;
209     return -1;
210   }
211 
212   strncpy( (*namelist)[*number-1]->name, name, length );
213   (*namelist)[*number-1]->name[ length - 1 ] = 0;
214 
215   return 0;
216 }
217 
218 #if defined AMIGA || defined __MORPHOS__
219 char *
amiga_asl(char * title,BOOL is_saving)220 amiga_asl( char *title, BOOL is_saving ) {
221   char *filename;
222   struct FileRequester *filereq;
223 
224 #ifndef __MORPHOS__
225   if( AslBase = IExec->OpenLibrary( "asl.library", 52 ) ) {
226     if( IAsl = ( struct AslIFace * ) IExec->GetInterface( AslBase,"main",1,NULL ) ) {
227       filereq = IAsl->AllocAslRequestTags( ASL_FileRequest,
228                                            ASLFR_RejectIcons,TRUE,
229                                            ASLFR_TitleText,title,
230                                            ASLFR_DoSaveMode,is_saving,
231                                            ASLFR_InitialPattern,"#?.(sna|z80|szx|sp|snp|zxs|tap|tzx|csw|rzx|dsk|trd|scl|mdr|dck|hdf|rom|psg|scr|mlt|png|gz|bz2)",
232                                            ASLFR_DoPatterns,TRUE,
233                                            TAG_DONE );
234       if( err = IAsl->AslRequest( filereq, NULL ) ) {
235         filename = ( STRPTR ) IExec->AllocVec( 1024, MEMF_CLEAR );
236 #else				/* #ifndef __MORPHOS__ */
237   if( AslBase = OpenLibrary( "asl.library", 0 ) ) {
238       filereq = AllocAslRequestTags( ASL_FileRequest,
239                                      ASLFR_RejectIcons,TRUE,
240                                      ASLFR_TitleText,title,
241                                      ASLFR_DoSaveMode,is_saving,
242                                      ASLFR_InitialPattern,"#?.(sna|z80|szx|sp|snp|zxs|tap|tzx|csw|rzx|dsk|trd|scl|mdr|dck|hdf|rom|psg|scr|mlt|png|gz|bz2)",
243                                      ASLFR_DoPatterns,TRUE,
244                                      TAG_DONE );
245       if( err = AslRequest( filereq, NULL ) ) {
246         filename = ( STRPTR ) AllocVec( 1024, MEMF_CLEAR );
247 #endif				/* #ifndef __MORPHOS__ */
248 
249         strcpy( filename,filereq->fr_Drawer );
250 #ifndef __MORPHOS__
251         IDOS->AddPart( filename, filereq->fr_File, 1024 );
252 #else				/* #ifndef __MORPHOS__ */
253         AddPart( filename, filereq->fr_File, 1024 );
254 #endif				/* #ifndef __MORPHOS__ */
255         widget_filesel_name = utils_safe_strdup( filename );
256 #ifndef __MORPHOS__
257         IExec->FreeVec( filename );
258 #else				/* #ifndef __MORPHOS__ */
259         FreeVec( filename );
260 #endif				/* #ifndef __MORPHOS__ */
261         err = WIDGET_FINISHED_OK;
262       } else {
263         err = WIDGET_FINISHED_CANCEL;
264       }
265 #ifndef __MORPHOS__
266       IExec->DropInterface( ( struct Interface * )IAsl );
267     }
268     IExec->CloseLibrary( AslBase );
269 #else				/* #ifndef __MORPHOS__ */
270     CloseLibrary( AslBase );
271 #endif				/* #ifndef __MORPHOS__ */
272   }
273   return widget_filesel_name;
274 }
275 #else /* ifdef AMIGA */
276 
277 static int widget_scandir( const char *dir, struct widget_dirent ***namelist,
278 			   int (*select_fn)(const char*) )
279 {
280   compat_dir directory;
281 
282   int allocated, number;
283   int i;
284   int done = 0;
285 
286   *namelist = malloc( 32 * sizeof(**namelist) );
287   if( !*namelist ) return -1;
288 
289   allocated = 32; number = 0;
290 
291   directory = compat_opendir( dir );
292   if( !directory ) {
293     free( *namelist );
294     *namelist = NULL;
295     return -1;
296   }
297 
298 #ifdef WIN32
299   /* Assume this is the root directory, unless we find an entry named ".." */
300   is_rootdir = 1;
301 #endif				/* #ifdef WIN32 */
302 
303   while( !done ) {
304     char name[ PATH_MAX ];
305 
306     compat_dir_result_t result =
307       compat_readdir( directory, name, sizeof( name ) );
308 
309     switch( result )
310     {
311     case COMPAT_DIR_RESULT_OK:
312       if( select_fn( name ) ) {
313 #ifdef WIN32
314         if( is_rootdir && !strcmp( name, ".." ) ) {
315           is_rootdir = 0;
316         }
317 #endif				/* #ifdef WIN32 */
318         if( widget_add_filename( &allocated, &number, namelist, name ) ) {
319           compat_closedir( directory );
320           return -1;
321         }
322       }
323       break;
324 
325     case COMPAT_DIR_RESULT_END:
326       done = 1;
327       break;
328 
329     case COMPAT_DIR_RESULT_ERROR:
330       for( i=0; i<number; i++ ) {
331         free( (*namelist)[i]->name );
332         free( (*namelist)[i] );
333       }
334       free( *namelist );
335       *namelist = NULL;
336       compat_closedir( directory );
337       return -1;
338     }
339 
340   }
341 
342   if( compat_closedir( directory ) ) {
343     for( i=0; i<number; i++ ) {
344       free( (*namelist)[i]->name );
345       free( (*namelist)[i] );
346     }
347     free( *namelist );
348     *namelist = NULL;
349     return -1;
350   }
351 
352 #ifdef WIN32
353   if( is_rootdir ) {
354     /* Add a fake ".." entry for drive selection */
355     if( widget_add_filename( &allocated, &number, namelist, ".." ) ) {
356       return -1;
357     }
358   }
359 #endif				/* #ifdef WIN32 */
360 
361   return number;
362 }
363 
364 #ifdef WIN32
365 static int widget_scandrives( struct widget_dirent ***namelist )
366 {
367   int allocated, number;
368   unsigned long drivemask;
369   int i;
370   char drive[3];
371   const char *driveletters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
372 
373   drive[1] = ':';
374   drive[2] = '\0';
375 
376   *namelist = malloc( 32 * sizeof(**namelist) );
377   if( !*namelist ) return -1;
378 
379   allocated = 32; number = 0;
380 
381   drivemask = _getdrives();
382   if( !drivemask ) {
383     free( *namelist );
384     *namelist = NULL;
385     return -1;
386   }
387 
388   for( i = 0; i < 26; i++ ) {
389     if( drivemask & 1) {
390       drive[0] = driveletters[i];
391       if( widget_add_filename( &allocated, &number, namelist, drive ) ) {
392         return -1;
393       }
394     }
395     drivemask >>= 1;
396   }
397 
398   return number;
399 }
400 #endif
401 
402 static void widget_scan( char *dir )
403 {
404   struct stat file_info;
405 
406   size_t i; int error;
407 
408   /* Free the memory belonging to the files in the previous directory */
409   for( i=0; i<widget_numfiles; i++ ) {
410     free( widget_filenames[i]->name );
411     free( widget_filenames[i] );
412   }
413 
414 #ifdef WIN32
415   if( dir ) {
416     widget_numfiles = widget_scandir( dir, &widget_filenames,
417 				      widget_select_file );
418   } else {
419     widget_numfiles = widget_scandrives( &widget_filenames );
420   }
421 #else				/* #ifdef WIN32 */
422   widget_numfiles = widget_scandir( dir, &widget_filenames,
423 				    widget_select_file );
424 #endif				/* #ifdef WIN32 */
425 
426   if( widget_numfiles == (size_t)-1 ) return;
427 
428   for( i=0; i<widget_numfiles; i++ ) {
429     error = stat( widget_filenames[i]->name, &file_info );
430     widget_filenames[i]->mode = error ? 0 : file_info.st_mode;
431   }
432 
433   qsort( widget_filenames, widget_numfiles, sizeof(struct widget_dirent*),
434 	 (int(*)(const void*,const void*))widget_scan_compare );
435 
436 }
437 
438 static int
439 widget_select_file( const char *name )
440 {
441   if( !name ) return 0;
442 
443   /* Skip current directory */
444   if( !strcmp( name, "." ) ) return 0;
445 
446 #ifndef WIN32
447   /* Skip hidden files/directories */
448   if( strlen( name ) > 1 && name[0] == '.' && name[1] != '.' ) return 0;
449 #endif				/* #ifdef WIN32 */
450 
451   return 1;
452 }
453 
454 static int widget_scan_compare( const struct widget_dirent **a,
455 				const struct widget_dirent **b )
456 {
457   int isdir1 = S_ISDIR( (*a)->mode ),
458       isdir2 = S_ISDIR( (*b)->mode );
459 
460   if( isdir1 && !isdir2 ) {
461     return -1;
462   } else if( isdir2 && !isdir1 ) {
463     return 1;
464   } else {
465     return strcmp( (*a)->name, (*b)->name );
466   }
467 
468 }
469 #endif /* ifdef AMIGA */
470 
471 /* File selection widget */
472 
473 static int
474 widget_filesel_draw( void *data )
475 {
476   widget_filesel_data *filesel_data = data;
477   char *directory;
478   int error;
479 
480   exit_all_widgets = filesel_data->exit_all_widgets;
481   title = filesel_data->title;
482 
483 #if !defined AMIGA && !defined __MORPHOS__
484 #ifdef WIN32
485   if( !is_drivesel ) {
486     directory = widget_getcwd();
487     if( directory == NULL ) return 1;
488   } else {
489     directory = NULL;
490   }
491 #else				/* #ifdef WIN32 */
492   directory = widget_getcwd();
493   if( directory == NULL ) return 1;
494 #endif				/* #ifdef WIN32 */
495 
496   widget_scan( directory );
497   new_current_file = current_file = 0;
498   top_left_file = 0;
499 
500   /* Create the dialog box */
501   error = widget_dialog_with_border( 1, 2, 30, 22 );
502   if( error ) {
503     free( directory );
504     return error;
505   }
506 
507 #ifdef WIN32
508   if( directory == NULL ) {
509     directory = utils_safe_strdup( "Drive selection" );
510   }
511 #endif				/* #ifdef WIN32 */
512 
513   /* Show all the filenames */
514   widget_print_all_filenames( widget_filenames, widget_numfiles,
515 			      top_left_file, current_file, directory );
516 
517   free( directory );
518 
519 #endif /* ifndef AMIGA */
520 
521   return 0;
522 }
523 
524 int widget_filesel_finish( widget_finish_state finished ) {
525 
526   /* Return with null if we didn't finish cleanly */
527   if( finished != WIDGET_FINISHED_OK ) {
528     if( widget_filesel_name ) free( widget_filesel_name );
529     widget_filesel_name = NULL;
530   }
531 
532   return 0;
533 }
534 
535 int
536 widget_filesel_load_draw( void *data )
537 {
538   is_saving = 0;
539   return widget_filesel_draw( data );
540 }
541 
542 int
543 widget_filesel_save_draw( void *data )
544 {
545   is_saving = 1;
546   return widget_filesel_draw( data );
547 }
548 
549 #if !defined AMIGA && !defined __MORPHOS__
550 static char* widget_getcwd( void )
551 {
552   char *directory; size_t directory_length;
553   char *ptr;
554 
555   directory_length = 64;
556   directory = malloc( directory_length * sizeof( char ) );
557   if( directory == NULL ) {
558     return NULL;
559   }
560 
561   do {
562     ptr = getcwd( directory, directory_length );
563     if( ptr ) break;
564     if( errno == ERANGE ) {
565       ptr = directory;
566       directory_length *= 2;
567       directory =
568 	(char*)realloc( directory, directory_length * sizeof( char ) );
569       if( directory == NULL ) {
570 	free( ptr );
571 	return NULL;
572       }
573     } else {
574       free( directory );
575       return NULL;
576     }
577   } while(1);
578 
579 #ifdef WIN32
580   if( directory[0] && directory[1] == ':' ) {
581     directory[0] = toupper( directory[0] );
582   }
583 #endif
584 
585   return directory;
586 }
587 
588 static int widget_print_all_filenames( struct widget_dirent **filenames, int n,
589 				       int top_left, int current,
590 				       const char *dir )
591 {
592   int i;
593   int error;
594 
595   /* Give us a clean box to start with */
596   error = widget_dialog_with_border( 1, 2, 30, 22 );
597   if( error ) return error;
598 
599   widget_printstring( 10, 16, WIDGET_COLOUR_TITLE, title );
600   if( widget_stringwidth( dir ) > 223 ) {
601     char buffer[128];
602     int prefix = widget_stringwidth( "..." ) + 1;
603     while( widget_stringwidth( dir ) > 223 - prefix ) dir++;
604     snprintf( buffer, sizeof( buffer ), "...%s", dir );
605     widget_print_title( 24, WIDGET_COLOUR_FOREGROUND, buffer );
606   } else {
607     widget_print_title( 24, WIDGET_COLOUR_FOREGROUND, dir );
608   }
609 
610   if( top_left ) widget_up_arrow( 1, 5, WIDGET_COLOUR_FOREGROUND );
611 
612   /* Print the filenames, mostly normally, but with the currently
613      selected file inverted */
614   for( i = top_left; i < n && i < top_left + ENTRIES_PER_SCREEN; i++ ) {
615     if( i == current ) {
616       widget_print_filename( filenames[i], i-top_left, 1 );
617     } else {
618       widget_print_filename( filenames[i], i-top_left, 0 );
619     }
620   }
621 
622   if( is_saving )
623   {
624     widget_printstring( 12, 22 * 8, WIDGET_COLOUR_FOREGROUND,
625 				     "\012RETURN\001 = select" );
626     widget_printstring_right( 244, 22 * 8, WIDGET_COLOUR_FOREGROUND,
627 					   "\012TAB\001 = enter name" );
628   }
629 
630   if( i < n )
631     widget_down_arrow( 1, is_saving ? 20 : 22, WIDGET_COLOUR_FOREGROUND );
632 
633   /* Display that lot */
634   widget_display_lines( 2, 22 );
635 
636   return 0;
637 }
638 
639 /* Print a filename onto the dialog box */
640 static int widget_print_filename( struct widget_dirent *filename, int position,
641 				  int inverted )
642 {
643   char buffer[64], suffix[64], *dot = 0;
644   int width, suffix_width = 0;
645   int dir = S_ISDIR( filename->mode );
646   int truncated = 0, suffix_truncated = 0;
647 
648 #define FILENAME_WIDTH 112
649 #define MAX_SUFFIX_WIDTH 56
650 
651   int x = (position & 1) ? 132 : 16,
652       y = 40 + (position >> 1) * 8;
653 
654   int foreground = WIDGET_COLOUR_FOREGROUND,
655 
656       background = inverted ? WIDGET_COLOUR_HIGHLIGHT
657                             : WIDGET_COLOUR_BACKGROUND;
658 
659   widget_rectangle( x, y, FILENAME_WIDTH, 8, background );
660 
661   strncpy( buffer, filename->name, sizeof( buffer ) - dir - 1);
662   buffer[sizeof( buffer ) - dir - 1] = '\0';
663 
664   if (dir)
665     dir = widget_charwidth( FUSE_DIR_SEP_CHR );
666   else {
667     /* get filename extension */
668     dot = strrchr( filename->name, '.' );
669 
670     /* if .gz or .bz2, we want the previous component too */
671     if( dot &&( !strcasecmp( dot, ".gz" ) || !strcasecmp( dot, ".bz2" ) ) ) {
672       char *olddot = dot;
673       *olddot = '\0';
674       dot = strrchr( filename->name, '.' );
675       *olddot = '.';
676       if (!dot)
677 	dot = olddot;
678     }
679 
680     /* if the dot is at the start of the name, ignore it */
681     if( dot == filename->name )
682       dot = 0;
683   }
684 
685   if( dot ) {
686     /* split filename at extension separator */
687     if( dot - filename->name < sizeof( buffer ) )
688       buffer[dot - filename->name] = '\0';
689 
690     /* get extension width (for display purposes) */
691     snprintf( suffix, sizeof( suffix ), "%s", dot );
692     while( ( suffix_width = ( dot && !dir )
693 	     ? widget_stringwidth( suffix ) : 0 ) > 110 ) {
694       suffix_truncated = 1;
695       suffix[strlen( suffix ) - 1] = '\0';
696     }
697   }
698 
699   while( ( width = widget_stringwidth( buffer ) ) >=
700 	 FILENAME_WIDTH - dir - ( dot ? truncated + suffix_width : 0 ) ) {
701     truncated = 2;
702     if( suffix_width >= MAX_SUFFIX_WIDTH ) {
703       suffix_truncated = 2;
704       suffix[strlen (suffix) - 1] = '\0';
705       suffix_width = widget_stringwidth (suffix);
706     }
707     else
708       buffer[strlen (buffer) - 1] = '\0';
709   }
710   if( dir )
711     strcat (buffer, FUSE_DIR_SEP_STR );
712 
713   widget_printstring( x + 1, y, foreground, buffer );
714   if( truncated )
715     widget_rectangle( x + width + 2, y, 1, 8, 4 );
716   if( dot )
717     widget_printstring( x + width + 2 + truncated, y,
718 			foreground ^ 2, suffix );
719   if( suffix_truncated )
720     widget_rectangle( x + FILENAME_WIDTH, y, 1, 8, 4 );
721 
722   return 0;
723 }
724 #endif /* ifndef AMIGA */
725 
726 #ifdef WIN32
727 static void
728 widget_filesel_chdrv( void )
729 {
730   char *fn;
731 
732   if( chdir( widget_filenames[ current_file ]->name ) ) {
733     ui_error( UI_ERROR_ERROR, "Could not change directory" );
734     return;
735   }
736 
737   is_drivesel = 0;
738   fn = widget_getcwd();
739   widget_scan( fn ); free( fn );
740   new_current_file = 0;
741   /* Force a redisplay of all filenames */
742   current_file = 1; top_left_file = 1;
743 }
744 
745 static void
746 widget_filesel_drvlist( void )
747 {
748   is_drivesel = 1;
749   widget_scan( NULL );
750   new_current_file = 0;
751   /* Force a redisplay of all filenames */
752   current_file = 1; top_left_file = 1;
753 }
754 #endif				/* #ifdef WIN32 */
755 
756 static int
757 widget_filesel_chdir( void )
758 {
759   char *fn, *ptr;
760 
761   /* Get the new directory name */
762   fn = widget_getcwd();
763   if( fn == NULL ) {
764     widget_end_widget( WIDGET_FINISHED_CANCEL );
765     return 1;
766   }
767   ptr = fn;
768   fn = realloc( fn,
769      ( strlen( fn ) + 1 + strlen( widget_filenames[ current_file ]->name ) +
770        1 ) * sizeof(char)
771   );
772   if( fn == NULL ) {
773     free( ptr );
774     widget_end_widget( WIDGET_FINISHED_CANCEL );
775     return 1;
776   }
777 #ifndef GEKKO
778   /* Wii getcwd() already has the slash on the end */
779   strcat( fn, FUSE_DIR_SEP_STR );
780 #endif				/* #ifndef GEKKO */
781   strcat( fn, widget_filenames[ current_file ]->name );
782 
783 /*
784 in Win32 errno resulting from chdir on file is EINVAL which may mean many things
785 this will not be fixed in mingw - must use native function instead
786 http://thread.gmane.org/gmane.comp.gnu.mingw.user/9197
787 */
788 
789   if( chdir( fn ) == -1 ) {
790 #ifndef WIN32
791     if( errno == ENOTDIR ) {
792 #else   /* #ifndef WIN32 */
793     if( GetFileAttributes( fn ) != FILE_ATTRIBUTE_DIRECTORY ) {
794 #endif  /* #ifndef WIN32 */
795       widget_filesel_name = fn; fn = NULL;
796       if( exit_all_widgets ) {
797 	widget_end_all( WIDGET_FINISHED_OK );
798       } else {
799 	widget_end_widget( WIDGET_FINISHED_OK );
800       }
801     }
802   } else {
803     widget_scan( fn );
804     new_current_file = 0;
805     /* Force a redisplay of all filenames */
806     current_file = 1; top_left_file = 1;
807   }
808 
809   free( fn );
810 
811   return 0;
812 }
813 
814 void
815 widget_filesel_keyhandler( input_key key )
816 {
817 #if !defined AMIGA && !defined __MORPHOS__
818   char *fn, *ptr;
819   char *dirtitle;
820 #endif
821 
822   /* If there are no files (possible on the Wii), can't really do anything */
823   if( widget_numfiles == 0 ) {
824     if( key == INPUT_KEY_Escape ) widget_end_widget( WIDGET_FINISHED_CANCEL );
825     return;
826   }
827 
828 #if defined AMIGA || defined __MORPHOS__
829   if( exit_all_widgets ) {
830     widget_end_all( err );
831   } else {
832     widget_end_widget( err );
833   }
834 #else  /* ifndef AMIGA */
835 
836   new_current_file = current_file;
837 
838   switch(key) {
839 
840 #if 0
841   case INPUT_KEY_Resize:	/* Fake keypress used on window resize */
842     widget_dialog_with_border( 1, 2, 30, 20 );
843     widget_print_all_filenames( widget_filenames, widget_numfiles,
844 				top_left_file, current_file        );
845     break;
846 #endif
847 
848   case INPUT_KEY_Escape:
849   case INPUT_JOYSTICK_FIRE_2:
850     widget_end_widget( WIDGET_FINISHED_CANCEL );
851     break;
852 
853   case INPUT_KEY_Left:
854   case INPUT_KEY_5:
855   case INPUT_KEY_h:
856   case INPUT_JOYSTICK_LEFT:
857     if( current_file > 0                 ) new_current_file--;
858     break;
859 
860   case INPUT_KEY_Down:
861   case INPUT_KEY_6:
862   case INPUT_KEY_j:
863   case INPUT_JOYSTICK_DOWN:
864     if( current_file+2 < widget_numfiles ) new_current_file += 2;
865     break;
866 
867   case INPUT_KEY_Up:
868   case INPUT_KEY_7:		/* Up */
869   case INPUT_KEY_k:
870   case INPUT_JOYSTICK_UP:
871     if( current_file > 1                 ) new_current_file -= 2;
872     break;
873 
874   case INPUT_KEY_Right:
875   case INPUT_KEY_8:
876   case INPUT_KEY_l:
877   case INPUT_JOYSTICK_RIGHT:
878     if( current_file < widget_numfiles-1 ) new_current_file++;
879     break;
880 
881   case INPUT_KEY_Page_Up:
882     new_current_file = ( current_file > ENTRIES_PER_SCREEN ) ?
883                        current_file - ENTRIES_PER_SCREEN     :
884                        0;
885     break;
886 
887   case INPUT_KEY_Page_Down:
888     new_current_file = current_file + ENTRIES_PER_SCREEN;
889     if( new_current_file >= widget_numfiles )
890       new_current_file = widget_numfiles - 1;
891     break;
892 
893   case INPUT_KEY_Home:
894     new_current_file = 0;
895     break;
896 
897   case INPUT_KEY_End:
898     new_current_file = widget_numfiles - 1;
899     break;
900 
901   case INPUT_KEY_Tab:
902     if( is_saving ) {
903       widget_text_t text_data;
904       text_data.title = title;
905       text_data.allow = WIDGET_INPUT_ASCII;
906       text_data.max_length = 30;
907       text_data.text[0] = 0;
908       if( widget_do_text( &text_data ) ||
909 	  !widget_text_text || !*widget_text_text      )
910 	break;
911       if( !compat_is_absolute_path( widget_text_text ) ) {
912 							/* relative name */
913         /* Get current dir name and allocate space for the leafname */
914         fn = widget_getcwd();
915         ptr = fn;
916         if( fn )
917     	  fn = realloc( fn, strlen( fn ) + strlen( widget_text_text ) + 2 );
918         if( !fn ) {
919           free( ptr );
920 	  widget_end_widget( WIDGET_FINISHED_CANCEL );
921 	  return;
922         }
923         /* Append the leafname and return it */
924         strcat( fn, FUSE_DIR_SEP_STR );
925         strcat( fn, widget_text_text );
926       } else {						/* absolute name */
927 	fn = utils_safe_strdup( widget_text_text );
928       }
929       widget_filesel_name = fn;
930       if( exit_all_widgets ) {
931 	widget_end_all( WIDGET_FINISHED_OK );
932       } else {
933 	widget_end_widget( WIDGET_FINISHED_OK );
934       }
935     }
936     break;
937 
938   case INPUT_KEY_Return:
939   case INPUT_KEY_KP_Enter:
940   case INPUT_JOYSTICK_FIRE_1:
941 #ifdef WIN32
942     if( is_drivesel ) {
943       widget_filesel_chdrv();
944     } else if( is_rootdir &&
945 	       !strcmp( widget_filenames[ current_file ]->name, ".." ) ) {
946       widget_filesel_drvlist();
947     } else {
948 #endif				/* #ifdef WIN32 */
949       if( widget_filesel_chdir() ) return;
950 #ifdef WIN32
951     }
952 #endif				/* #ifdef WIN32 */
953     break;
954 
955   default:	/* Keep gcc happy */
956     break;
957 
958   }
959 
960 #ifdef WIN32
961   if( is_drivesel ) {
962     dirtitle = utils_safe_strdup( "Drive selection" );
963   } else {
964 #endif				/* #ifdef WIN32 */
965     dirtitle = widget_getcwd();
966 #ifdef WIN32
967   }
968 #endif				/* #ifdef WIN32 */
969 
970   /* If we moved the cursor */
971   if( new_current_file != current_file ) {
972 
973     /* If we've got off the top or bottom of the currently displayed
974        file list, then reset the top-left corner and display the whole
975        thing */
976     if( new_current_file < top_left_file ) {
977 
978       top_left_file = new_current_file & ~1;
979       widget_print_all_filenames( widget_filenames, widget_numfiles,
980 				  top_left_file, new_current_file, dirtitle );
981 
982     } else if( new_current_file >= top_left_file+ENTRIES_PER_SCREEN ) {
983 
984       top_left_file = new_current_file & ~1;
985       top_left_file -= ENTRIES_PER_SCREEN - 2;
986       widget_print_all_filenames( widget_filenames, widget_numfiles,
987 				  top_left_file, new_current_file, dirtitle );
988 
989     } else {
990 
991       /* Otherwise, print the current file uninverted and the
992 	 new current file inverted */
993 
994       widget_print_filename( widget_filenames[ current_file ],
995 			     current_file - top_left_file, 0 );
996 
997       widget_print_filename( widget_filenames[ new_current_file ],
998 			     new_current_file - top_left_file, 1 );
999 
1000       widget_display_lines( 2, 21 );
1001     }
1002 
1003     /* Reset the current file marker */
1004     current_file = new_current_file;
1005 
1006   }
1007 
1008   free( dirtitle );
1009 #endif /* ifdef AMIGA */
1010 }
1011