0

I am working on a game, and I need a program that will find random filenames from about the system. I am unsure as to how I should go about it.

Here is what I have so far: Note, I need help with the get_random_files function. I don't exactly know how to grab random files in a quick and memory extensive way.

 char islld(char *path)
 {
      DIR *d = opendir(path);
      struct dirent *ds;
      struct stat st;
      char *buf = malloc(strlen(path) + 1024), ret = -1;
      while ((ds = readdir(d)) != NULL)
      {
           sprintf(buf, "%s/%s", path, ds->d_name);
           stat(buf, &st);
           if (S_ISDIR(st.st_mode))
                goto err;
      }
      ret = 1;
 err:
      ret = ret < 0 ? 0 : ret;
      closedir(d);
      free(buf);
      return ret;
 }

 char hasfiles(char *path)
 {
      DIR *d = opendir(path);
      struct dirent *ds;
      struct stat st;
      char *buf = malloc(strlen(path) + 1024);
      while ((ds = readdir(d)) != NULL)
      {
           sprintf(buf, "%s/%s", path, ds->d_name);
           stat(buf, &st);
           if (S_ISREG(st.st_mode))
           {
                free(buf);
                closedir(d);
                return 1;
           }
      }
      free(buf);
      closedir(d);
      return 0;
 }

 tlist *getdirs(char *path)
 {
      tlist *ret = tlist_init();
      DIR *d = opendir(path);
      struct dirent *ds;
      struct stat st;
      char *buf = malloc(strlen(path) + 1024);
      while ((ds = readdir(d)) != NULL)
      {
           sprintf(buf, "%s/%s", path, ds->d_name);
           stat(buf, &st);
           if (S_ISDIR(st.st_mode))
                tlist_insert(ret, buf, (unsigned int)strlen(buf) + 1);
      }
      free(buf);
      closedir(d);
      return ret;
 }

 tlist *getfiles(char *path)
 {
      tlist *ret = tlist_init();
      DIR *d = opendir(path);
      struct dirent *ds;
      struct stat st;
      char *buf = malloc(strlen(path) + 1024);
      while ((ds = readdir(d)) != NULL)
      {
           sprintf(buf, "%s/%s", path, ds->d_name);
           stat(buf, &st);
           if (S_ISREG(st.st_mode))
                tlist_insert(ret, buf, (unsigned int)strlen(buf) + 1);
      }
      free(buf);
      closedir(d);
      return ret;
 }

 tlist *get_random_files(char *basepath, int num)
 {
      tlist *dirs = NULL, *files = NULL, *retfiles = tlist_init();
      char buf[4096]; //should be plenty
      int i, j, nf;
      strcpy(buf, basepath);
      for (nf = 0; nf < num;)
      {
           if (files == NULL) files = tlist_init();
           if (dirs == NULL)
           {
                if (!islld(buf))
                     if (hasfiles(buf))
                          files = getfiles(buf);
           }
      }
      return NULL;
 }

I don't know if I need to just completely scrap that whole thing, but I am in need of assistance in completing the get_random_files function. For reference, here is my tlist definition:

struct typelesslist_node
{
    void *data;
    struct typelesslist_node *next;
    unsigned int size;
};

typedef struct typelesslist_node tlist;

tlist * tlist_init(void);
void tlist_insert(tlist *list, void *data, unsigned int size);
tlist * tlist_remove(tlist *list, unsigned int key);
void tlist_get(tlist *list, void *dest, unsigned int key);
tlist * tlist_free(tlist *list);
void tlist_insert_list(tlist *dest, tlist *src);

again, any help at all is much appreciated. I would like to steer away from things like C++, boost, etc. My hopes are that this program doesn't depend on any external libraries.

Thanks.

phyrrus9
  • 1,441
  • 11
  • 26
  • Are there any constraints for which files are allowed? (device-nodes, remote-file-systems, fifos, ...) (config-files, program-files, password-files, ...) – Deduplicator Apr 28 '14 at 22:38
  • I would like just any file that passes S_IFREG. no restraints other than that. – phyrrus9 Apr 28 '14 at 22:39
  • "Should be plenty" ... Until someone makes a loop or stacks deep. Why not use such nodes: `typedef struct node{struct node* next;size_t size;char data[];} node;`? Avoids half your allocations. – Deduplicator Apr 28 '14 at 22:43
  • any file that is not a symlink and passes S_IFREG then lol...and to ignore directories that are links. – phyrrus9 Apr 28 '14 at 22:44
  • How about mount-loops/net-mount-loops? What do you want to do with it anyway? Please read [Limits for file name length](http://stackoverflow.com/questions/6571435/limit-on-file-name-length-in-bash) – Deduplicator Apr 28 '14 at 22:48
  • Must you select any random file from *anywhere* on your entire hard disk? Scanning all files may take a while. I notice you are thinking of some constraint (the `num` parameter in get_random_files) .. but what if your basepath happens to point to a 10-level deep folder containing just one file? These do exist on my system ... – Jongware Apr 28 '14 at 22:51
  • Anything publicly accessible with a path (/mnt/netwhatever/hello/file.out is a valid option). I am making a hangman game that randomly deletes files instead of actually drawing a man and a nuce. – phyrrus9 Apr 28 '14 at 22:51
  • @Jongware I don't plan to scan through all files.. I plan to just select a few (no more than 30) random files from the system. – phyrrus9 Apr 28 '14 at 22:52
  • Were you planning on warning your future users!? Enjoy testing this ... – Jongware Apr 28 '14 at 22:54
  • @Jongware I plan on testing it in a vm, and yes, there will be ample warnings. – phyrrus9 Apr 28 '14 at 22:55
  • Other pertinent background reading: [Where is PATH_MAX defined in Linux?](http://stackoverflow.com/questions/9449241/where-is-path-max-defined-in-linux) – Deduplicator Apr 28 '14 at 22:57
  • @Deduplicator I don't plan on getting a filename over 1024 bytes long, so I am far away from that one. Any thoughts? – phyrrus9 Apr 28 '14 at 22:58

1 Answers1

0

I figured out a solution. Get the number of entries per directory, take that number as the mod of arc4random, then if that is a regular file, add it to the list, else recurse into that directory and repeat.

#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <curses.h>
#include <sys/dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "typelesslist.h"

#define DEBUGV 0
#define MIN_LEVELS 4

 int numentries(char *path)
 {
      int ret = 0;
      DIR *d;
      struct dirent *ds;
      struct stat st;
      char *buf;
      if ((d = opendir(path)) == NULL) return -1;
      buf = malloc(strlen(path) + 1024);
      while ((ds = readdir(d)) != NULL)
      {
           if (strcmp(ds->d_name, ".") == 0 || strstr(ds->d_name, "./") != NULL) continue;
           sprintf(buf, "%s/%s", path, ds->d_name);
           stat(buf, &st);
           if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
                ++ret;
      }
      free(buf);
      closedir(d);
      return ret;
 }

 char *fullnamefromkey(char *path, int key)
 {
      DIR *d;
      struct dirent *ds;
      struct stat st;
      char *buf;
      int i = 0;
      if ((d = opendir(path)) == NULL) return NULL;
      buf = malloc(strlen(path) + 1024);
      while ((ds = readdir(d)) != NULL)
      {
           sprintf(buf, "%s/%s", path, ds->d_name);
           stat(buf, &st);
           if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
           {
                if (i++ == key)
                {
                     closedir(d);
                     return buf;
                }
           }
      }
      return NULL; //this should never happen
 }

 int countstr(char *str, char * c)
 {
      if (strstr(str, c) == NULL)
           return 0;
      return 1 + countstr(strstr(str, c) + 1, c);
 }

 void minimize_path(char *s)
 {
      int i, j;
      do
      {
           for (i = 0; i < strlen(s); i++)
           {
                if (s[i] == '/' && s[i + 1] == '/') //double /
                     for (j = i; j < strlen(s) + 1; j++)
                          s[j] = s[j + 1];
                if (s[i] == '.' && s[i + 1] == '/')
                     for (j = i; j < strlen(s) + 1; j++)
                          s[j] = s[j + 2];
           }
      } while (strstr(s, "//") != NULL);
 }

 tlist *get_random_files(char *basepath, int num)
 {
      tlist *retfiles = tlist_init(), *ttmp;
      char *tmpbuf; //should be plenty
      struct stat st;
      int i, ne = numentries(basepath), found = 0;
      if (DEBUGV) printf("[get_random_files] enter with basepath=%s and num=%d\n", basepath, num);
      while (found < num || num == -1)
      {
           if (ne < 5) return NULL;
           found = tlist_num_entries(retfiles);
           do
           {
                i = arc4random() % ne;
                tmpbuf = fullnamefromkey(basepath, i);
           } while (tmpbuf == NULL);
           stat(tmpbuf, &st);
           if (S_ISREG(st.st_mode))
           {
                minimize_path(tmpbuf);
                if (strstr(tmpbuf, "/./") != NULL || strstr(tmpbuf, " ") != NULL) continue;
                if (countstr(tmpbuf, "/") > MIN_LEVELS && strstr(tmpbuf, "..") == NULL)
                     tlist_insert(retfiles, tmpbuf, (unsigned int)strlen(tmpbuf) + 1);
                free(tmpbuf);
           }
           else //is a directory
           {
                if (DEBUGV) printf("[get_random_files] recursing with basepath=%s\n", basepath);
                ttmp = get_random_files(tmpbuf, -1);
                if (DEBUGV) printf("[get_random_files] exited recursion\n");
                if (ttmp != NULL)
                {
                     tlist_insert_list(retfiles, ttmp);
                     ttmp = tlist_free(ttmp);
                }
                //free(tmpbuf);
           }
           if (num == -1)
                break;
      }
      return retfiles;
 }

 int main(int argc, char * * argv)
 {
      tlist *l = get_random_files("/", 5), *t = l;
      char buf[1024];
      //srand(time(0));
      while (t != NULL)
      {
           if (t->data != NULL)
           {
                strcpy(buf, t->data);
                printf("Got file %s\n", buf);
                t = t->next;
           }
           else break;
      }
      tlist_free(l);
 }
phyrrus9
  • 1,441
  • 11
  • 26