1 #define EX_UTILS_NO_USE_OPEN  1
2 #define EX_UTILS_NO_USE_LIMIT 1
3 #define EX_UTILS_NO_USE_GET   1
4 #include "ex_utils.h"
5 
6 #include <sys/types.h>
7 #include <dirent.h>
8 
app_netstr_cstr(Vstr_base * s1,const char * val)9 static void app_netstr_cstr(Vstr_base *s1, const char *val)
10 {
11   size_t nb = vstr_add_netstr_beg(s1, s1->len);
12   vstr_add_cstr_buf(s1, s1->len, val);
13   vstr_add_netstr_end(s1, nb, s1->len);
14 }
app_netstr_uintmax(Vstr_base * s1,VSTR_AUTOCONF_uintmax_t val)15 static void app_netstr_uintmax(Vstr_base *s1, VSTR_AUTOCONF_uintmax_t val)
16 {
17   char buf[sizeof(val) * 8];
18   size_t nb = vstr_add_netstr_beg(s1, s1->len);
19   size_t len = vstr_sc_conv_num10_uintmax(buf, sizeof(buf), val);
20   vstr_add_buf(s1, s1->len, buf, len);
21   vstr_add_netstr_end(s1, nb, s1->len);
22 }
23 
stat_from(struct stat64 * buf,const char * from,const char * name,Vstr_base * tmp)24 static int stat_from(struct stat64 *buf, const char *from, const char *name,
25                      Vstr_base *tmp)
26 {
27   const char *full_name = NULL;
28 
29   vstr_del(tmp, 1, tmp->len);
30   vstr_add_fmt(tmp, tmp->len, "%s/%s", from, name);
31 
32   if (!(full_name = vstr_export_cstr_ptr(tmp, 1, tmp->len)) ||
33       stat64(full_name, buf))
34   {
35     warn("stat(%s)", name);
36     return (FALSE);
37   }
38 
39   vstr_del(tmp, 1, tmp->len);
40   return (TRUE);
41 }
42 
43 /* This is "dir_list", without any command line options */
main(int argc,char * argv[])44 int main(int argc, char *argv[])
45 {
46   Vstr_base *tmp = NULL;
47   Vstr_base *s1 = ex_init(&tmp); /* init the library etc. */
48   int count = 1; /* skip the program name */
49   DIR *dir = NULL;
50   struct dirent *ent = NULL;
51   int sizes = FALSE;
52   int follow = FALSE;
53 
54   /* parse command line arguments... */
55   while (count < argc)
56   { /* quick hack getopt_long */
57     if (!strcmp("--", argv[count]))
58     {
59       ++count;
60       break;
61     }
62     else if (!strcmp("--size", argv[count]))
63       sizes = !sizes;
64     else if (!strcmp("--follow", argv[count]))
65       follow = !follow;
66     else if (!strcmp("--version", argv[count]))
67     { /* print version and exit */
68       vstr_add_fmt(s1, 0, "%s", "\
69 jdir_list 1.0.0\n\
70 Written by James Antill\n\
71 \n\
72 Uses Vstr string library.\n\
73 ");
74       goto out;
75     }
76     else if (!strcmp("--help", argv[count]))
77     { /* print version and exit */
78       vstr_add_fmt(s1, 0, "%s", "\
79 Usage: jdir_list <DIRECTORY>...\n\
80    or: jdir_list OPTION\n\
81 Output filenames.\n\
82 \n\
83       --help     Display this help and exit\n\
84       --version  Output version information and exit\n\
85       --size     Stat files to output size information\n\
86       --follow   Stat symlinks to get type information\n\
87       --         Treat rest of cmd line as input filenames\n\
88 \n\
89 Report bugs to James Antill <james@and.org>.\n\
90 ");
91       goto out;
92     }
93     else
94       break;
95     ++count;
96   }
97 
98   /* if no arguments are given just do stdin to stdout */
99   if (count >= argc)
100     errx(EXIT_FAILURE, "No directory given");
101 
102   if (!(dir = opendir(argv[count])))
103     err(EXIT_FAILURE, "opendir(%s)", argv[count]);
104 
105   {
106     size_t nb = vstr_add_netstr_beg(s1, s1->len);
107 
108     app_netstr_cstr(s1, "version");
109     app_netstr_cstr(s1, "1");
110 
111     vstr_add_netstr_end(s1, nb, s1->len);
112   }
113 
114   /* readdir() == blocking, dirfd() for poll() ? */
115   while (!s1->conf->malloc_bad && (ent = readdir(dir)))
116   {
117     size_t nb = vstr_add_netstr_beg(s1, s1->len);
118     struct stat64 buf;
119     int use_stat = FALSE;
120 
121     app_netstr_cstr(s1, "name");
122     app_netstr_cstr(s1, ent->d_name);
123 
124     if (sizes &&
125         ((ent->d_type == DT_REG) || (ent->d_type == DT_UNKNOWN)))
126       use_stat = TRUE;
127 
128     if (follow &&
129         ((ent->d_type == DT_LNK) || (ent->d_type == DT_UNKNOWN)))
130       use_stat = TRUE;
131 
132     if (use_stat && stat_from(&buf, argv[count], ent->d_name, tmp))
133     {
134       app_netstr_cstr(s1, "inode");
135       app_netstr_uintmax(s1, buf.st_ino);
136 
137       if ((ent->d_type != DT_UNKNOWN) && !follow)
138       {
139         app_netstr_cstr(s1, "type");
140         app_netstr_uintmax(s1, DTTOIF(ent->d_type));
141       }
142       else
143       {
144         app_netstr_cstr(s1, "type");
145         app_netstr_uintmax(s1, buf.st_mode);
146       }
147 
148       app_netstr_cstr(s1, "size");
149       app_netstr_uintmax(s1, buf.st_size);
150     }
151     else
152     {
153       app_netstr_cstr(s1, "inode");
154       app_netstr_uintmax(s1, ent->d_ino);
155 
156       if (ent->d_type != DT_UNKNOWN)
157       {
158         app_netstr_cstr(s1, "type");
159         app_netstr_uintmax(s1, DTTOIF(ent->d_type));
160       }
161     }
162 
163     vstr_add_netstr_end(s1, nb, s1->len);
164   }
165 
166   if (s1->conf->malloc_bad)
167     errno = ENOMEM, err(EXIT_FAILURE, "readdir(%s)", argv[count]);
168 
169  out:
170   io_put_all(s1, STDOUT_FILENO);
171 
172   exit (ex_exit(s1, tmp));
173 }
174