[Tux4kids-commits] r358 - tuxmath/trunk/src

tholy-guest at alioth.debian.org tholy-guest at alioth.debian.org
Sat Dec 8 11:56:18 UTC 2007


Author: tholy-guest
Date: 2007-12-08 11:56:17 +0000 (Sat, 08 Dec 2007)
New Revision: 358

Modified:
   tuxmath/trunk/src/fileops.c
   tuxmath/trunk/src/tuxmath-admin.c
Log:
This completes all the self-advertised functionality of tuxmath-admin,
from creating accounts, setting up highscore configuration, and
maintaining highscores & goldstars.  Still missing is a way to
generate reports on student performance, but this might benefit from
consultation with teachers about how they'd like to see that work.


Modified: tuxmath/trunk/src/fileops.c
===================================================================
--- tuxmath/trunk/src/fileops.c	2007-12-07 21:51:45 UTC (rev 357)
+++ tuxmath/trunk/src/fileops.c	2007-12-08 11:56:17 UTC (rev 358)
@@ -398,6 +398,8 @@
 #define OPTIONS_FILENAME "options.cfg"
 #define HIGHSCORE_FILENAME "highscores.txt"
 #define GOLDSTAR_FILENAME "goldstars.txt"
+#define USER_MENU_ENTRIES_FILENAME "user_menu_entries.txt"
+#define USER_LOGIN_QUESTIONS_FILENAME "user_login_questions.txt"
 #else
 
 # define get_home getenv("HOME")
@@ -405,6 +407,8 @@
 #define OPTIONS_FILENAME "options"
 #define HIGHSCORE_FILENAME "highscores"
 #define GOLDSTAR_FILENAME "goldstars"
+#define USER_MENU_ENTRIES_FILENAME "user_menu_entries"
+#define USER_LOGIN_QUESTIONS_FILENAME "user_login_questions"
 
 #endif
 
@@ -1147,7 +1151,7 @@
   // Look for a menu_entries file
   get_user_data_dir_with_subdir(opt_path);
   strncpy(menu_entries_file,opt_path,PATH_MAX);
-  strncat(menu_entries_file,"user_menu_entries",PATH_MAX-strlen(menu_entries_file));
+  strncat(menu_entries_file,USER_MENU_ENTRIES_FILENAME,PATH_MAX-strlen(menu_entries_file));
   n_entries = 0;
   fp = fopen(menu_entries_file,"r");
   if (fp)
@@ -1171,7 +1175,7 @@
   // Look for a user_login_questions file
   get_user_data_dir_with_subdir(opt_path);
   strncpy(user_login_questions_file,opt_path,PATH_MAX);
-  strncat(user_login_questions_file,"user_login_questions",PATH_MAX-strlen(user_login_questions_file));
+  strncat(user_login_questions_file,USER_LOGIN_QUESTIONS_FILENAME,PATH_MAX-strlen(user_login_questions_file));
    n_entries = 0;
   fp = fopen(user_login_questions_file,"r");
   if (fp)

Modified: tuxmath/trunk/src/tuxmath-admin.c
===================================================================
--- tuxmath/trunk/src/tuxmath-admin.c	2007-12-07 21:51:45 UTC (rev 357)
+++ tuxmath/trunk/src/tuxmath-admin.c	2007-12-08 11:56:17 UTC (rev 358)
@@ -30,15 +30,216 @@
 //#include "../config.h"
 #endif
 
-#define USER_MENU_ENTRIES "user_menu_entries"
+#ifdef BUILD_MINGW32
+#define USER_MENU_ENTRIES_FILENAME "user_menu_entries.txt"
+#define HIGHSCORE_FILENAME "highscores.txt"
+#define GOLDSTAR_FILENAME "goldstars.txt"
+#else
+#define USER_MENU_ENTRIES_FILENAME "user_menu_entries"
+#define HIGHSCORE_FILENAME "highscores"
+#define GOLDSTAR_FILENAME "goldstars"
+#endif
 
 #define PATH_MAX 4096
+#define MAX_USERS 100000
 #define ADMINVERSION "0.1"
 
 void display_help(void);
 void usage(int err, char * cmd);
 int extract_variable(FILE *fp, const char *varname, char** value);
+int directory_crawl(const char *path);
+void free_directories(int n);
+void create_homedirs(const char *path,const char *file);
+void config_highscores(const char *path,int level);
+void unconfig_highscores(const char *path);
+void clear_highscores(const char *path);
+void clear_goldstars(const char *path);
+void clear_file(const char *path,const char *filename,const char *invoke_name);
 
+char *directory[MAX_USERS];
+int directory_level[MAX_USERS];
+
+int main(int argc, char *argv[])
+{
+  int i;
+  FILE *fp;
+  DIR *dir;
+
+  int is_creatinghomedirs = 0;
+  int is_confighighscores = 0;
+  int is_unconfighighscores = 0;
+  int is_clearinggoldstars = 0;
+  int is_clearinghighscores = 0;
+  char *path = NULL;
+  char *file = NULL;
+  int level = 0;
+  int success;
+
+  // Null-out global directory pointers
+  for (i = 0; i < MAX_USERS; i++)
+    directory[i] = NULL;
+
+  if (argc < 2) {
+    display_help();
+    exit(EXIT_FAILURE);
+  }
+
+  // Check global config file for a homedir path (must be uncommented)
+  fp = fopen(DATA_PREFIX "/missions/options", "r");
+  if (fp) {
+    extract_variable(fp,"homedir",&path);
+    fclose(fp);
+  }
+
+  // Parse the command line options
+  for (i = 1; i < argc; i++) {
+    if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
+      display_help();
+      exit(EXIT_SUCCESS);
+    }
+    else if (strcmp(argv[i], "--copyright") == 0 ||
+	     strcmp(argv[i], "-c") == 0)
+    {
+      printf(
+	"\ntuxmath-admin version " ADMINVERSION ", Copyright (C) 2007 Tim Holy\n"
+        "This program is free software; you can redistribute it and/or\n"
+        "modify it under the terms of the GNU General Public License\n"
+        "as published by the Free Software Foundation.  See COPYING.txt\n"
+	"\n"
+	"This program is distributed in the hope that it will be useful,\n"
+	"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+	"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
+	"\n");
+      exit(EXIT_SUCCESS);
+    }
+    else if (strcmp(argv[i], "--usage") == 0 ||
+	     strcmp(argv[i], "-u") == 0) {
+      usage(0, argv[0]);
+      exit(EXIT_SUCCESS);
+    }
+    else if (strcmp(argv[i], "--path") == 0) {
+      if (i+1 > argc) {
+	fprintf(stderr, "%s option requires an argument (a directory name)\n", argv[i]);
+	usage(EXIT_FAILURE, argv[0]);
+      }
+      else {
+	path = argv[i+1];
+	dir = opendir(path);  // determine whether directory exists
+	if (dir == NULL)
+	  error(EXIT_FAILURE,errno,"path:\n  %s",path);
+	closedir(dir);
+	i++; // increment so further processing skips over the argument
+      }
+    }
+    else if (strcmp(argv[i], "--level") == 0) {
+      if (i+1 > argc) {
+	fprintf(stderr, "%s option requires an argument (a level number)\n", argv[i]);
+	usage(EXIT_FAILURE, argv[0]);
+      }
+      else {
+	success = sscanf(argv[i+1],"%d",&level);
+	if (!success) {
+	  fprintf(stderr,"level: %s is not a number\n",argv[i+1]);
+	  exit(EXIT_FAILURE);
+	}
+	i++; // increment so further processing skips over the argument
+      }
+    }
+    else if (strcmp(argv[i], "--createhomedirs") == 0) {
+      is_creatinghomedirs = 1;
+      if (i+1 > argc) {
+	fprintf(stderr, "%s option requires an argument (a file name)\n", argv[i]);
+	usage(EXIT_FAILURE, argv[0]);
+      }
+      else {
+	file = argv[i+1];
+	fp = fopen(file,"r");   // determine whether the file exists
+	if (fp == NULL)
+	  error(EXIT_FAILURE,errno,"createhomedirs using:\n  %s",file);
+	fclose(fp);  // don't read it yet, do that elsewhere
+	i++; // increment so further processing skips over the argument
+      }
+    }
+    else if (strcmp(argv[i], "--confighighscores") == 0) {
+      is_confighighscores = 1;
+    }
+    else if (strcmp(argv[i], "--unconfighighscores") == 0) {
+      is_unconfighighscores = 1;
+    }
+    else if (strcmp(argv[i], "--clearhighscores") == 0) {
+      is_clearinghighscores = 1;
+    }
+    else if (strcmp(argv[i], "--cleargoldstars") == 0) {
+      is_clearinggoldstars = 1;
+    }
+    else {
+      fprintf(stderr,"Error: option %s not recognized.\n",argv[i]);
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  // All operations require a valid path, so check that now
+  if (path == NULL) {
+    fprintf(stderr,"Must have a valid path (either with --path or in the global configuration)\n");
+    usage(EXIT_FAILURE, argv[0]);
+  }
+
+  // Create homedirs
+  if (is_creatinghomedirs) {
+    if (file == NULL) {
+      fprintf(stderr,"Must specify a filename when creating homedirs\n");
+      usage(EXIT_FAILURE, argv[0]);
+    }
+    create_homedirs(path,file);
+  }
+
+  // Configure high scores
+  if (is_confighighscores) {
+    if (level == 0) {
+      fprintf(stderr,"Must specify a level when configuring highscores\n");
+      usage(EXIT_FAILURE, argv[0]);
+    }
+    config_highscores(path,level);
+  }
+
+  // Unconfigure high scores
+  if (is_unconfighighscores) {
+    unconfig_highscores(path);
+  }
+
+  // Clear high scores
+  if (is_clearinghighscores) {
+    clear_highscores(path);
+  }
+
+  // Clear gold stars
+  if (is_clearinggoldstars) {
+    clear_goldstars(path);
+  }
+   
+  return EXIT_SUCCESS;
+}
+
+
+void usage(int err, char * cmd)
+{
+  FILE * f;
+
+  if (err == 0)
+    f = stdout;
+  else
+    f = stderr;
+
+  fprintf(f,
+   "\nUsage: %s {--help | --usage | --copyright}\n"
+   "       %s [--path <directory>] --createhomedirs <file>\n"
+   "       %s [--level <levelnum>] --confighighscores\n"
+   "       %s [--path <directory>] [--clearhighscores] [--cleargoldstars]\n"
+    "\n", cmd, cmd, cmd, cmd);
+
+  exit (err);
+}
+
 void display_help(void)
 {
   printf("\ntuxmath-admin\n"
@@ -51,11 +252,15 @@
 	 "    according to the structure specified in users.csv.  See configure.pdf\n"
 	 "    for details.  The second syntax is applicable if you've defined the\n"
 	 "    homedir path in the global configuration file.\n\n"
-	 "  tuxmath-admin --confighighscores --level 2\n"
-	 "    Sets up sharing of high scores at level 2 of the hierarchy (top is\n"
-	 "    level 1).  If you've divided things as School:Grade:Classroom:User, then\n"
-	 "    this would correspond to sharing high scores among all kids in the same\n"
-	 "    grade.\n\n"
+	 "  tuxmath-admin --confighighscores --level 3\n"
+	 "    Sets up sharing of high scores at level 3 of the hierarchy (top is\n"
+	 "    level 1).  If students logging in are presented with a choice of grade,\n"
+	 "    then classroom, and then user, then level 1 is the school, level 2 is the\n"
+	 "    grade, level 3 is the classroom, and level 4 is the individual student.\n"
+	 "    So level 3 would set it up so that all kids in the same classroom would\n"
+	 "    compete for high scores.\n\n"
+	 "  tuxmath-admin --unconfighighscores\n"
+	 "    Removes any existing highscores configuration.\n\n"
 	 "  tuxmath-admin --clearhighscores\n"
 	 "    Clears high scores for all users in the location specified by the homedir\n"
 	 "    setting in the global configuration file.\n\n"
@@ -68,42 +273,10 @@
 	 );
 }
 
-// Extracts a single variable from a configuration file. Returns 1
-// on success and 0 on failure.
-int extract_variable(FILE *fp, const char *varname, char** value)
-{
-  char buf[PATH_MAX];
-  char *param_begin;
-  char *tmpvalue;
-
-  rewind(fp);  // start at the beginning of the file
-
-  // Read in a line at a time:
-  while (fgets (buf, PATH_MAX, fp)) {
-    param_begin = buf;
-    // Skip leading whitespace
-    while (isspace(*param_begin))
-      param_begin++;
-    // Skip comments
-    if ((*param_begin == ';') || (*param_begin == '#'))
-      continue;
-    // Test whether it matches the variable name
-    if (strncmp(param_begin,varname,strlen(varname)) == 0) {
-      // Find the "=" sign
-      tmpvalue = strchr(param_begin+strlen(varname), '=');
-      if (tmpvalue == NULL)
-	continue;
-      // Skip whitespace
-      while (isspace(*tmpvalue))
-	tmpvalue++;
-      // Copy the result
-      *value = strdup(tmpvalue);
-      return 1;
-    }
-  }
-  return 0;
-}
-
+// This function does the work of creating the user directory tree,
+// given the structure specified in the CSV (comma separated value)
+// file "file".  "path" is the base directory in which this tree is
+// created.
 void create_homedirs(const char *path,const char *file)
 {
   FILE *fp,*fpue;
@@ -124,10 +297,9 @@
   mode_t mask;
 
   fp = fopen(file,"r");
-  if (!fp) {
-    fprintf(stderr,"Error: couldn't open %s for reading.\n",file);
-    exit(EXIT_FAILURE);
-  }
+  if (!fp)
+    error(EXIT_FAILURE,errno,"Error: couldn't open:\n  %s for reading",file);
+
   mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | S_IXUSR | S_IXGRP | S_IXOTH;
   umask(0x0);  // make dirs read/write for everyone
   while (fgets (buf, PATH_MAX, fp)) {
@@ -177,10 +349,11 @@
       }
     }
     
-    // Parse the directories from back to front.  Blank fields at the
-    // end indicate a lack of subdirectories; blank fields at the
+    // Parse the pathname from back to front.  Blank fields at the end
+    // indicate a lack of subdirectories; blank fields at the
     // beginning indicate that the higher levels of the hierarchy are
-    // not to be changed.  So these have to be treated differently.
+    // not to be changed (just copied "down").  So these have to be
+    // treated differently.
     *line_cur = '\0';  // replace linefeed with terminal \0
     line_cur_end = line_cur;
     current_depth = max_depth-1;
@@ -237,7 +410,8 @@
     // Create the directory
     if (strlen(fullpath) < PATH_MAX) {
       if (mkdir(fullpath,mask) < 0) {
-	// There was some kind of error, figure out what happened
+	// There was some kind of error, figure out what happened.
+	// Be a little more verbose than the standard library errors.
 	if (errno == EEXIST) {
 	  fprintf(stderr,"Warning: %s already exists, continuing.\n",fullpath);
 	}
@@ -254,9 +428,10 @@
 	  exit(EXIT_FAILURE);
 	}
 	else {
-	  // This includes EACCESS and all other errors
+	  // Fall back on the standard library for the remaining error
+	  // handling
 	  fprintf(stderr,"Error: couldn't make directory %s:\nDo you have write permission for this location?\nDo you need to be root/administrator?\n",fullpath);
-	  error(1,errno,"error");
+	  error(EXIT_FAILURE,errno,"error");
 	}
       }
       else {
@@ -282,7 +457,7 @@
 	strncpy(buf,line_begin,PATH_MAX);  // we don't need buf anymore
 	buf[strlen(buf)] = '/';  // append directory separator
 	len = strlen(buf);
-	strncpy(buf+len,USER_MENU_ENTRIES,PATH_MAX-len-strlen(USER_MENU_ENTRIES));
+	strncpy(buf+len,USER_MENU_ENTRIES_FILENAME,PATH_MAX-len-strlen(USER_MENU_ENTRIES_FILENAME));
 	// Now do the appending
 	fpue = fopen(buf,"a");
 	if (!fpue) {
@@ -302,7 +477,6 @@
       fprintf(stderr,"Error: the directory name:\n  %s\nwas too long, quitting.\n",fullpath);
       exit(EXIT_FAILURE);
     }
-    //printf("Directory: %s\n",fullpath);
   }
   
   // Free memory
@@ -312,177 +486,270 @@
     free(current_dirtree);
 }
 
-int main(int argc, char *argv[])
+
+// Creates blank highscores files at the specified level of the
+// directory hierarchy.  This will be the level at which highscore
+// competition will occur.
+void config_highscores(const char *path,int level)
 {
+  FILE *fp;
+  char buf[PATH_MAX];
+  int n_dirs;
   int i;
-  FILE *fp;
-  DIR *dir;
-
-  int is_creatinghomedirs = 0;
-  int is_confighighscores = 0;
-  int is_clearinggoldstars = 0;
-  int is_clearinghighscores = 0;
-  char *path = NULL;
-  char *file = NULL;
-  int level = 0;
   int success;
 
-  if (argc < 2) {
-    display_help();
+  n_dirs = directory_crawl(path);
+  success = 0;  // This will change to 1 if we find a directory of the
+		// right level
+  for (i = 0; i < n_dirs; i++) {
+    if (directory_level[i] == level) {
+      // Create a blank highscores file in this directory
+      strncpy(buf,directory[i],PATH_MAX);
+      strncat(buf,HIGHSCORE_FILENAME,PATH_MAX-strlen(buf)-1);
+      if (strlen(buf) >= PATH_MAX-1) {
+	fprintf(stderr,"confighighscores: pathname %s truncated, exiting.\n",buf);
+	exit(EXIT_FAILURE);
+      }
+      fp = fopen(buf,"w");
+      if (!fp)
+	error(EXIT_FAILURE,errno,"confighighscores: file:\n  %s",buf);
+      // That creates a blank file, which is all we have to do
+      fclose(fp);
+      success = 1;
+    }
+  }
+  if (!success) {
+    fprintf(stderr,"Error: no directories of level %d found!",level);
     exit(EXIT_FAILURE);
   }
 
-  // Check global config file for a homedir path (must be uncommented)
-  fp = fopen(DATA_PREFIX "/missions/options", "r");
-  if (fp) {
-    extract_variable(fp,"homedir",&path);
-    fclose(fp);
-  }
+  free_directories(n_dirs);
+}
 
-  // Parse the command line options
-  for (i = 1; i < argc; i++) {
-    if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
-      display_help();
-      exit(EXIT_SUCCESS);
+// Delete all highscores files in the directory hierarchy
+void unconfig_highscores(const char *path)
+{
+  clear_file(path,HIGHSCORE_FILENAME,"unconfighighscores");
+}
+
+// Replaces all highscores files with blank files anywhere in the
+// directory hierarchy.  Replacing it with a blank file, rather than
+// just deleting it, insures that the highscores configuration (in
+// terms of the level at which highscore competition occurs) is not
+// altered.
+void clear_highscores(const char *path)
+{
+  FILE *fp;
+  char buf[PATH_MAX];
+  int n_dirs;
+  int i;
+
+  n_dirs = directory_crawl(path);
+  for (i = 0; i < n_dirs; i++) {
+    // Search for a highscores file in this directory
+    strncpy(buf,directory[i],PATH_MAX);
+    strncat(buf,HIGHSCORE_FILENAME,PATH_MAX-strlen(buf)-1);
+    if (strlen(buf) >= PATH_MAX-1) {
+      fprintf(stderr,"clearhighscores: pathname %s truncated, exiting.\n",buf);
+      exit(EXIT_FAILURE);
     }
-    else if (strcmp(argv[i], "--copyright") == 0 ||
-	     strcmp(argv[i], "-c") == 0)
-    {
-      printf(
-	"\ntuxmath-admin version " ADMINVERSION ", Copyright (C) 2007 Tim Holy\n"
-        "This program is free software; you can redistribute it and/or\n"
-        "modify it under the terms of the GNU General Public License\n"
-        "as published by the Free Software Foundation.  See COPYING.txt\n"
-	"\n"
-	"This program is distributed in the hope that it will be useful,\n"
-	"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
-	"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
-	"\n");
-      exit(EXIT_SUCCESS);
+    fp = fopen(buf,"r");
+    if (fp) {
+      // We found such a file, replace it with a blank one
+      fclose(fp);
+      fp = fopen(buf,"w");
+      if (!fp)
+	error(EXIT_FAILURE,errno,"clearhighscores: file:\n  %s",buf);
+      // That creates a blank file, which is all we have to do
+      fclose(fp);
     }
-    else if (strcmp(argv[i], "--usage") == 0 ||
-	     strcmp(argv[i], "-u") == 0) {
-      usage(0, argv[0]);
-      exit(EXIT_SUCCESS);
-    }
-    else if (strcmp(argv[i], "--path") == 0) {
-      if (i+1 > argc) {
-	fprintf(stderr, "%s option requires an argument (a directory name)\n", argv[i]);
-	usage(1, argv[0]);
-      }
-      else {
-	path = argv[i+1];
-	dir = opendir(path);  // determine whether directory exists
-	if (dir == NULL) {
-	  fprintf(stderr,"Error: path:\n  %s\nis not an existing directory, or could not be read.\n",path);
-	  exit(EXIT_FAILURE);
-	}
-	closedir(dir);
-	i++; // increment so further processing skips over the argument
-      }
-    }
-    else if (strcmp(argv[i], "--level") == 0) {
-      if (i+1 > argc) {
-	fprintf(stderr, "%s option requires an argument (a level number)\n", argv[i]);
-	usage(1, argv[0]);
-      }
-      else {
-	success = sscanf(argv[i+1],"%d",&level);
-	if (!success) {
-	  fprintf(stderr,"level: %s is not a number\n",argv[i+1]);
-	  exit(EXIT_FAILURE);
-	}
-      }
-    }
-    else if (strcmp(argv[i], "--createhomedirs") == 0) {
-      is_creatinghomedirs = 1;
-      if (i+1 > argc) {
-	fprintf(stderr, "%s option requires an argument (a file name)\n", argv[i]);
-	usage(1, argv[0]);
-      }
-      else {
-	file = argv[i+1];
-	fp = fopen(file,"r");   // determine whether the file exists
-	if (fp == NULL) {
-	  fprintf(stderr,"createhomedirs: %s is not an existing filename, or could not be read\n",file);
-	  exit(EXIT_FAILURE);
-	}
-	fclose(fp);  // don't read it yet
-	i++; // increment so further processing skips over the argument
-      }
-    }
-    else if (strcmp(argv[i], "--confighighscores") == 0) {
-      is_confighighscores = 1;
-    }
-    else if (strcmp(argv[i], "--clearinghighscores") == 0) {
-      is_clearinghighscores = 1;
-    }
-    else if (strcmp(argv[i], "--clearinggoldstars") == 0) {
-      is_clearinggoldstars = 1;
-    }
-    else {
-      fprintf(stderr,"Error: option %s not recognized.\n",argv[i]);
-      exit(EXIT_FAILURE);
-    }
   }
 
-  // All operations require a valid path, so check that now
-  if (path == NULL) {
-    fprintf(stderr,"Must have a valid path (either with --path or in the global configuration)\n");
-    usage(1, argv[0]);
-    exit(EXIT_FAILURE);
-  }
+  free_directories(n_dirs);
+}
 
-  // Create homedirs
-  if (is_creatinghomedirs) {
-    if (file == NULL) {
-      fprintf(stderr,"Must specify a filename when creating homedirs\n");
-      usage(1, argv[0]);
+// Delete all goldstars files in the directory hierarchy
+void clear_goldstars(const char *path)
+{
+  clear_file(path,GOLDSTAR_FILENAME,"cleargoldstars");
+}
+
+// Deletes a named filetype in the directory hierarchy
+void clear_file(const char *path,const char *filename,const char *invoke_name)
+{
+  FILE *fp;
+  char buf[PATH_MAX];
+  int n_dirs;
+  int i;
+
+  n_dirs = directory_crawl(path);
+  for (i = 0; i < n_dirs; i++) {
+    // Search for a goldstars file in this directory
+    strncpy(buf,directory[i],PATH_MAX);
+    strncat(buf,filename,PATH_MAX-strlen(buf)-1);
+    if (strlen(buf) >= PATH_MAX-1) {
+      fprintf(stderr,"%s: pathname %s truncated, exiting.\n",invoke_name,buf);
       exit(EXIT_FAILURE);
     }
-    create_homedirs(path,file);
+    fp = fopen(buf,"r");
+    if (fp != NULL) {
+      // We found such a file, delete it
+      fclose(fp);
+      if (remove(buf) < 0)
+	error(EXIT_FAILURE,errno,"%s: file:\n  %s",invoke_name,buf);
+    }
   }
 
-  // Configure high scores
-  if (is_confighighscores) {
-    if (level == 0) {
-      fprintf(stderr,"Must specify a level when configuring highscores\n");
-      usage(1, argv[0]);
-      exit(EXIT_FAILURE);
+  free_directories(n_dirs);
+}
+
+
+// Extracts a single variable from a configuration file and puts the
+// string in the variable "value". Returns 1 on success and 0 on
+// failure.
+int extract_variable(FILE *fp, const char *varname, char** value)
+{
+  char buf[PATH_MAX];
+  char *param_begin;
+  char *tmpvalue;
+
+  rewind(fp);  // start at the beginning of the file
+
+  // Read in a line at a time:
+  while (fgets (buf, PATH_MAX, fp)) {
+    param_begin = buf;
+    // Skip leading whitespace
+    while (isspace(*param_begin))
+      param_begin++;
+    // Skip comments
+    if ((*param_begin == ';') || (*param_begin == '#'))
+      continue;
+    // Test whether it matches the variable name
+    if (strncmp(param_begin,varname,strlen(varname)) == 0) {
+      // Find the "=" sign
+      tmpvalue = strchr(param_begin+strlen(varname), '=');
+      if (tmpvalue == NULL)
+	continue;
+      // Skip whitespace
+      while (isspace(*tmpvalue))
+	tmpvalue++;
+      // Copy the result
+      *value = strdup(tmpvalue);
+      return 1;
     }
-    //config_highscores(path,level);
   }
+  return 0;
+}
 
-  // Clear high scores
-  if (is_clearinghighscores) {
-    //clear_highscores(path);
+// Recursively generates a list of all subdirectories listed in
+// user_menu_entries starting from the given path.  It populates the
+// global variables "directory" and "directory_level", and returns the
+// total number found.  Note this function allocated memory with
+// malloc, so after you're done using these directories you should
+// call free_directories.
+//
+// This function checks to make sure that each directory exists, and
+// exits if not, so you can be sure that all listed directories exist.
+//
+// Note this puts the top level directory (assigned in path) as the
+// first entry (which "main" verifies to be valid), so this function
+// is guaranteed to return at least one directory.
+int directory_crawl(const char *path)
+{
+  int current_length;
+  int previous_length;
+  int current_level;
+  FILE *fp;
+  char buf[PATH_MAX];
+  char fullpath[PATH_MAX];
+  int isdone;
+  int i;
+  char *line_begin;
+  char *line_end;
+  DIR *dir;
+  
+  current_length = 1;
+  directory[0] = (char*) malloc((strlen(path)+2)*sizeof(char));
+  if (directory[0] == NULL) {
+    fprintf(stderr,"Memory allocation error in directory_crawl.\n");
+    exit(EXIT_FAILURE);
   }
+  strcpy(directory[0],path);
+  // Append '/' if necessary
+  if (directory[0][strlen(path)-1] != '/')
+    strcat(directory[0],"/");
+  current_level = 1;
+  directory_level[0] = current_level;
 
-  // Clear gold stars
-  if (is_clearinggoldstars) {
-    //clear_goldstars(path);
-  }
-   
-  return EXIT_SUCCESS;
+  isdone = 0;
+  while (!isdone) {
+    previous_length = current_length;
+    isdone = 1;  // We'll be finished if we don't find any new user_menu_entries files
+    for (i = 0; i < previous_length; i++) {
+      // Just parse directories of the most recently-added level
+      // (we've already done the work for previous levels)
+      if (directory_level[i] == current_level) {
+	// Read the user_menu_entries file, if it exists
+	// Note that previous items already have "/" appended, no need
+	// to worry about that here.
+	strncpy(fullpath,directory[i],PATH_MAX);
+	strncat(fullpath,USER_MENU_ENTRIES_FILENAME,PATH_MAX-strlen(fullpath)-1);
+	fp = fopen(fullpath,"r");
+	if (fp != NULL) {
+	  // We found the user_menu_entries file, read it and add directories
+	  while (fgets (buf, PATH_MAX, fp)) {
+	    if (current_length >= MAX_USERS) {
+	      fprintf(stderr,"Error: maximum number of users exceeded.");
+	      exit(EXIT_FAILURE);
+	    }
+	    // Skip over white space, and especially blank lines
+	    line_begin = buf;
+	    while (isspace(*line_begin))
+	      line_begin++;
+	    // Eliminate the \n at the end of the line
+	    line_end = line_begin+strlen(line_begin)-1;
+	    while (line_end >= line_begin && (*line_end == '\n' || *line_end == '\r')) {
+	      *line_end = '\0';
+	      line_end--;
+	    }
+	      
+	    if (strlen(line_begin) == 0)
+	      continue;
+	    directory[current_length] = (char *) malloc((strlen(directory[i])+strlen(line_begin)+2)*sizeof(char));
+	    if (directory[current_length] == NULL) {
+	      fprintf(stderr,"Memory allocation error in directory_crawl.\n");
+	      exit(EXIT_FAILURE);
+	    }
+	    // Append each new directory to the list
+	    strcpy(directory[current_length],directory[i]);
+	    strcat(directory[current_length],line_begin);
+	    strcat(directory[current_length],"/");
+	    directory_level[current_length] = current_level+1;
+	    // Check to make sure it's valid
+	    dir = opendir(directory[current_length]);
+	    if (dir == NULL)
+	      error(EXIT_FAILURE,errno,"directory:\n %s",directory[current_length]);
+	    closedir(dir);
+	    current_length++;
+	  }
+	  isdone = 0;  // We know we need to check the subdirectories
+	  fclose(fp);
+	}  // end of: if (fp != NULL)
+      } // end of: if (directory_level[i] == current_level)
+    } // end of: loop over previous directories
+    current_level++;  // We're all done parsing this level, move on
+  } // end of: while (!isdone)
+  
+  return current_length;
 }
 
-
-void usage(int err, char * cmd)
+void free_directories(int n)
 {
-  FILE * f;
+  int i;
 
-  if (err == 0)
-    f = stdout;
-  else
-    f = stderr;
-
-  fprintf(f,
-   "\nUsage: %s {--help | --usage | --copyright}\n"
-   "       %s [--path <directory>] --createhomedirs <file>\n"
-   "       %s [--level <levelnum>] --confighighscores\n"
-   "       %s [--path <directory>] [--clearhighscores] [--cleargoldstars]\n"
-    "\n", cmd, cmd, cmd, cmd);
-
-  exit (err);
+  for (i = 0; i < n; i++) {
+    free(directory[i]);
+    directory[i] = NULL;
+  }
 }
 




More information about the Tux4kids-commits mailing list