return of pointer

Discussion in 'C Programming' started by Sergey Koveshnikov, Nov 4, 2003.

  1. Hello,
    If my function return a pointer on a memory area, where do I free it?
    e.x.:
    char *foo()
    {
    char *p;
    p = malloc(10);
    strcpy(p, "something");
    return(p);
    }
    void bar()
    {
    char *p = foo();
    printf("%s", p);
    free(p);
    }
    But force anybody to calling free() after use foo() not elegant solution,
    for my mind... How can I avoid posible memory leaks if somebody forget to
    call free()?

    Thanks a lot!
    --
    Sergey Koveshnikov.
     
    Sergey Koveshnikov, Nov 4, 2003
    #1
    1. Advertising

  2. In article <bo7re9$r3c$>, Sergey Koveshnikov wrote:
    > Hello,
    > If my function return a pointer on a memory area, where do I free it?
    > e.x.:
    > char *foo()
    > {
    > char *p;
    > p = malloc(10);
    > strcpy(p, "something");
    > return(p);
    > }
    > void bar()
    > {
    > char *p = foo();
    > printf("%s", p);
    > free(p);
    > }
    > But force anybody to calling free() after use foo() not elegant solution,


    Maybe not, but as long as you are consistent and document the
    interface it works well.

    > for my mind... How can I avoid posible memory leaks if somebody forget to
    > call free()?


    You can't force someone to remember to call free().

    Creating a foo_alloc() and a foo_free() function might help
    though (untested):

    #include <stdlib.h>
    #include <stdio.h>

    int foo_alloc(char **p)
    {
    /* sanity checks of *p here */

    *p = malloc(10);
    if (*p == NULL) return 1;
    strcpy(*p, "something");
    return 0;
    }

    int foo_free(char **p)
    {
    /* sanity checks of *p here */

    free(*p);
    *p = NULL;

    return 0;
    }

    void bar()
    {
    char *p;

    foo_alloc(&p);
    printf("foo: %s\n", p);

    foo_free(&p);
    }




    --
    Andreas Kähäri
     
    Andreas Kahari, Nov 4, 2003
    #2
    1. Advertising

  3. i.e. No way to do automatic cleanup dynamic allocated memory, if it's
    unnecessary anywhere, except context of 'bar()', isn't it?
    --
    Sergey Koveshnikov.
     
    Sergey Koveshnikov, Nov 4, 2003
    #3
  4. In article <bo83p3$qkh$>, Sergey Koveshnikov wrote:
    > i.e. No way to do automatic cleanup dynamic allocated memory, if it's
    > unnecessary anywhere, except context of 'bar()', isn't it?


    There is no built-in garbage collector in C, no.

    --
    Andreas Kähäri
     
    Andreas Kahari, Nov 4, 2003
    #4
  5. But, what can I do if some foreign code expect from my function that it will
    return char*, and it doesn't call free()? Do you know any 'save string'
    library for ANSI C that provide self-destroyed strings?
    May be it's possible to solve this problem with macros or 'static' type?

    PS. Sorry if my questions are stupid, I'm new in the C.
    --
    Sergey Koveshnikov.
     
    Sergey Koveshnikov, Nov 4, 2003
    #5
  6. Sergey Koveshnikov <> wrote:

    > Hello,
    > If my function return a pointer on a memory area, where do I free it?
    > e.x.:
    > char *foo()
    > {
    > char *p;
    > p = malloc(10);
    > strcpy(p, "something");
    > return(p);
    > }
    > void bar()
    > {
    > char *p = foo();
    > printf("%s", p);
    > free(p);
    > }
    > But force anybody to calling free() after use foo() not elegant solution,
    > for my mind... How can I avoid posible memory leaks if somebody forget to
    > call free()?


    Maybe it's a good idea to adopt the bevaviour of, for example, the
    standard library string functions, and let the caller take the full
    responsibility for memory allocation/deallocation. E.g.:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #define BIG_ENOUGH 50

    char *foo( char *p )
    {
    if ( p != NULL )
    strcpy( p, "something" );
    return( p );
    }

    int main( void )
    {
    char *p = malloc( BIG_ENOUGH );
    if ( foo( p ) != NULL )
    printf( "%s\n", p );
    free( p );
    return 0;
    }

    Note: you may want foo() to take an additional argument specifying the
    buffer size, in order to avoid buffer overflows.

    OTOH, if you, for example, want to imitate the non-standard strdup
    function, you of course have to allocate the memory inside the function,
    document the behaviour and hopefully the user of the function will get
    it right.

    HTH
    Regards
    --
    Irrwahn
    ()
     
    Irrwahn Grausewitz, Nov 4, 2003
    #6
  7. In article <bo89eu$dc4$>, Sergey Koveshnikov wrote:
    > But, what can I do if some foreign code expect from my function that it will
    > return char*, and it doesn't call free()? Do you know any 'save string'
    > library for ANSI C that provide self-destroyed strings?
    > May be it's possible to solve this problem with macros or 'static' type?
    >
    > PS. Sorry if my questions are stupid, I'm new in the C.


    You, as a library implementor, are not required to make sure
    that the users of the library writes correct code.

    Document the correct usage (whether to explicitly free() storage
    or to call a special cleanup function), and that's the end of
    your troubles.

    It is possible to return a pointer to a static array, but you
    will have to make sure that you point this out to the library
    user as well, so that he/she doesn't wander off and tries to
    free() that pointer, or so that he/she doesn't store its value
    somewhere and gets confused when the data it points to suddenly
    changes after the next call to your function.

    My favourite solution is to provide two functions; one for
    allocation and one for deallocation. This is a bit too involved
    if it's just a question of a simple string and not a struct or
    some opaque data type.

    --
    Andreas Kähäri
     
    Andreas Kahari, Nov 4, 2003
    #7
  8. Sergey Koveshnikov

    Dan Pop Guest

    In <bo7re9$r3c$> Sergey Koveshnikov <> writes:

    >Hello,
    >If my function return a pointer on a memory area, where do I free it?
    >e.x.:
    >char *foo()
    >{
    > char *p;
    > p = malloc(10);
    > strcpy(p, "something");
    > return(p);
    >}
    >void bar()
    >{
    > char *p = foo();
    > printf("%s", p);
    > free(p);
    >}
    >But force anybody to calling free() after use foo() not elegant solution,
    >for my mind... How can I avoid posible memory leaks if somebody forget to
    >call free()?


    This is a perfectly valid approach. No matter where the memory is
    dynamically allocated, someone may forget to release it. This is an
    intrinsic issue with dynamically allocated memory.

    The alternative is to pass to foo the address of the buffer. If the
    buffer was not dynamically allocated, no need to release it:

    #define STRING "something"

    void foo(char *p)
    {
    strcpy(p, STRING);
    }

    void bar()
    {
    char p[sizeof STRING];
    foo(p);
    printf("%s\n", p);
    }

    Of course, this doesn't work if only the called function can evaluate
    the size of the buffer.

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
     
    Dan Pop, Nov 4, 2003
    #8
  9. Sergey Koveshnikov

    Morris Dovey Guest

    Sergey Koveshnikov wrote:

    > Hello,
    > If my function return a pointer on a memory area, where do I free it?
    > e.x.:
    > char *foo()
    > {
    > char *p;
    > p = malloc(10);
    > strcpy(p, "something");
    > return(p);
    > }
    > void bar()
    > {
    > char *p = foo();
    > printf("%s", p);
    > free(p);
    > }
    > But force anybody to calling free() after use foo() not elegant solution,
    > for my mind... How can I avoid posible memory leaks if somebody forget to
    > call free()?
    >
    > Thanks a lot!


    Sergey...

    You may be able to write a wrapper for malloc() that keeps a list
    of all allocations it makes, then write a function to call free()
    for each of the allocations in that list. Refine as necessary.

    --
    Morris Dovey
    West Des Moines, Iowa USA
    C links at http://www.iedu.com/c
    Read my lips: The apple doesn't fall very far from the tree.
     
    Morris Dovey, Nov 4, 2003
    #9
  10. Sergey Koveshnikov

    James Hu Guest

    On 2003-11-04, Sergey Koveshnikov <> wrote:
    > Hello,
    > If my function return a pointer on a memory area, where do I free it?
    > e.x.:
    > char *foo()
    > {
    > char *p;
    > p = malloc(10);
    > strcpy(p, "something");
    > return(p);
    > }
    > void bar()
    > {
    > char *p = foo();
    > printf("%s", p);
    > free(p);
    > }
    > But force anybody to calling free() after use foo() not elegant solution,
    > for my mind... How can I avoid posible memory leaks if somebody forget to
    > call free()?
    >
    > Thanks a lot!


    You can design a callback interface to someone that wants the
    memory:

    void foo(void (*cb)(char *))
    {
    char *p = malloc(10);
    if (p)
    strcpy(p, "something");
    cb(p);
    free(p);
    }

    void mycallback(char *p)
    {
    printf("%s", p);
    }

    void bar(char *p)
    {
    foo(mycallback);
    }

    -- James
     
    James Hu, Nov 4, 2003
    #10
  11. Sergey Koveshnikov

    Dan Pop Guest

    In <bo89eu$dc4$> Sergey Koveshnikov <> writes:

    >But, what can I do if some foreign code expect from my function that it will
    >return char*, and it doesn't call free()?


    You only need to worry about the correctness of the code you write
    yourself. No matter what you do or don't, someone may use your code
    incorrectly. It's his problem, not yours. That is, assuming that you
    have properly documented the correct usage of your code.

    >Do you know any 'save string'
    >library for ANSI C that provide self-destroyed strings?


    There is no way to write such a library in C.

    >May be it's possible to solve this problem with macros or 'static' type?


    The only way to solve this problem is by using automatic allocation in
    the function that is calling your function and passing the address (and,
    possibly, the size of the allocated block) to your function. The string
    will self-destroy when the function that allocated it returns.

    The other "solution", return the address of a statically allocated buffer
    inside your function, which is reused at each invocation of your
    function, has its own big caveat: if the caller forgets to make a local
    copy of the buffer before calling the function again, he ends up with a
    hard to find bug. Typical example: imagine that your function returns
    a random string each time it's called, in a statically allocated buffer.
    What happens to the following printf call:

    char *randstr(void)
    {
    static char alnum[] = "0123456789abcdefghijklmnopqrstuvwxyz";
    static char str[10];
    int i;

    for (i = 0; i < sizeof str - 1; i++)
    str = alnum[rand() % strlen(alnum)];
    return str;
    }
    ....
    printf("%s %s\n", randstr(), randstr());

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
     
    Dan Pop, Nov 4, 2003
    #11
  12. On Tue, 4 Nov 2003, James Hu wrote:
    >
    > On 2003-11-04, Sergey Koveshnikov <> wrote:
    > >
    > > If my function return a pointer on a memory area, where do I free it?

    [snip]
    > > But force anybody to calling free() after use foo() not elegant solution,
    > > for my mind... How can I avoid posible memory leaks if somebody forget
    > > to call free()?

    >
    > You can design a callback interface to someone that wants the
    > memory:


    [snip code example]

    > void bar(char *p)


    ITYM void bar(void) here.

    > {
    > foo(mycallback);
    > }



    Wow -- I'd never seen that approach before! It's... interesting,
    but I can't really see myself ever using it. :)
    [Just in case the OP didn't get it the first time around: here,
    'bar' is the "main" function of the program, and 'foo' is the
    function that encapsulates everything to do with a single dynamic
    object. 'mycallback'/'cb' is a pointer to a function that says,
    "After creating the object, do this and this and this; once that's
    done, it's safe to free the object again."]
    So to use my favorite example, we could take an image format
    converter that looked like this [NOT REAL CODE]:

    struct Im
    {
    unsigned char *data;
    int w, h;
    };

    int main(int argc, char **argv)
    {
    struct Im *image;
    ReadPGM(argv[1], &image);
    WritePPM(argv[2], image);
    free(image->data);
    free(image);
    return 0;
    }

    void ReadPGM(const char *fname, struct Im *im)
    {
    /* something like... */
    im = malloc(sizeof *im);
    im->w = im->h = 42;
    im->data = malloc(im->w * im->h);
    }


    and transform it into this:

    static const char *OutFileName = NULL;

    int main(int argc, char **argv)
    {
    OutFileName = argv[2];
    ProcessPGM(argv[1], write_it_out);
    return 0;
    }

    void write_it_out(struct Im *im)
    {
    WritePPM(OutFileName, im);
    }

    void ProcessPGM(const char *fname, void (*cb)(struct Im *))
    {
    struct Im *image;
    ReadPGM(argv[1], &image);
    cb(image);
    free(image->data);
    free(image);
    }


    Uglier in this case, but possibly this could save a few
    keystrokes and possible memory leaks down the road. :)

    HTH,
    -Arthur
     
    Arthur J. O'Dwyer, Nov 4, 2003
    #12
  13. Sergey Koveshnikov

    James Hu Guest

    "Arthur J. O'Dwyer" <> wrote in message news:<>...
    > On Tue, 4 Nov 2003, James Hu wrote:
    > >
    > > void bar(char *p)

    >
    > ITYM void bar(void) here.


    Yes, thanks.

    > Wow -- I'd never seen that approach before! It's... interesting,
    > but I can't really see myself ever using it. :)


    The original poster just asked if there was a solution that didn't
    require the user of the interface to call free to reap the memory.
    I just wanted to demonstrate that there was one that did not require
    a garbage collection facility.

    -- James
     
    James Hu, Nov 4, 2003
    #13
  14. Sergey Koveshnikov

    Joe Wright Guest

    Morris Dovey wrote:
    >
    > Sergey Koveshnikov wrote:
    >
    > > Hello,

    [ lots of snips ]
    > > But force anybody to calling free() after use foo() not elegant solution,
    > > for my mind... How can I avoid posible memory leaks if somebody forget to
    > > call free()?
    > >
    > > Thanks a lot!

    >
    > Sergey...
    >
    > You may be able to write a wrapper for malloc() that keeps a list
    > of all allocations it makes, then write a function to call free()
    > for each of the allocations in that list. Refine as necessary.
    >

    Morris,

    I have done just that. I have a 'library' called GE for Garbage
    Eliminator. With a little preprocessor magic it traps the *alloc() calls
    and free(). GE manages a list of structures which hold the pointers to
    allocated memory and the size of the allocation. A library function
    'size_t size(void *p)' will look up p in the list and return its
    allocated size (or 0 if p is not found). free(void *p) is trapped and
    and looks up p and frees it and its entry in the list if found,
    otherwise does nothing. A library function freeall() will free all
    allocations including the list itself.

    --
    Joe Wright http://www.jw-wright.com
    "Everything should be made as simple as possible, but not simpler."
    --- Albert Einstein ---
     
    Joe Wright, Nov 5, 2003
    #14
  15. Sergey Koveshnikov

    Morris Dovey Guest

    Joe Wright wrote:

    > I have done just that. I have a 'library' called GE for
    > Garbage Eliminator. With a little preprocessor magic it traps
    > the *alloc() calls and free(). GE manages a list of structures
    > which hold the pointers to allocated memory and the size of
    > the allocation. A library function 'size_t size(void *p)' will
    > look up p in the list and return its allocated size (or 0 if p
    > is not found). free(void *p) is trapped and and looks up p and
    > frees it and its entry in the list if found, otherwise does
    > nothing. A library function freeall() will free all
    > allocations including the list itself.


    Joe...

    Damn I'm good! ( Too bad you beat me to it. :cool:

    I particularly like your size() function. 'Xcuse me while I go
    plagerize^H^H^H^H^H^H^H^H^H(ahem) make some notes...
    --
    Morris Dovey
    West Des Moines, Iowa USA
    C links at http://www.iedu.com/c
    Read my lips: The apple doesn't fall very far from the tree.
     
    Morris Dovey, Nov 5, 2003
    #15
  16. "Dan Pop" <> wrote in message
    news:bo8ons$159$...
    > In <bo89eu$dc4$> Sergey Koveshnikov

    <> writes:
    >
    > >But, what can I do if some foreign code expect from my function that it

    will
    > >return char*, and it doesn't call free()?

    >
    > You only need to worry about the correctness of the code you write
    > yourself. No matter what you do or don't, someone may use your code
    > incorrectly. It's his problem, not yours. That is, assuming that you
    > have properly documented the correct usage of your code.


    (snip)

    > The other "solution", return the address of a statically allocated buffer
    > inside your function, which is reused at each invocation of your
    > function, has its own big caveat: if the caller forgets to make a local
    > copy of the buffer before calling the function again, he ends up with a
    > hard to find bug. Typical example: imagine that your function returns
    > a random string each time it's called, in a statically allocated buffer.


    (snip)

    Or consider the ctime() C library function and a program to print the
    creation, modification, and last access time for a file.

    printf("%s %s %s\n",ctime(created),ctime(modified),ctime(accessed));

    (yes, it actually happened to me. It didn't take long to figure it out, but
    it did happen.)

    -- glen
     
    Glen Herrmannsfeldt, Nov 5, 2003
    #16
  17. Sergey Koveshnikov

    CBFalconer Guest

    Morris Dovey wrote:
    > Joe Wright wrote:
    >
    > > I have done just that. I have a 'library' called GE for
    > > Garbage Eliminator. With a little preprocessor magic it traps
    > > the *alloc() calls and free(). GE manages a list of structures
    > > which hold the pointers to allocated memory and the size of
    > > the allocation. A library function 'size_t size(void *p)' will
    > > look up p in the list and return its allocated size (or 0 if p
    > > is not found). free(void *p) is trapped and and looks up p and
    > > frees it and its entry in the list if found, otherwise does
    > > nothing. A library function freeall() will free all
    > > allocations including the list itself.

    >
    > Damn I'm good! ( Too bad you beat me to it. :cool:
    >
    > I particularly like your size() function. 'Xcuse me while I go
    > plagerize^H^H^H^H^H^H^H^H^H(ahem) make some notes...


    ALL those features are available in nmalloc.zip, available at:

    <http://cbfalconer.home.att.net/download/>

    which was written for the DJGPP system, and should work on any
    system whose fundamental allocation scheme depends on sbrk and
    deals with 8 bit bytes. The extension function memalign() is not
    ready. An equivalent to the size function you mention is
    trivially implemented, and could be added to the malldbg module.

    The code is very close to standard C, with the principal deviation
    being the use of GCC varargs macros for debugging purposes.
    Because of the form of call these don't simply define away. Those
    macros also use non-standard write calls, the purpose being to
    allow them to function during initialization, at least under
    DJGPP.

    --
    Chuck F () ()
    Available for consulting/temporary embedded and systems.
    <http://cbfalconer.home.att.net> USE worldnet address!
     
    CBFalconer, Nov 5, 2003
    #17
  18. "Glen Herrmannsfeldt" <> writes:
    > "Dan Pop" <> wrote in message
    > news:bo8ons$159$...
    > > In <bo89eu$dc4$> Sergey Koveshnikov

    > <> writes:
    > >
    > > >But, what can I do if some foreign code expect from my function that it

    > will
    > > >return char*, and it doesn't call free()?

    > >
    > > You only need to worry about the correctness of the code you write
    > > yourself. No matter what you do or don't, someone may use your code
    > > incorrectly. It's his problem, not yours. That is, assuming that you
    > > have properly documented the correct usage of your code.

    >
    > (snip)
    >
    > > The other "solution", return the address of a statically allocated buffer
    > > inside your function, which is reused at each invocation of your
    > > function, has its own big caveat: if the caller forgets to make a local
    > > copy of the buffer before calling the function again, he ends up with a
    > > hard to find bug. Typical example: imagine that your function returns
    > > a random string each time it's called, in a statically allocated buffer.

    >
    > (snip)
    >
    > Or consider the ctime() C library function and a program to print the
    > creation, modification, and last access time for a file.
    >
    > printf("%s %s %s\n",ctime(created),ctime(modified),ctime(accessed));
    >
    > (yes, it actually happened to me. It didn't take long to figure it out, but
    > it did happen.)


    Another solution I've used in the past is to declare a static array of
    objects of the result type, and rotate among the elements for each
    call. For example:

    #include <stdio.h>

    char *twice(char *s)
    {
    #define RESULT_COUNT 6
    static char result[RESULT_COUNT][100];
    static int index = -1;

    index ++;
    if (index >= RESULT_COUNT) index = 0;

    strcpy(result[index], s);
    strcat(result[index], s);
    return result[index];
    }

    int main(void)
    {
    printf("twice(\"foo\") = \"%s\", twice(\"bar\") = \"%s\"\n",
    twice("foo"), twice("bar"));
    return 0;
    }

    This lets me have up to RESULT_COUNT active calls to the function
    without collisions, while not requiring the caller to allocate or
    deallocate the result. It's not particularly pretty, and it can run
    into problems if the caller saves a pointer to the result, but I found
    it useful.

    Probably the worst feature of this approach is that any problems are
    likely to be rare, showing up only in unusual circumstances. It
    handles the easy cases and doesn't do much for the really hard ones.
    It also introduces the temptation of kludging around any problems by
    increasing RESULT_COUNT.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://www.sdsc.edu/~kst>
    Schroedinger does Shakespeare: "To be *and* not to be"
     
    Keith Thompson, Nov 5, 2003
    #18
  19. Sergey Koveshnikov

    Morris Dovey Guest

    CBFalconer wrote:

    > ALL those features are available in nmalloc.zip, available at:
    >
    > <http://cbfalconer.home.att.net/download/>
    >
    > which was written for the DJGPP system, and should work on any
    > system whose fundamental allocation scheme depends on sbrk and
    > deals with 8 bit bytes. The extension function memalign() is not
    > ready. An equivalent to the size function you mention is
    > trivially implemented, and could be added to the malldbg module.
    >
    > The code is very close to standard C, with the principal deviation
    > being the use of GCC varargs macros for debugging purposes.
    > Because of the form of call these don't simply define away. Those
    > macros also use non-standard write calls, the purpose being to
    > allow them to function during initialization, at least under
    > DJGPP.


    Sorry. I knew you had a pile of memory management functions on
    the web - I just forgot.

    Sergey, browse Chuck's site - he writes pretty good stuff...

    --
    Morris Dovey
    West Des Moines, Iowa USA
    C links at http://www.iedu.com/c
    Read my lips: The apple doesn't fall very far from the tree.
     
    Morris Dovey, Nov 5, 2003
    #19
  20. Sergey Koveshnikov

    goose Guest

    (Dan Pop) wrote in message news:<bo8erg$7ik$>...
    > In <bo7re9$r3c$> Sergey Koveshnikov <> writes:
    >
    > >Hello,
    > >If my function return a pointer on a memory area, where do I free it?
    > >e.x.:
    > >char *foo()
    > >{
    > > char *p;
    > > p = malloc(10);
    > > strcpy(p, "something");
    > > return(p);
    > >}
    > >void bar()
    > >{
    > > char *p = foo();
    > > printf("%s", p);
    > > free(p);
    > >}
    > >But force anybody to calling free() after use foo() not elegant solution,
    > >for my mind... How can I avoid posible memory leaks if somebody forget to
    > >call free()?

    >
    > This is a perfectly valid approach. No matter where the memory is
    > dynamically allocated, someone may forget to release it. This is an
    > intrinsic issue with dynamically allocated memory.
    >
    > The alternative is to pass to foo the address of the buffer. If the
    > buffer was not dynamically allocated, no need to release it:
    >
    > #define STRING "something"
    >
    > void foo(char *p)
    > {
    > strcpy(p, STRING);
    > }
    >
    > void bar()
    > {
    > char p[sizeof STRING];
    > foo(p);
    > printf("%s\n", p);
    > }
    >
    > Of course, this doesn't work if only the called function can evaluate
    > the size of the buffer.
    >


    an alternative is to have the called function be able to
    return the size of the buffer needed.

    #define STRING "something"

    size_t foo (char *p, size_t size) {
    size_t needed_size = strlen (STRING) +1;
    if (p==NULL || size < needed_size) {
    return needed_size;
    }
    strcpy (p, STRING);
    return 0;
    }

    void bar ()
    char *the_string = NULL;
    size_t size_of_the_string = 0;

    /* get the amount of memory needed */
    size_of_the_string = foo (NULL, 0);

    the_string = malloc (size_of_the_string);
    if (!the_string) {
    /* report error!!! */
    } else {
    foo (the_string, size_of_the_string);
    }
    }


    goose,
    criticise away :)
     
    goose, Nov 6, 2003
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    10
    Views:
    738
    Chris Torek
    Feb 4, 2005
  2. Greenhorn
    Replies:
    15
    Views:
    881
    Keith Thompson
    Mar 6, 2005
  3. jimjim
    Replies:
    16
    Views:
    873
    Jordan Abel
    Mar 28, 2006
  4. decker
    Replies:
    9
    Views:
    349
    Barry Schwarz
    Nov 10, 2007
  5. Replies:
    4
    Views:
    1,323
    Fred Zwarts
    Jul 2, 2009
Loading...

Share This Page