summaryrefslogtreecommitdiff
path: root/src/filename_list.cxx
blob: 806df067193327ba652b8f51e4c4a3c1a9e2a050 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//
// "$Id$"
//
// Filename list routines for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2010 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
//

// Wrapper for scandir with const-correct function prototypes.

#include <FL/filename.H>
#include <FL/fl_utf8.h>
#include "flstring.h"
#include <stdlib.h>
#ifdef __APPLE__  // PORTME: Fl_System_Driver - directory stuff
#include <FL/x.H>
#endif

#ifdef WIN32
#elif defined(__APPLE__) // PORTME: Fl_System_Driver - directory stuff
#elif defined(FL_PORTING)
#  pragma message "FL_PORTING: implement directory and filename handling for your platform if needed"
#else // X11
#endif

extern "C" {
#ifndef HAVE_SCANDIR
  int fl_scandir (const char *dir, dirent ***namelist,
	          int (*select)(dirent *),
	          int (*compar)(dirent **, dirent **));
#endif
}

int fl_alphasort(struct dirent **a, struct dirent **b) {
  return strcmp((*a)->d_name, (*b)->d_name);
}

int fl_casealphasort(struct dirent **a, struct dirent **b) {
  return strcasecmp((*a)->d_name, (*b)->d_name);
}


/**
   Portable and const-correct wrapper for the scandir() function. 
   For each file in that directory a "dirent" structure is created. 
   The only portable thing about a dirent is that dirent.d_name is the nul-terminated file name. 
   An pointers array to these dirent's is created and a pointer to the array is returned in *list.
   The number of entries is given as a return value. 
   If there is an error reading the directory a number less than zero is returned, 
   and errno has the reason; errno does not work under WIN32. 

   \b Include:
   \code
   #include <FL/filename.H>
   \endcode

   \param[in] d the name of the directory to list.  It does not matter if it has a trailing slash.
   \param[out] list table containing the resulting directory listing
   \param[in] sort sorting functor:
    - fl_alphasort: The files are sorted in ascending alphabetical order; 
        upper and lowercase letters are compared according to their ASCII ordering  uppercase before lowercase.
    - fl_casealphasort: The files are sorted in ascending alphabetical order; 
        upper and lowercase letters are compared equally case is not significant.
    - fl_casenumericsort: The files are sorted in ascending "alphanumeric" order, where an attempt is made 
        to put unpadded numbers in consecutive order; upper and lowercase letters 
        are compared equally case is not significant.
    - fl_numericsort: The files are sorted in ascending "alphanumeric" order, where an attempt is made 
        to put unpadded numbers in consecutive order; upper and lowercase letters are compared 
        according to their ASCII ordering - uppercase before lowercase. 
   \return the number of entries if no error, a negative value otherwise.
*/
int fl_filename_list(const char *d, dirent ***list,
                     Fl_File_Sort_F *sort) {
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(HAVE_SCANDIR)
  // For Windows we have a special scandir implementation that uses
  // the Win32 "wide" functions for lookup, avoiding the code page mess
  // entirely. It also fixes up the trailing '/'.
  return fl_scandir(d, list, 0, sort);

#else // WIN32

  int dirlen;
  char *dirloc;

  // Assume that locale encoding is no less dense than UTF-8
  dirlen = strlen(d);
#ifdef __APPLE__  // PORTME: Fl_System_Driver - directory stuff
  dirloc = (char *)d;
#else
  dirloc = (char *)malloc(dirlen + 1);
  fl_utf8to_mb(d, dirlen, dirloc, dirlen + 1);
#endif

#ifndef HAVE_SCANDIR
  // This version is when we define our own scandir
  int n = fl_scandir(dirloc, list, 0, sort);
#elif defined(HAVE_SCANDIR_POSIX) && !defined(__APPLE__)  // PORTME: Fl_System_Driver - directory stuff
  // POSIX (2008) defines the comparison function like this:
  int n = scandir(dirloc, list, 0, (int(*)(const dirent **, const dirent **))sort);
#elif defined(__osf__)
  // OSF, DU 4.0x
  int n = scandir(dirloc, list, 0, (int(*)(dirent **, dirent **))sort);
#elif defined(_AIX)
  // AIX is almost standard...
  int n = scandir(dirloc, list, 0, (int(*)(void*, void*))sort);
#elif defined(__sgi)
  int n = scandir(dirloc, list, 0, sort);
#elif defined(__APPLE__)  // PORTME: Fl_System_Driver - directory stuff
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
  int n = scandir(dirloc, list, 0, (int(*)(const struct dirent**,const struct dirent**))sort);
# else
  int n = scandir(dirloc, list, 0, (int(*)(const void*,const void*))sort);
# endif
#elif defined(FL_PORTING)
#  pragma message "FL_PORTING: defien scandir"
  int n = 0;
#else
  // The vast majority of UNIX systems want the sort function to have this
  // prototype, most likely so that it can be passed to qsort without any
  // changes:
  int n = scandir(dirloc, list, 0, (int(*)(const void*,const void*))sort);
#endif

#ifndef __APPLE__ // PORTME: Fl_System_Driver - directory stuff
  free(dirloc);
#endif

  // convert every filename to utf-8, and append a '/' to all
  // filenames that are directories
  int i;
  char *fullname = (char*)malloc(dirlen+FL_PATH_MAX+3); // Add enough extra for two /'s and a nul
  // Use memcpy for speed since we already know the length of the string...
  memcpy(fullname, d, dirlen+1);

  char *name = fullname + dirlen;
  if (name!=fullname && name[-1]!='/')
    *name++ = '/';

  for (i=0; i<n; i++) {
    int newlen;
    dirent *de = (*list)[i];
    int len = strlen(de->d_name);
#ifdef __APPLE__ // PORTME: Fl_System_Driver - directory stuff
    newlen = len;
#else
    newlen = fl_utf8from_mb(NULL, 0, de->d_name, len);
#endif
    dirent *newde = (dirent*)malloc(de->d_name - (char*)de + newlen + 2); // Add space for a / and a nul

    // Conversion to UTF-8
    memcpy(newde, de, de->d_name - (char*)de);
#ifdef __APPLE__ // PORTME: Fl_System_Driver - directory stuff
    strcpy(newde->d_name, de->d_name);
#else
    fl_utf8from_mb(newde->d_name, newlen + 1, de->d_name, len);
#endif

    // Check if dir (checks done on "old" name as we need to interact with
    // the underlying OS)
    if (de->d_name[len-1]!='/' && len<=FL_PATH_MAX) {
      // Use memcpy for speed since we already know the length of the string...
      memcpy(name, de->d_name, len+1);
      if (fl_filename_isdir(fullname)) {
        char *dst = newde->d_name + newlen;
        *dst++ = '/';
        *dst = 0;
      }
    }

    free(de);
    (*list)[i] = newde;
  }
  free(fullname);

  return n;

#endif // WIN32
}

/**
 \brief Free the list of filenames that is generated by fl_filename_list().
 
 Free everything that was allocated by a previous call to fl_filename_list().
 Use the return values as parameters for this function.
 
 \param[in,out] list table containing the resulting directory listing
 \param[in] n number of entries in the list
 */
void fl_filename_free_list(struct dirent ***list, int n)
{
  if (n<0) return;
  
  int i;
  for (i = 0; i < n; i ++) {
    if ((*list)[i])
      free((*list)[i]);
  }  
  free(*list);
  *list = 0;
}


//
// End of "$Id$".
//