1 /*!
2   \file lib/db/dbmi_base/dirent.c
3 
4   \brief DBMI Library (base) - directory entities management
5 
6   (C) 1999-2010 by the GRASS Development Team
7 
8   This program is free software under the GNU General Public License
9   (>=v2). Read the file COPYING that comes with GRASS for details.
10 
11   \author Joel Jones (CERL/UIUC)
12   \author Upgraded to GRASS 5.7 by Radim Blazek
13 */
14 
15 #include <string.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <grass/dbmi.h>
19 /* NOTE: these should come from <unistd.h> or from <sys/file.h> */
20 #define R_OK 4
21 #define W_OK 2
22 #define X_OK 1
23 
24 #include <sys/types.h>
25 #ifdef USE_DIRECT
26 # include <sys/dir.h>
27 typedef struct direct dir_entry;
28 #else
29 # include <dirent.h>
30 typedef struct dirent dir_entry;
31 #endif
32 
33 extern DIR *opendir();
34 extern dir_entry *readdir();
35 
36 static int cmp_dirent(const void *, const void *);
37 static int get_perm(char *);
38 static void sort_dirent(dbDirent *, int);
39 
40 /*!
41   \brief Read directory and build an array of dbDirent's
42 
43   Append one entry with name = NULL to mark end of array
44 
45   \param dirname directory name
46   \param[out] n number of entities
47 
48   \return pointer to dbDirent
49   \return NULL on error
50 */
db_dirent(const char * dirname,int * n)51 dbDirent *db_dirent(const char *dirname, int *n)
52 {
53     DIR *dp;
54     dir_entry *entry;
55     dbDirent *dirent;
56     int i, count;
57     char *path;
58     int len, max;
59 
60     db_clear_error();
61 
62     *n = 0;
63     dp = opendir(dirname);
64     if (dp == NULL) {
65 	db_syserror(dirname);
66 	return (dbDirent *) NULL;
67     }
68 
69 
70     /* count the number of entries and get the strlen of the longest name */
71     count = 0;
72     max = 0;
73     while ((entry = readdir(dp))) {
74 	count++;
75 	len = strlen(entry->d_name);
76 	if (len > max)
77 	    max = len;
78     }
79     rewinddir(dp);
80 
81     path = db_malloc(strlen(dirname) + max + 2);	/* extra 2 for / and NULL */
82     if (path == NULL) {
83 	closedir(dp);
84 	return (dbDirent *) NULL;
85     }
86     dirent = db_alloc_dirent_array(count);
87     if (dirent == NULL) {
88 	closedir(dp);
89 	return (dbDirent *) NULL;
90     }
91     *n = count;
92     for (i = 0; i < count; i++) {
93 	entry = readdir(dp);
94 	if (entry == NULL)	/* this shouldn't happen */
95 	    break;
96 
97 	if (DB_OK != db_set_string(&dirent[i].name, entry->d_name))
98 	    break;
99 	sprintf(path, "%s/%s", dirname, entry->d_name);
100 	dirent[i].perm = get_perm(path);
101 	dirent[i].isdir = (db_isdir(path) == DB_OK);
102     }
103     closedir(dp);
104     db_free(path);
105 
106     sort_dirent(dirent, *n);
107 
108     return dirent;
109 }
110 
111 /*!
112   \brief Free dbDirent
113 
114   \param dirent pointer to dbDirent
115   \param count number of entities in the array
116 */
db_free_dirent_array(dbDirent * dirent,int count)117 void db_free_dirent_array(dbDirent * dirent, int count)
118 {
119     int i;
120 
121     if (dirent) {
122 	for (i = 0; i < count; i++)
123 	    db_free_string(&dirent[i].name);
124 	db_free(dirent);
125     }
126 }
127 
get_perm(char * path)128 static int get_perm(char *path)
129 {
130     int perm;
131 
132     perm = 0;
133 
134     if (access(path, R_OK) == 0)
135 	perm |= DB_PERM_R;
136     if (access(path, W_OK) == 0)
137 	perm |= DB_PERM_W;
138     if (access(path, X_OK) == 0)
139 	perm |= DB_PERM_X;
140 
141     return perm;
142 }
143 
cmp_dirent(const void * aa,const void * bb)144 static int cmp_dirent(const void *aa, const void *bb)
145 {
146     const dbDirent *a = aa;
147     const dbDirent *b = bb;
148 
149     return strcmp(db_get_string((dbString *) & a->name),
150 		  db_get_string((dbString *) & b->name));
151 }
152 
sort_dirent(dbDirent * a,int n)153 static void sort_dirent(dbDirent * a, int n)
154 {
155     qsort(a, n, sizeof(dbDirent), cmp_dirent);
156 }
157 
158 /*!
159   \brief Allocate dirent array
160 
161   \param count number of entities in the array
162 
163   \return pointer to dbDirent array
164   \return NULL on failure
165 */
db_alloc_dirent_array(int count)166 dbDirent *db_alloc_dirent_array(int count)
167 {
168     int i;
169     dbDirent *dirent;
170 
171     dirent = (dbDirent *) db_calloc(count, sizeof(dbDirent));
172     if (dirent == NULL)
173 	return dirent;
174 
175     for (i = 0; i < count; i++)
176 	db_init_string(&dirent[i].name);
177 
178     return dirent;
179 }
180