Gallery can now list photos ... names sorted using quicksort.

This commit is contained in:
Stuart Longland 2008-01-09 16:03:21 +10:00
parent 80feccdd87
commit d02b825fba
10 changed files with 492 additions and 42 deletions

View File

@ -1,5 +1,5 @@
.PHONY: clean
OBJS=obj/main.o obj/util.o obj/galleries.o obj/varray.o
OBJS=obj/main.o obj/util.o obj/galleries.o obj/gallery.o obj/varray.o
gallery.cgi: $(OBJS)
$(CC) -o $@ $(OBJS)

View File

@ -17,6 +17,7 @@ struct gallery_info {
char* gallery_name; /* Directory name for gallery */
char* gallery_title; /* Title of gallery */
char* gallery_desc; /* Description of gallery */
char* gallery_dir; /* Filesystem path of gallery */
};
/* Two helper functions create, (deep) copy and destroy these structures */
@ -28,6 +29,7 @@ struct gallery_info {
* struct is malloc()'d for use and returned.
*/
struct gallery_info* create_gallery_info(
const char* dir,
const char* name,
const char* title,
const char* desc,

43
include/gallery.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef _GALLERY_H
#define _GALLERY_H
#include <varray.h>
#include <galleries.h>
/* The following file describes structs that pertain to a specific gallery. */
/* Gallery Contents structure. The following file wraps up a gallery_info
* file along with a listing of all photos in the gallery, stored as a string
* array.
*
* Note that in this structure, the info pointer is neigher copied nor freed.
*/
struct gallery_contents {
struct gallery_info* info;
struct static_string_array* photos;
};
/* Get the contents of a gallery. This takes an existing gallery_info struct,
* opens the directory, then grabs the names of all supported photo types.
*
* A pre-allocated struct may be supplied for storage, otherwise a new one will
* be created (pass NULL as the argument).
*
* Returns a brand new gallery_contents struct, or NULL if creation failed.
*/
struct gallery_contents* read_gallery_contents( struct gallery_info* gallery,
struct gallery_contents* dest );
/* Destroy a gallery_contents struct. The fields_only member tells this
* function that the gallery_contents struct, just its contents.
*/
void destroy_gallery_contents( struct gallery_contents* gc, int fields_only );
/* Duplicate a gallery_contents struct. A pre-allocated struct may be supplied.
*
* This does a deep copy of the structure, returning it, or NULL if it failed.
*/
struct gallery_contents* copy_gallery_contents( struct gallery_contents* gc,
struct gallery_contents* dest );
#endif

View File

@ -109,4 +109,26 @@ struct txtinfo_chain* read_txtinfo_chain( const char* file );
/* Write out a txtinfo_chain object. Returns 0 on success, errno on failure. */
int write_txtinfo_chain( const char* file, struct txtinfo_chain* chain );
/* Duplicate a structure in memory.
* This calls malloc to allocate a region of the specified size,
* then does a memcpy from the supplied address.
*
* Returns the copy on success, or NULL on failure.
*/
void* memdup( void* ptr, size_t length );
/* Sort a generic array
*
* This does an in-place sort of the objects contained in the array.
* Since there's no way of knowing how to sort the objects, a function
* for doing this must be supplied as a pointer.
*
* str_sort_array does the above sort with strings, setting case_insensitive
* will cause the sorting algorithm to ignore the case.
*/
void sort_str_array( char** array, size_t length, int case_insensitive );
void sort_array( char** array, size_t length,
int (*compare)( const void*, const void* ) );
#endif

View File

@ -75,6 +75,14 @@ struct vararray* create_vararray( struct vararray* dest, size_t obj_size,
*/
void destroy_vararray( struct vararray* dest );
/* Copy a vararray
*
* This does a deep copy of the entire contents in the array.
*
* Returns the copy on success, or NULL on failure
*/
struct vararray* copy_vararray( struct vararray* src );
/* Fetch a node value. There's one function for each type.
*
* They are provided to allow easy fetching of simple types without needing to
@ -380,8 +388,9 @@ CREATE_STATIC_ARRAY_STRUCT( static_string_array, char*, string );
/* The following are for all the simple types in use. Note that the
* size parameter is omitted. They all match the generic type array
* (static_simple_array), and may be casted as such when freeing.
* (static_simple_array), which is used when duplicating the arrays.
*/
struct static_simple_array { size_t length; void* value; };
struct static_char_array { size_t length; char* value; };
struct static_uchar_array { size_t length; unsigned char* value; };
struct static_short_array { size_t length; short* value; };
@ -400,38 +409,38 @@ struct static_double_array { size_t length; double* value; };
*
* They return the vararray created, or NULL on failure.
*/
struct vararray* import_obj_array( struct static_obj_array* sa );
struct vararray* import_string_array( struct static_string_array* sa );
struct vararray* import_char_array( struct static_char_array* sa );
struct vararray* import_uchar_array( struct static_uchar_array* sa );
struct vararray* import_short_array( struct static_short_array* sa );
struct vararray* import_ushort_array( struct static_ushort_array* sa );
struct vararray* import_int_array( struct static_int_array* sa );
struct vararray* import_uint_array( struct static_uint_array* sa );
struct vararray* import_long_array( struct static_long_array* sa );
struct vararray* import_ulong_array( struct static_ulong_array* sa );
struct vararray* import_llong_array( struct static_llong_array* sa );
struct vararray* import_ullong_array( struct static_ullong_array* sa );
struct vararray* import_float_array( struct static_float_array* sa );
struct vararray* import_double_array( struct static_double_array* sa );
struct vararray* import_obj_array( struct static_obj_array* sa );
struct vararray* import_string_array( struct static_string_array* sa );
struct vararray* import_char_array( struct static_char_array* sa );
struct vararray* import_uchar_array( struct static_uchar_array* sa );
struct vararray* import_short_array( struct static_short_array* sa );
struct vararray* import_ushort_array( struct static_ushort_array* sa );
struct vararray* import_int_array( struct static_int_array* sa );
struct vararray* import_uint_array( struct static_uint_array* sa );
struct vararray* import_long_array( struct static_long_array* sa );
struct vararray* import_ulong_array( struct static_ulong_array* sa );
struct vararray* import_llong_array( struct static_llong_array* sa );
struct vararray* import_ullong_array( struct static_ullong_array* sa );
struct vararray* import_float_array( struct static_float_array* sa );
struct vararray* import_double_array( struct static_double_array* sa );
/* Export functions. These take a variable array and output a plain static
* C array of the specified type, or NULL if this can't be achieved.
*/
struct static_obj_array* export_obj_array( struct vararray* va );
struct static_string_array* export_string_array( struct vararray* va );
struct static_char_array* export_char_array( struct vararray* va );
struct static_uchar_array* export_uchar_array( struct vararray* va );
struct static_short_array* export_short_array( struct vararray* va );
struct static_ushort_array* export_ushort_array( struct vararray* va );
struct static_int_array* export_int_array( struct vararray* va );
struct static_uint_array* export_uint_array( struct vararray* va );
struct static_long_array* export_long_array( struct vararray* va );
struct static_ulong_array* export_ulong_array( struct vararray* va );
struct static_llong_array* export_llong_array( struct vararray* va );
struct static_ullong_array* export_ullong_array( struct vararray* va );
struct static_float_array* export_float_array( struct vararray* va );
struct static_double_array* export_double_array( struct vararray* va );
struct static_obj_array* export_obj_array( struct vararray* va );
struct static_string_array* export_string_array( struct vararray* va);
struct static_char_array* export_char_array( struct vararray* va );
struct static_uchar_array* export_uchar_array( struct vararray* va );
struct static_short_array* export_short_array( struct vararray* va );
struct static_ushort_array* export_ushort_array( struct vararray* va);
struct static_int_array* export_int_array( struct vararray* va );
struct static_uint_array* export_uint_array( struct vararray* va );
struct static_long_array* export_long_array( struct vararray* va );
struct static_ulong_array* export_ulong_array( struct vararray* va );
struct static_llong_array* export_llong_array( struct vararray* va );
struct static_ullong_array* export_ullong_array( struct vararray* va);
struct static_float_array* export_float_array( struct vararray* va );
struct static_double_array* export_double_array( struct vararray* va);
/* Free a static array. The following can be used to free any
* statically-allocated array. Macros are provided to free the above static
@ -451,7 +460,7 @@ void destroy_array( void* container, size_t length,
* object, in the case of static_obj_array)
*/
#define destroy_obj_array( array, ptr_member ) \
destroy_array( array, array->length, array->member, 1 )
destroy_array( array, array->length, array->ptr_member, 1 )
/* Free an array of strings. */
#define destroy_string_array( array ) \
@ -461,4 +470,25 @@ void destroy_array( void* container, size_t length,
#define destroy_simple_array( array ) \
destroy_array( array, array->length, array->value, 0 )
/* Duplicate a static array. The following will make a deep copy of any
* statically-allocated array in one of the above structs.
*
* For convenience, a macro is provided that casts your array to the
* right type for use. You should use this, rather than calling the
* function directly. The macro assumes at least one element is present
* in the array.
*
* Returns the copy on success, NULL on failure.
*/
struct static_simple_array* duplicate_simple_array_impl(
struct static_simple_array* src, size_t data_size );
#define duplicate_static_array( array, type ) \
(type*)duplicate_simple_array_impl( \
(struct static_simple_array*)array, \
sizeof( array->value[0] ) )
struct static_obj_array* duplicate_obj_array_impl(
struct static_obj_array* src );
#define duplicate_static_obj_array( array, type ) \
(type*)duplicate_obj_array_impl( (struct static_obj_array*)array )
#endif

View File

@ -22,6 +22,7 @@
* struct is malloc()'d for use and returned.
*/
struct gallery_info* create_gallery_info(
const char* dir,
const char* name,
const char* title,
const char* desc,
@ -38,16 +39,19 @@ struct gallery_info* create_gallery_info(
/* If we're still here, then allocation succeeded:
* copy in the strings
*/
dest->gallery_dir = strdup(dir);
dest->gallery_name = strdup(name);
dest->gallery_title= strdup(title);
dest->gallery_desc = strdup(desc);
/* Check none of them are null */
if ( (dest->gallery_name == NULL) ||
if ( (dest->gallery_dir == NULL) ||
(dest->gallery_name == NULL) ||
(dest->gallery_title == NULL) ||
(dest->gallery_desc == NULL) ) {
dprintf("create_gallery_info: name at %p, title at %p, desc at %p -> bailout!\n",
dest->gallery_name, dest->gallery_title, dest->gallery_desc);
if ( dest->gallery_dir != NULL ) free( dest->gallery_dir );
if ( dest->gallery_name != NULL ) free( dest->gallery_name );
if ( dest->gallery_title != NULL ) free( dest->gallery_title );
if ( dest->gallery_desc != NULL ) free( dest->gallery_desc );
@ -62,7 +66,8 @@ struct gallery_info* create_gallery_info(
struct gallery_info* copy_gallery_info( struct gallery_info* info,
struct gallery_info* dest ) {
dprintf("copy_gallery_info: copying %p to %p\n", info, dest );
return create_gallery_info( info->gallery_name,
return create_gallery_info( info->gallery_dir,
info->gallery_name,
info->gallery_title,
info->gallery_desc,
dest );
@ -77,6 +82,7 @@ void destroy_gallery_info( struct gallery_info* info, int fields_only ) {
free( info->gallery_name );
free( info->gallery_title );
free( info->gallery_desc );
free( info->gallery_dir );
if ( !fields_only ) free( info );
}
@ -124,7 +130,7 @@ struct gallery_info* read_gallery( const char* dir, struct gallery_info* dest )
dprintf( "read_gallery: name=%s\n", name );
/* Create the object */
dest = create_gallery_info( name, title, desc, dest );
dest = create_gallery_info( dir, name, title, desc, dest );
/* Free our copy of the data and return */
free( name );

125
src/gallery.c Normal file
View File

@ -0,0 +1,125 @@
#include <gallery.h>
#include <dprintf.h>
#include <stdlib.h>
#include <stdio.h>
#include <util.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
/* Get the contents of a gallery. This takes an existing gallery_info struct,
* opens the directory, then grabs the names of all supported photo types.
*
* A pre-allocated struct may be supplied for storage, otherwise a new one will
* be created (pass NULL as the argument).
*
* Returns a brand new gallery_contents struct, or NULL if creation failed.
*/
struct gallery_contents* read_gallery_contents( struct gallery_info* gallery,
struct gallery_contents* dest ){
int free_struct = 1;
if ( gallery == NULL ) return NULL;
if ( dest == NULL )
dest = (struct gallery_contents*)
calloc( 1, sizeof( struct gallery_contents ) );
else free_struct = 0;
if ( dest == NULL ) return NULL;
dprintf("read_gallery_contents: reading gallery info from gallery %s storing in %p\n", gallery->gallery_name, dest);
dest->info = gallery;
struct vararray* list = create_vararray( NULL, 0, NULL, NULL );
if ( list == NULL ) {
dputs("read_gallery_contents: failed to allocate vararray\n");
if ( free_struct ) free( dest );
return NULL;
}
/* Open the directory up */
DIR* dir = opendir( gallery->gallery_dir );
if ( dir == NULL ) {
dprintf("read_gallery_contents: failed to opendir(\"%s\")\n", gallery->gallery_dir);
if ( free_struct ) free( dest );
return NULL;
}
struct dirent* de = readdir( dir );
while ( de != NULL ) {
/* Look for files that end in:
* .jpg, .jpeg, .gif, .png, .tif, .tiff
*
* Ultimately, I should just probe using magic bytes, but
* this'll do for now.
*/
char* file_ext = rindex( de->d_name, '.' );
if ( ( file_ext != NULL ) &&
( (strcasecmp( file_ext, ".jpg" ) == 0) ||
(strcasecmp( file_ext, ".jpeg" ) == 0) ||
(strcasecmp( file_ext, ".gif" ) == 0) ||
(strcasecmp( file_ext, ".png" ) == 0) ||
(strcasecmp( file_ext, ".tif" ) == 0) ||
(strcasecmp( file_ext, ".tiff" ) == 0) ) )
push_vararray_string( list, de->d_name );
de = readdir( dir );
}
closedir( dir );
/* We now should have a list of files in list...
* convert this to a static array, then dispense with the original
* variable array.
*/
dest->photos = export_string_array( list );
destroy_vararray( list );
/* Check for success... if not, free everything and return NULL */
if ( dest->photos == NULL ) {
dputs("read_gallery_contents: failed to list files\n");
if ( free_struct ) free( dest );
return NULL;
}
/* Do a sort of the filenames */
if ( dest->photos->length > 1 )
sort_str_array( dest->photos->string, dest->photos->length, 1 );
return dest;
}
/* Destroy a gallery_contents struct. The fields_only member tells this
* function that the gallery_contents struct, just its contents.
*/
void destroy_gallery_contents( struct gallery_contents* gc, int fields_only ) {
destroy_string_array( gc->photos );
free( gc );
}
/* Duplicate a gallery_contents struct. A pre-allocated struct may be supplied.
*
* This does a deep copy of the structure, returning it, or NULL if it failed.
*/
struct gallery_contents* copy_gallery_contents( struct gallery_contents* gc,
struct gallery_contents* dest ) {
int free_struct = 1;
if ( dest == NULL ) {
dest = (struct gallery_contents*)
memdup( (void*)gc,
sizeof( struct gallery_contents ) );
if ( dest == NULL ) return NULL;
} else {
free_struct = 0;
memcpy( (void*)dest, (void*)gc,
sizeof( struct gallery_contents ) );
}
/* That's duplicated the main container... now the photo list */
dest->photos = duplicate_static_obj_array( gc->photos,
struct static_string_array );
if ( dest->photos == NULL ) {
if ( free_struct ) free( dest );
return NULL;
}
return dest;
}

View File

@ -5,6 +5,7 @@
#include <stdlib.h>
#include <dprintf.h>
#include <errno.h>
#include <gallery.h>
/* Entry point into entire webapp */
int main( int argc, char** argv ) {
@ -108,7 +109,17 @@ void gallery_handler( struct gallery_info* gallery,
struct vararray* path_info ) {
printf("Content-Type: text/plain\n\nTODO: gallery handler\n");
printf("Name: %s\nTitle: %s\nDescription: %s\n",
gallery->gallery_name, gallery->gallery_title, gallery->gallery_desc );
gallery->gallery_name,
gallery->gallery_title,
gallery->gallery_desc );
/* Read the photo list */
struct gallery_contents* contents =
read_gallery_contents( gallery, NULL );
size_t i;
for( i = 0; i < contents->photos->length; i++ )
puts( contents->photos->string[i] );
}
/* Action pages handler */

View File

@ -375,3 +375,93 @@ int write_txtinfo_chain( const char* file, struct txtinfo_chain* chain ) {
fclose( fp );
return 0;
}
/* Duplicate a structure in memory.
* This calls malloc to allocate a region of the specified size,
* then does a memcpy from the supplied address.
*
* Returns the copy on success, or NULL on failure.
*/
void* memdup( void* ptr, size_t length ) {
void* out = malloc( length );
if ( out != NULL ) memcpy( out, ptr, length );
return out;
}
/* Sort an array of strings
*
* This does an in-place sort of the strings contained in the array.
* Setting the case_insensitive argument non-zero causes the case to be ignored
* when sorting.
*
* The implementation is based on pseudocode from
* http://en.wikipedia.org/wiki/Quick_sort#Version_with_in-place_partition
*/
inline size_t quicksort_part( void** array, size_t left,
size_t right, size_t pivot,
int (*compare)( const void*, const void* ) ) {
void* pivot_value = array[ pivot ];
dprintf( "quicksort_part: array at %p, between %d and %d, pivot %d\n",
array, left, right, pivot );
/* swap array[pivotIndex] and array[right] // Move pivot to end */
array[ pivot ] = array[ right ];
array[ right ] = pivot_value;
size_t store_index = left;
void* store_val;
size_t i;
for( i = left; i < right; i++ ) {
dprintf( "quicksort_part: at %d, string at %p\n",
i, &array[i] );
/* if array[i] <= pivotValue */
if ( compare( array[i], pivot_value ) <= 0 ) {
/* swap array[i] and array[storeIndex] */
store_val = array[ store_index ];
array[ store_index ] = array[ i ];
array[ i ] = store_val;
store_index++;
}
}
/* // Move pivot to its final place
* swap array[storeIndex] and array[right]
*/
store_val = array[ store_index ];
array[ store_index ] = array[ right ];
array[ right ] = store_val;
return store_index;
}
inline void quicksort_main( void** array, size_t left, size_t right,
int (*compare)( const void*, const void* ) ) {
if ( right > left ) {
size_t pivot_index =
quicksort_part( array, left, right, left, compare );
quicksort_main( array, left, pivot_index-1, compare );
quicksort_main( array, pivot_index+1, right, compare );
}
}
inline int void_strcmp( const void* a, const void* b ) {
return strcmp( (char*)a, (char*)b );
}
inline int void_strcasecmp( const void* a, const void* b ) {
return strcasecmp( (char*)a, (char*)b );
}
void sort_array( char** array, size_t length,
int (*compare)( const void*, const void* ) ) {
quicksort_main( array, 0, length-1, compare );
}
void sort_str_array( char** array, size_t length, int case_insensitive ) {
if ( case_insensitive )
quicksort_main( (void**)array, 0, length-1, &void_strcasecmp );
else
quicksort_main( (void**)array, 0, length-1, &void_strcmp );
}

View File

@ -1,6 +1,7 @@
#include <varray.h>
#include <stdlib.h>
#include <dprintf.h>
#include <util.h>
/*
* Variable array implementation. The following implements a linked-list
* allowing for easy dynamic array manipulation.
@ -10,11 +11,12 @@
* for efficient indexing once the content is settled.
*/
/* Helper: Shallow-copy function for objects */
void* object_shallow_copy( void* orig, size_t size ) {
void* out = malloc( size );
if ( out != NULL ) memcpy( out, orig, size );
return out;
/* Special strdup. This is used for string arrays, since the strings can be
* variable length. strdup takes one argument, so we make a wrapper to ignore
* the object size (which should be zero).
*/
void* va_strdup( void* orig, size_t size ) {
return (void*)strdup( (char*)orig );
}
/* Create a new vararray
@ -42,11 +44,16 @@ struct vararray* create_vararray( struct vararray* dest, size_t obj_size,
dest->head = NULL;
dest->tail = NULL;
if ( copy_func == NULL )
dest->copy_func = &object_shallow_copy;
if ( obj_size > 0 ) /* Object container */
dest->copy_func = &memdup; /* see util.h
* and util.c
*/
else
dest->copy_func = &va_strdup;
else
dest->copy_func = copy_func;
if ( destroy_func == NULL )
dest->destroy_func = &free;
dest->destroy_func = &free; /* standard C library */
else
dest->destroy_func = destroy_func;
}
@ -82,6 +89,61 @@ void destroy_vararray( struct vararray* dest ) {
dprintf("destroy_vararray: array at %p and all contents freed\n", dest);
}
/* Copy a vararray
*
* This does a deep copy of the entire contents in the array.
*
* Returns the copy on success, or NULL on failure
*/
struct vararray* copy_vararray( struct vararray* src ) {
if ( src == NULL ) return NULL;
/* Start by doing a shallow copy of the struct. */
struct vararray* dest = (struct vararray*)
memdup( (void*)src, sizeof( struct vararray ) );
if ( dest == NULL ) return NULL;
/* Unlink the original chain from the copy */
dest->head = NULL;
dest->tail = NULL;
/* Okay, now start at the head, and duplicate each node. */
struct vararray_node* s_node = src->head;
struct vararray_node* d_node = dest->head;
while( s_node != NULL ) {
/* Copy the node */
struct vararray_node* copy = (struct vararray_node*)
memdup((void*)s_node, sizeof( struct vararray_node ));
if ( copy == NULL ) {
/* Node copy failed */
destroy_vararray( dest );
return NULL;
}
/* Copy the node contents if needed */
if ( copy->using_ptr )
copy->data.as_ptr = (*src->copy_func)
(copy->data.as_ptr, src->data_size);
/* Link it in */
copy->prev = d_node;
if ( d_node == NULL )
dest->head = copy;
else
d_node->next = copy;
/* Move on */
s_node = s_node->next;
d_node = copy;
}
/* Now at the end of the chain... we can link the tail */
dest->tail = d_node;
/* Linking should be complete... return the new array */
return dest;
}
/* Helper function: Locate a node by index. 'end' specifies from which
* end to begin the count, zero for the start (head), non-sero for
* the end (tail).
@ -538,6 +600,7 @@ struct static_obj_array* export_obj_array( struct vararray* va ) {
sa->length = va->length;
sa->copy_func = va->copy_func;
sa->destroy_func = va->destroy_func;
sa->obj_size = va->data_size;
struct vararray_node* node = va->head;
void** destptr = sa->object;
@ -567,6 +630,7 @@ struct static_string_array* export_string_array( struct vararray* va ) {
sa->length = va->length;
sa->copy_func = va->copy_func;
sa->destroy_func = va->destroy_func;
sa->obj_size = 0;
struct vararray_node* node = va->head;
size_t index = 0;
@ -639,3 +703,60 @@ void destroy_array( void* container, size_t length,
free( elements );
}
}
/* Duplicate a static array. The following will make a deep copy of any
* statically-allocated array in one of the above structs.
*
* Returns the copy on success, NULL on failure.
*/
struct static_simple_array* duplicate_simple_array_impl(
struct static_simple_array* src, size_t data_size ) {
if ( src == NULL ) return NULL;
/* Duplicate the array container */
struct static_simple_array* copy = (struct static_simple_array*)
memdup( (void*)src, sizeof( struct static_simple_array ) );
if ( copy == NULL ) return NULL;
/* Now, duplicate the data */
copy->value = memdup( src->value, data_size*src->length );
if ( copy->value == NULL ) {
free( copy );
return NULL;
}
return copy;
}
struct static_obj_array* duplicate_obj_array_impl(
struct static_obj_array* src ) {
if ( src == NULL ) return NULL;
/* Duplicate the array container */
struct static_obj_array* copy = (struct static_obj_array*)
memdup( (void*)src, sizeof( struct static_obj_array ) );
if ( copy == NULL ) return NULL;
/* Allocate the array itself */
copy->object = (void**)calloc( src->length, sizeof(void*) );
if ( copy->object == NULL ) {
free( copy );
return NULL;
}
/* Now, duplicate the data */
size_t i;
for( i=0; i<src->length; i++) {
copy->object[i] = (*src->copy_func)( src->object[i],
src->obj_size );
if ( copy->object[i] == NULL ) {
copy->length = i;
destroy_obj_array( copy, object );
return NULL;
}
}
return copy;
}