-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathEDE_DirTree.cpp
348 lines (282 loc) · 10.8 KB
/
EDE_DirTree.cpp
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/*
* $Id$
*
* EDE Directory tree class
* Part of edelib.
* Copyright (c) 2005-2007 EDE Authors.
*
* This program is licenced under terms of the
* GNU General Public Licence version 2 or newer.
* See COPYING for details.
*/
#include "EDE_DirTree.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
// This library is hopelessly single-platform
#include <FL/Fl_Shared_Image.H>
#include <FL/fl_ask.H>
#include <edelib/Nls.h>
#include <edelib/IconTheme.h>
#include <edelib/IconLoader.h>
#include <edelib/StrUtil.h>
#define MAX_TREE_DEPTH 100
#define FL_PATH_MAX 256
// Fn that makes sure path ends with slash
// (ensure that path allocation is large enough to take one extra char)
void make_end_with_slash(char* path) {
int i=strlen(path);
if (path[i-1] != '/') {
path[i]='/';
path[i+1]='\0';
}
}
// Filesystem data
// TODO: find better icons for Amiga, Macintosh etc.
// TODO: use USB icon for removable media, regardless of filesystem
static struct {
char id[15];
char uiname[50];
char icon[20];
} filesystems[] = {
// Filesystems that should be ignored
{"auto", "", ""}, // removables will be added another way
{"autofs", "", ""}, // removables will be added another way
{"binfmt_misc", "", ""},
{"debugfs", "", ""},
{"devfs", "", ""},
{"devpts", "", ""},
{"fd", "", ""},
{"fdesc", "", ""},
{"kernfs", "", ""},
{"proc", "", ""},
{"procfs", "", ""},
{"swap", "", ""},
{"sysfs", "", ""},
{"tmpfs", "", ""},
{"usbfs", "", ""},
// Common filesystems
{"ext2", "Linux legacy (%s)", "drive-harddisk"},
{"ext2fs", "Linux legacy (%s)", "drive-harddisk"},
{"ext3", "Linux disk (%s)", "drive-harddisk"},
{"ext4", "Linux disk (%s)", "drive-harddisk"},
{"reiserfs", "Linux alternative (%s)", "drive-harddisk"},
{"ffs", "BSD disk (%s)", "drive-harddisk"},
{"ufs", "Unix disk (%s)", "drive-harddisk"},
{"dos", "Windows disk (%s)", "drive-harddisk"}, // I would prefer a Windows icon
{"fat", "Windows disk (%s)", "drive-harddisk"},
{"msdos", "Windows disk (%s)", "drive-harddisk"},
{"ntfs", "Windows disk (%s)", "drive-harddisk"},
{"pcfs", "Windows disk (%s)", "drive-harddisk"},
{"umsdos", "Windows disk (%s)", "drive-harddisk"},
{"vfat", "Windows disk (%s)", "drive-harddisk"},
{"cd9660", "CD-ROM (%s)", "media-cdrom"},
{"cdfs", "CD-ROM (%s)", "media-cdrom"},
{"cdrfs", "CD-ROM (%s)", "media-cdrom"},
{"hsfs", "CD-ROM (%s)", "media-cdrom"},
{"iso9660", "CD-ROM (%s)", "media-cdrom"},
{"isofs", "CD-ROM (%s)", "media-cdrom"},
{"udf", "DVD-ROM (%s)", "media-dvd"},
{"cifs", "Shared directory (%s)", "folder-remote"},
{"nfs", "Shared directory (%s)", "folder-remote"},
{"nfs4", "Shared directory (%s)", "folder-remote"},
{"smbfs", "Shared directory (%s)", "folder-remote"},
{"cramfs", "Virtual (RAM) disk (%s)", "memory"}, // no icon for ramdisk in fd.o specification or edeneu
{"mfs", "Virtual (RAM) disk (%s)", "memory"},
{"ramfs", "Virtual (RAM) disk (%s)", "memory"},
{"romfs", "Virtual (RAM) disk (%s)", "memory"},
{"union", "Virtual (RAM) disk (%s)", "memory"}, // not accurate, but good enough
{"unionfs", "Virtual (RAM) disk (%s)", "memory"}, // not accurate, but good enough
{"jfs", "IBM AIX disk (%s)", "drive-harddisk"},
{"xfs", "SGI IRIX disk (%s)", "drive-harddisk"},
// Other filesystems
{"coherent", "Unix disk (%s)", "drive-harddisk"},
{"sysv", "Unix disk (%s)", "drive-harddisk"},
{"xenix", "Unix disk (%s)", "drive-harddisk"},
{"adfs", "RiscOS disk (%s)", "drive-harddisk"},
{"filecore", "RiscOS disk (%s)", "drive-harddisk"},
{"ados", "Amiga disk (%s)", "drive-harddisk"},
{"affs", "Amiga disk (%s)", "drive-harddisk"},
{"afs", "AFS shared directory (%s)", "folder-remote"},
{"befs", "BeOS disk (%s)", "drive-harddisk"},
{"bfs", "UnixWare disk (%s)", "drive-harddisk"},
{"efs", "SGI IRIX disk (%s)", "drive-harddisk"},
{"ext", "Old Linux disk (%s)", "drive-harddisk"},
{"hfs", "Macintosh disk (%s)", "drive-harddisk"}, // Also used for HP-UX filesystem!!
{"hpfs", "OS/2 disk (%s)", "drive-harddisk"},
// {"lfs", "BSD LFS disk (%s)", "drive-harddisk"}, // This is said to not work
{"minix", "Minix disk (%s)", "drive-harddisk"},
{"ncpfs", "NetWare shared directory (%s)", "folder-remote"},
{"nwfs", "NetWare shared directory (%s)", "folder-remote"},
{"qns", "QNX disk (%s)", "drive-harddisk"},
{"supermount", "Removable disk (%s)", "media-cdrom"}, // ???
{"xiafs", "Old Linux disk (%s)", "drive-harddisk"},
// Virtual filesystems (various views of existing files)
// How often are these used?
// {"cachefs", "Unix virtual disk ?? (%s)", "memory"},
// {"lofs", "Solaris virtual disk ?? (%s)", "memory"},
// {"null", "BSD virtual disk ?? (%s)", "memory"},
// {"nullfs", "BSD virtual disk ?? (%s)", "memory"},
// {"overlay", "BSD virtual disk ?? (%s)", "memory"},
// {"portal", "BSD virtual disk ?? (%s)", "memory"},
// {"portalfs", "BSD virtual disk ?? (%s)", "memory"},
// {"umap", "BSD virtual disk ?? (%s)", "memory"},
// {"umapfs", "BSD virtual disk ?? (%s)", "memory"},
{"", "", ""}
};
//
// Initializator - loads level 1 of the directory tree
// number of items is size()
//
void DirTree::init()
{
int num_files=0;
char buffer[PATH_MAX]; // buffer for labels
clear();
// Top level icon
add(_("System"));
set_icon(1, Fl_Shared_Image::get(edelib::IconLoader::get_path("computer",edelib::ICON_SIZE_TINY).c_str()));
// TODO: use OS icons: Tux for Linux, devil for FreeBSD...
data(1, strdup("about:sysinfo"));
// Home icon
snprintf(buffer,PATH_MAX,_("%s's Home"), getenv("USER"));
add(strdup(buffer));
set_icon(2, Fl_Shared_Image::get(edelib::IconLoader::get_path("user-home",edelib::ICON_SIZE_TINY).c_str()));
strncpy(buffer,getenv("HOME"),PATH_MAX);
make_end_with_slash(buffer);
data(2, strdup(buffer));
indent(2,1);
// Root icon
add(_("Whole disk"));
edelib::String root_icon = edelib::IconLoader::get_path("folder_red",edelib::ICON_SIZE_TINY);
if (root_icon=="") // sigh...
root_icon = edelib::IconLoader::get_path("folder",edelib::ICON_SIZE_TINY);
set_icon(3, Fl_Shared_Image::get(root_icon.c_str()));
data(3,strdup("/"));
indent(3,1);
// Read mtab/fstab/whatever
num_files=3;
FILE *mtab; // /etc/mtab or /etc/mnttab file
char line[1024]; // Input line
char device[1024], mountpoint[1024], fs[1024];
// mtab doesn't contain removable entries, but we will find another way
// to add them (some day)
mtab = fopen("/etc/mnttab", "r"); // Fairly standard
if (mtab == NULL)
mtab = fopen("/etc/mtab", "r"); // More standard
if (mtab == NULL)
mtab = fopen("/etc/fstab", "r"); // Otherwise fallback to full list
if (mtab == NULL)
mtab = fopen("/etc/vfstab", "r"); // Alternate full list file
while (mtab!= NULL && fgets(line, sizeof(line), mtab) != NULL) {
if (line[0] == '#' || line[0] == '\n')
continue;
if (sscanf(line, "%s%s%s", device, mountpoint, fs) != 3)
continue;
make_end_with_slash(mountpoint);
// Stuff that should be invisible cause we already show it
if (strcmp(mountpoint,"/") == 0) continue; // root
if (strcmp(mountpoint,getenv("HOME")) == 0) continue; // home dir
// Device name without "/dev/"
char *shortdev = strrchr(device,'/');
if (shortdev==NULL) shortdev=device; else shortdev++;
// Go through list of known filesystems
for (int i=0; filesystems[i].id[0] != '\0'; i++) {
if (strcmp(fs, filesystems[i].id) == 0) {
if (filesystems[i].uiname[0] == '\0') break; // skipping this fs
snprintf(buffer, PATH_MAX, filesystems[i].uiname, shortdev);
add(strdup(buffer));
set_icon(num_files+1, Fl_Shared_Image::get( edelib::IconLoader::get_path(filesystems[i].icon,edelib::ICON_SIZE_TINY).c_str()));
data(num_files+1, strdup(mountpoint));
indent(num_files+1, 1);
num_files++;
}
}
}
fclose(mtab);
}
// FIXME: this is copied from efiler.cpp
// modification of versionsort which ignores case
int mmyversionsort(const void *a, const void *b) {
struct dirent** ka = (struct dirent**)a;
struct dirent** kb = (struct dirent**)b;
char* ma = strdup((*ka)->d_name);
char* mb = strdup((*kb)->d_name);
edelib::str_tolower((unsigned char*)ma); edelib::str_tolower((unsigned char*)mb);
int k = strverscmp(ma,mb);
free(ma); free(mb);
return k;
}
// Function that scans subdirectories of given entry and adds them to browser
// Returns false if entry has no subdirectories or it was already scanned
bool DirTree::subdir_scan(int line) {
if (line==0) return false; // we don't allow scanning of first item
if (indent(line+1) > indent(line)) return false; // apparently it's already scanned
int size=0;
dirent** files;
struct stat buf;
char fullpath[FL_PATH_MAX];
char* directory = (char*)data(line);
if (ignore_case_)
size = scandir(directory, &files, 0, (int(*)(const dirent **, const dirent**))mmyversionsort);
else
size = scandir(directory, &files, 0, versionsort);
if (size<1) return false; // Permission denied - but view will warn the user
int pos=line+1;
for(int i=0; i<size; i++) {
char *n = files[i]->d_name; //shortcut
if (i>0) free(files[i-1]); // see scandir(3)
// don't show . (current directory)
if (strcmp(n,".")==0 || strcmp(n,"..")==0) continue;
// hide files with dot except .. (up directory)
if (!show_hidden_ && (n[0] == '.') && (strcmp(n,"..")!=0)) continue;
snprintf (fullpath,FL_PATH_MAX-1,"%s%s/",directory,n); // all dirs should end with /
if (stat(fullpath,&buf)) continue; // happens when user has traverse but not read privilege
if (!S_ISDIR(buf.st_mode)) continue; // not a directory
insert(pos, strdup(n), strdup(fullpath));
set_icon(pos, Fl_Shared_Image::get(edelib::IconLoader::get_path("folder", edelib::ICON_SIZE_TINY).c_str()));
indent(pos++, indent(line)+1);
}
free(files[size-1]); free(files); // see scandir(3)
return (pos>line+1);
}
// Recursively find tree entry whose user_data() (system path)
// contains the largest portion of given path
// Best match is focused, return value tells if perfect match was found
bool DirTree::set_current(const char* path) {
int k=find_best_match(path,1);
if (k==-1) {
fprintf(stderr, "This shouldn't happen! %s\n", path);
return false;
}
expand(k);
subdir_scan(k); // expand currently selected directory
select(k,1);
middleline(k);
return (strcmp(path,(char*)data(k))==0);
}
// Find browser entry whose full path (data) is the closest match
// to the given path
// If parent is given, only entries below it will be scanned
int DirTree::find_best_match(const char* path, int parent) {
uint bestlen=0;
int bestindex=-1;
for (int i=parent; i<=size(); i++) {
if ((i!=parent) && (indent(i)<=indent(parent))) break;
char* d = (char*)data(i);
uint len = strlen(d);
if ((len>bestlen) && (strncmp((char*)path, d, len)==0)) {
bestlen=len;
bestindex=i;
}
}
if (strlen(path)!=bestlen && subdir_scan(bestindex))
return find_best_match(path, bestindex);
else
return bestindex;
}
//
// End of "$Id: FileBrowser.cxx 5071 2006-05-02 21:57:08Z fabien $".
//