homeanddirs.c

guylhem@oeil.qc.ca
27 Jul 1998 08:41:34 -0000


/*
 * Handle directories & files
 *
 * Copyright (c) 1998 Michael Vitecek <M.Vitecek@sh.cvut.cz>
 * Copyright (c) 1998 Chris Ridd <c.ridd@isode.com>
 * Copyright (c) 1997 Guylhem AZNAR <guylhem@oeil.qc.ca>
 *
 */

#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>

#include "../include/configure.h"
#include "../include/afterstep.h"
#include "../include/aftersteplib.h"

my_sort_f my_sort_list[] =
{my_dirsort,
 my_alphasort,
 NULL};

int HomeCreate(const char *filename)
{
    int c;
    char *target, *source;
    FILE *targetfile, *sourcefile;

    target = (char *) safemalloc(strlen(AFTER_DIR) + strlen(filename) + 2);
    sprintf(target, "%s/%s", AFTER_DIR, filename);

    source = (char *) safemalloc(strlen(AFTER_SHAREDIR) + strlen(filename) + 2);
    sprintf(source, "%s/%s", AFTER_SHAREDIR, filename);

    if ((targetfile = fopen(PutHome(target), "w")) == NULL) {
	fprintf(stderr, "can't open %s !", target);
	return (-1);
    }
    if ((sourcefile = fopen(PutHome(source), "r")) == NULL) {
	fprintf(stderr, "can't open %s !", source);
	return (-2);
    }
/* free memory allocated for the filenames */
    free(target);
    free(source);

/* copy the source file to the target file */
    while ((c = getc(sourcefile)) != EOF)
	putc(c, targetfile);


    fclose(targetfile);
    fclose(sourcefile);

    return 0;
}

int CheckFile(const char *file)
{
    struct stat st;

    if ((stat(file, &st) == -1) || (st.st_mode & S_IFMT) != S_IFREG)
	return (-1);
    else
	return (0);
}

int CheckDir(const char *directory)
{
    struct stat st;

    if ((stat(directory, &st) == -1) || (st.st_mode & S_IFMT) != S_IFDIR)
	return (-1);
    else
	return (0);
}

char *CheckOrShare(const char *directory, const char *homedir, const char *sharedir, const int share)
{
    char *result_dir;

    result_dir = (char *) safemalloc(PATH_MAX + 1);
    sprintf(result_dir, "%s/%s", homedir, directory);

    if (CheckDir(result_dir) != 0) {
/* the home directory doesn't exist or there's a problem with accessing it */
	if (!share) {
	    /* try it with the system directory */
	    sprintf(result_dir, "%s/%s", sharedir, directory);
	    if (CheckDir(result_dir) != 0) {
		/* problems with accessing the system directory */
		free(result_dir);
		return (NULL);
	    } else
		return (result_dir);
	} else {
	    /* ahh - it already was the system directory! */
	    free(result_dir);
	    return (NULL);

	}
    }
    return (result_dir);
}

char *PutHome(const char *path_with_home)
{
    char *home;			/* the HOME environment variable */
    char *realpath;

    /* home dir ? */
    if (!strncmp(path_with_home, "~/", 2)) {
	/* get home */
	if ((home = getenv("HOME")) == NULL)
	    home = "./";
	/* alloc it */
	realpath = (char *) safemalloc(strlen(home) + strlen(path_with_home) + 1);
	strcpy(realpath, home);
	strcat(realpath, "/");
	strcat(realpath, path_with_home + 2);
    } else {
	realpath = (char *) safemalloc(strlen(path_with_home) + 1);
	strcpy(realpath, path_with_home);
    }

    return (realpath);
}

int CheckOrCreate(const char *what)
{
    char *checkdir;
    mode_t perms = 0755;

    checkdir = PutHome(what);

    if (CheckDir(checkdir) != 0) {
	fprintf(stderr, "Creating %s ... ", what);
	if (mkdir(checkdir, perms)) {
	    free(checkdir);
	    fprintf(stderr, "ERROR !\n AfterStep depends on %s directory !\nPlease check permissions or contact your sysadmin !\n", what);
	    return (-1);
	} else
	    fprintf(stderr, "done\n");
    }
    free(checkdir);
    return (0);
}

int CheckOrCreateFile(const char *what)
{
    char *checkfile;
    FILE *touch;

    checkfile = PutHome(what);
    if (CheckFile(checkfile) != 0) {
	fprintf(stderr, "Creating %s ... ", what);
	if ((touch = fopen(checkfile, "w")) == NULL) {
	    free(checkfile);
	    fprintf(stderr, "ERROR !\n Cannot open file %s for writing!\n"
		    " Please check permissions or contact your sysadmin !\n", what);
	    return (-1);
	} else {
	    fclose(touch);
	    fprintf(stderr, "done\n");
	}

    }
   free(checkfile);
   return (0);
}

/*
 * Non-NULL select and dcomp pointers are *NOT* tested, but should be OK.
 * They are not used by afterstep however, so this implementation should
 * be good enough.
 *
 * c.ridd@isode.com
 */
int my_scandir(char *dirname, struct direntry *(*namelist[]),
	       int (*select) (struct dirent *),
	       int (*dcomp) (struct direntry **, struct direntry **))
{
    DIR *d;
    struct dirent *e;		/* Pointer to static struct inside readdir() */
    struct direntry **nl;	/* Array of pointers to dirents */
    struct direntry **nnl;
    int n;			/* Count of nl used so far */
    int sizenl;			/* Number of entries in nl array */
    int j;
    size_t realsize;
    char *filename;		/* For building filename to pass to stat */
    char *p;			/* Place where filename starts */
    struct stat buf;


    d = opendir(dirname);

    if (d == NULL)
	return -1;

    filename = (char *) safemalloc(strlen(dirname) + PATH_MAX + 2);
    if (filename == NULL) {
	closedir(d);
	return -1;
    }
    strcpy(filename, dirname);
    p = filename + strlen(filename);
    *p++ = '/';
    *p = 0;			/* Just in case... */

    nl = NULL;
    n = 0;
    sizenl = 0;

    while ((e = readdir(d)) != NULL) {
	if ((select == NULL) || select(e)) {
	    /* add */
	    if (sizenl == n) {
		/* Grow array */
		sizenl += 32;	/* arbitrary delta */
		nnl = realloc(nl, sizenl * sizeof(struct direntry *));
		if (nnl == NULL) {
		    /* Free the old array */
		    for (j = 0; j < n; j++)
			free(nl[j]);
		    free(nl);
		    free(filename);
		    closedir(d);
		    return -1;
		}
		nl = nnl;
	    }
	    realsize = offsetof(struct direntry, d_name) +strlen(e->d_name) + 1;
	    nl[n] = (struct direntry *) safemalloc(realsize);
	    if (nl[n] == NULL) {
		for (j = 0; j < n; j++)
		    free(nl[j]);
		free(nl);
		free(filename);
		closedir(d);
		return -1;
	    }
	    /* Fill in the fields using stat() */
	    strcpy(p, e->d_name);
	    if (stat(filename, &buf) == -1) {
		for (j = 0; j <= n; j++)
		    free(nl[j]);
		free(nl);
		free(filename);
		closedir(d);
		return -1;
	    }
	    nl[n]->d_mode = buf.st_mode;
	    nl[n]->d_mtime = buf.st_mtime;
	    strcpy(nl[n]->d_name, e->d_name);
	    n++;
	}
    }
    free(filename);

    if (closedir(d) == -1) {
	free(nl);
	return -1;
    }
    *namelist = realloc(nl, n * sizeof(struct direntry *));

    if (n == 0)
/* OK, but not point sorting or freeing anything */
	return 0;

    if (*namelist == NULL) {
	for (j = 0; j < n; j++)
	    free(nl[j]);
	free(nl);
	return -1;
    }
    /* Optionally sort the list */
    if (dcomp)
	qsort(*namelist, n, sizeof(struct direntry *), (int (*)()) dcomp);

    /* Return the count of the entries */
    return n;
}

/* sort entries based on the array my_sort_list */
int my_sort(struct direntry **d1, struct direntry **d2)
{
    int i, diff = 0;
    for (i = 0; i < 5; i++) {
	if (my_sort_list[i] != NULL) {
	    diff = my_sort_list[i] (d1, d2);
	    if (diff != 0)
		break;
	}
    }
    return diff;
}

/* sort entries based on their type.  directories come first */

int my_dirsort(struct direntry **d1, struct direntry **d2)
{
    return (*d1)->d_mode - (*d2)->d_mode;
}

/* Sort entries based on their names. A comes before Z. */

#define BUF_SIZE 10
 int my_alphasort(struct direntry **d1, struct direntry **d2)
 {
    char       *under1, *under2;
    int                num1, num2, len, final_len;
    char       buf[BUF_SIZE + 1];

    /* if the item name(s) that are compared begin with digit, try to get the
       numbers */
    if(isdigit(*((*d1)->d_name)) || (isdigit(*((*d2)->d_name))))
      {
       /* find out if the names are in the format <number>_<Item name> */
       under1 = (*d1)->d_name;
       while((*under1) && (isdigit(*under1)) && (*under1 != '_')) under1++;
       under2 = (*d2)->d_name;
       while((*under2) && (isdigit(*under2)) && (*under2 != '_')) under2++;

       /* <number>_<Item name> takes precedence before <Item name> */
       fprintf(stderr, "%s ?= %s : ", (*d1)->d_name, (*d2)->d_name);
       if((*under1 == '_') && (!*under2)) fprintf(stderr, "-1\n");
       else if((!*under2) && (*under2)) fprintf(stderr, "1\n");
       if((*under1 == '_') && (!*under2)) return(-1);
       else if((!*under2) && (*under2)) return(1);


       /* both names are in format <number>_<Item name> */
       else if((*under1 == '_') && (*under2 == '_'))
         {
           /* copy the number to buf */
           strncpy(buf, (*d1)->d_name, (final_len = ((len = under1 -
(*d1)->d_name) < BUF_SIZE ? len : BUF_SIZE)));
           buf[final_len] = '\0';
           num1 = atoi(buf);
           /* copy the number to buf */
           strncpy(buf, (*d2)->d_name, (final_len = ((len = under2 -
(*d2)->d_name) < BUF_SIZE ? len : BUF_SIZE)));
           buf[final_len] = '\0';
           num2 = atoi(buf);
           if(num1 < num2) return(-1);
           else if(num1 > num2) return(1);
           return(0);
         }
       /* it was only a plain attack, the names are not in the format we want */
      }
    return(strcmp((*d1)->d_name, (*d2)->d_name));
 }
#undef BUF_SIZE
 

/* Sort entries based on their mtimes. Old entries come before new entries,
 * entries with the same times get sorted alphabetically.
 */
int my_datesort(struct direntry **d1, struct direntry **d2)
{
    int diff = (*d1)->d_mtime - (*d2)->d_mtime;
    if (diff == 0)
	diff = my_alphasort(d1, d2);
    return diff;
}


/*
 * Use this function as the select argument to my_scandir to make it ignore
 * all files and directories starting with "."
 */
int ignore_dots(struct dirent *e)
{
    return (e->d_name[0] != '.');
}