1 /*
2  * Display directory contents
3  */
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <console.h>
7 #include <string.h>
8 #include <com32.h>
9 #include <dirent.h>
10 #include <minmax.h>
11 #include <unistd.h>
12 #include <getkey.h>
13 
14 static int rows, cols;		/* Screen parameters */
15 
16 #define DIR_CHUNK	1024
17 
type_str(int type)18 static const char *type_str(int type)
19 {
20     switch (type) {
21     case DT_FIFO:
22 	return "[fif]";
23     case DT_CHR:
24 	return "[chr]";
25     case DT_DIR:
26 	return "[dir]";
27     case DT_BLK:
28 	return "[blk]";
29     case DT_UNKNOWN:
30     case DT_REG:
31 	return "";
32     case DT_LNK:
33 	return "[lnk]";
34     case DT_SOCK:
35 	return "[sck]";
36     case DT_WHT:
37 	return "[wht]";
38     default:
39 	return "[???]";
40     }
41 }
42 
free_dirents(struct dirent ** dex,size_t n_de)43 static void free_dirents(struct dirent **dex, size_t n_de)
44 {
45     size_t i;
46 
47     for (i = 0; i < n_de; i++)
48 	free(dex[i]);
49 
50     free(dex);
51 }
52 
compare_dirent(const void * p_de1,const void * p_de2)53 static int compare_dirent(const void *p_de1, const void *p_de2)
54 {
55     const struct dirent *de1 = *(const struct dirent **)p_de1;
56     const struct dirent *de2 = *(const struct dirent **)p_de2;
57     int ndir1, ndir2;
58 
59     ndir1 = de1->d_type != DT_DIR;
60     ndir2 = de2->d_type != DT_DIR;
61 
62     if (ndir1 != ndir2)
63 	return ndir1 - ndir2;
64 
65     return strcmp(de1->d_name, de2->d_name);
66 }
67 
display_directory(const char * dirname)68 static int display_directory(const char *dirname)
69 {
70     DIR *dir;
71     struct dirent *de;
72     struct dirent **dex = NULL;
73     size_t n_dex = 0, n_de = 0;
74     size_t i, j, k;
75     size_t nrows, ncols, perpage;
76     size_t endpage;
77     int maxlen = 0;
78     int pos, tpos, colwidth;
79 
80     dir = opendir(dirname);
81     if (!dir) {
82 	printf("Unable to read directory: %s\n", dirname);
83 	return -1;
84     }
85 
86     while ((de = readdir(dir)) != NULL) {
87 	struct dirent *nde;
88 
89 	if (n_de >= n_dex) {
90 	    struct dirent **ndex;
91 
92 	    ndex = realloc(dex, (n_dex + DIR_CHUNK) * sizeof *dex);
93 	    if (!ndex)
94 		goto nomem;
95 
96 	    dex = ndex;
97 	    n_dex += DIR_CHUNK;
98 	}
99 
100 	nde = malloc(de->d_reclen);
101 	if (!nde)
102 	    goto nomem;
103 
104 	memcpy(nde, de, de->d_reclen);
105 	dex[n_de++] = nde;
106 
107 	maxlen = max(maxlen, de->d_reclen);
108     }
109 
110     closedir(dir);
111 
112     qsort(dex, n_de, sizeof *dex, compare_dirent);
113 
114     maxlen -= offsetof(struct dirent, d_name) + 1;
115     ncols = (cols + 2)/(maxlen + 8);
116     ncols = min(ncols, n_de);
117     ncols = max(ncols, 1U);
118     colwidth = (cols + 2)/ncols;
119     perpage = ncols * (rows - 1);
120 
121     for (i = 0; i < n_de; i += perpage) {
122 	/* Rows on this page */
123 	endpage = min(i+perpage, n_de);
124 	nrows = ((endpage-i) + ncols - 1)/ncols;
125 
126 	for (j = 0; j < nrows; j++) {
127 	    pos = tpos = 0;
128 	    for (k = i+j; k < endpage; k += nrows) {
129 		pos += printf("%*s%-5s %s",
130 			      (tpos - pos), "",
131 			      type_str(dex[k]->d_type),
132 			      dex[k]->d_name);
133 		tpos += colwidth;
134 	    }
135 	    printf("\n");
136 	}
137 
138 	if (endpage >= n_de)
139 	    break;
140 
141 	get_key(stdin, 0);
142     }
143 
144     free_dirents(dex, n_de);
145     return 0;
146 
147 nomem:
148     closedir(dir);
149     printf("Out of memory error!\n");
150     free_dirents(dex, n_de);
151     return -1;
152 }
153 
main(int argc,char * argv[])154 int main(int argc, char *argv[])
155 {
156     int rv;
157 
158     if (getscreensize(1, &rows, &cols)) {
159 	/* Unknown screen size? */
160 	rows = 24;
161 	cols = 80;
162     }
163 
164     if (argc < 2)
165 	rv = display_directory(".");
166     else if (argc == 2)
167 	rv = display_directory(argv[1]);
168     else {
169 	printf("Usage: dir directory\n");
170 	rv = 1;
171     }
172 
173     return rv ? 1 : 0;
174 }
175 
176