+
+/*
+ * Return a newly allocated option string. This string is to be used as the
+ * optstring argument of getopt_long(), see GETOPT(3). opt_count is the number
+ * of elements in the long_options array. Returns NULL if the string's
+ * allocation fails.
+ */
+LTTNG_HIDDEN
+char *utils_generate_optstring(const struct option *long_options,
+ size_t opt_count)
+{
+ int i;
+ size_t string_len = opt_count, str_pos = 0;
+ char *optstring;
+
+ /*
+ * Compute the necessary string length. One letter per option, two when an
+ * argument is necessary, and a trailing NULL.
+ */
+ for (i = 0; i < opt_count; i++) {
+ string_len += long_options[i].has_arg ? 1 : 0;
+ }
+
+ optstring = zmalloc(string_len);
+ if (!optstring) {
+ goto end;
+ }
+
+ for (i = 0; i < opt_count; i++) {
+ if (!long_options[i].name) {
+ /* Got to the trailing NULL element */
+ break;
+ }
+
+ optstring[str_pos++] = (char)long_options[i].val;
+ if (long_options[i].has_arg) {
+ optstring[str_pos++] = ':';
+ }
+ }
+
+end:
+ return optstring;
+}
+
+/*
+ * Try to remove a hierarchy of empty directories, recursively. Don't unlink
+ * any file. Try to rmdir any empty directory within the hierarchy.
+ */
+LTTNG_HIDDEN
+int utils_recursive_rmdir(const char *path)
+{
+ DIR *dir;
+ int dir_fd, ret = 0, closeret, is_empty = 1;
+ struct dirent *entry;
+
+ /* Open directory */
+ dir = opendir(path);
+ if (!dir) {
+ PERROR("Cannot open '%s' path", path);
+ return -1;
+ }
+ dir_fd = dirfd(dir);
+ if (dir_fd < 0) {
+ PERROR("dirfd");
+ return -1;
+ }
+
+ while ((entry = readdir(dir))) {
+ if (!strcmp(entry->d_name, ".")
+ || !strcmp(entry->d_name, ".."))
+ continue;
+ switch (entry->d_type) {
+ case DT_DIR:
+ {
+ char subpath[PATH_MAX];
+
+ strncpy(subpath, path, PATH_MAX);
+ subpath[PATH_MAX - 1] = '\0';
+ strncat(subpath, "/",
+ PATH_MAX - strlen(subpath) - 1);
+ strncat(subpath, entry->d_name,
+ PATH_MAX - strlen(subpath) - 1);
+ if (utils_recursive_rmdir(subpath)) {
+ is_empty = 0;
+ }
+ break;
+ }
+ case DT_REG:
+ is_empty = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+end:
+ closeret = closedir(dir);
+ if (closeret) {
+ PERROR("closedir");
+ }
+ if (is_empty) {
+ DBG3("Attempting rmdir %s", path);
+ ret = rmdir(path);
+ }
+ return ret;
+}