Params -> local vars?

Discussion in 'C Programming' started by Jon Howson, Jul 30, 2012.

  1. Jon Howson

    Jon Howson Guest

    Hello

    I was reading thru some (yellowing!) mimeographed introductory notes on C
    and I found code like the below.

    main(argc,argv)
    int argc;
    char *argv[];
    {
    char *malloc();
    char *strcpy();
    int puts();
    void free();

    char *s;
    if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
    strcpy(s,"Hello, ");
    strcpy(s+7,argv[1]);
    puts(s);
    free(s);
    }else{
    puts("Program failed, unknown error");
    }
    }

    I'm trying to work out what's happening. At the start of the function, I
    guess the parameters are being converted into local variables, maybe a
    way to get pass-by-value in C?

    Also what is the advantage of declaring the string and memory functions
    locally to the function?

    Cheers
    Jon
    Jon Howson, Jul 30, 2012
    #1
    1. Advertising

  2. Jon Howson

    Eric Sosman Guest

    On 7/30/2012 3:35 PM, Jon Howson wrote:
    > Hello
    >
    > I was reading thru some (yellowing!) mimeographed introductory notes on C
    > and I found code like the below.
    >
    > main(argc,argv)
    > int argc;
    > char *argv[];
    > {
    > char *malloc();
    > char *strcpy();
    > int puts();
    > void free();
    >
    > char *s;
    > if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
    > strcpy(s,"Hello, ");
    > strcpy(s+7,argv[1]);
    > puts(s);
    > free(s);
    > }else{
    > puts("Program failed, unknown error");
    > }
    > }
    >
    > I'm trying to work out what's happening. At the start of the function, I
    > guess the parameters are being converted into local variables, maybe a
    > way to get pass-by-value in C?


    It's the original "K&R style" way of defining functions and
    function parameters. In this style, the names of the parameters
    are separated from their types: The first line lists the names,
    in order, and the lines before the opening `{' "complete" the
    picture by filling in the missing details: types and perhaps
    qualifiers.

    Modern C still supports K&R style in the name of backward
    compatibility, but it's been obsolete since C89/C90 compilers
    became generally available some two decades ago. In a way, it's
    a good sign that you didn't know about this style; it shows that
    progress is indeed taking place!

    > Also what is the advantage of declaring the string and memory functions
    > locally to the function?


    None; it's an anti-advantage. In the long-gone Days of Yore
    there was no universally-available header declaring malloc() and
    free() -- some implementations provided <malloc.h>, some didn't.
    Also, there was no universal header for the string functions --
    some implementations used <string.h> and some used <strings.h>.
    In those days there might have been a small advantage in declaring
    those functions yourself, so you didn't have to worry about them
    being in different (or no!) places on different systems. (Note also
    that malloc() is declared as returning a `char*' -- K&R C had no
    `void*'.)

    But there's never been a good reason to write a free-hand
    declaration of puts()! The <stdio.h> header has been around since
    a microsecond after the Big Bang, and the right way to declare the
    I/O functions has always been `#include <stdio.h>'.

    Those old notes may have value, but only on Antiques Road Show.

    --
    Eric Sosman
    d
    Eric Sosman, Jul 30, 2012
    #2
    1. Advertising

  3. Jon Howson

    Stefan Ram Guest

    Jon Howson <> writes:
    >main(argc,argv)
    > int argc;
    > char *argv[];


    This is an older variant of the C language,
    that is usually not used today to write new code in.

    >I'm trying to work out what's happening.


    If your »introductory notes on C« do not explain this,
    try other tutorials or textbooks, possibly, describing
    a more recent C version.
    Stefan Ram, Jul 30, 2012
    #3
  4. Jon Howson

    James Kuyper Guest

    On 07/30/2012 03:35 PM, Jon Howson wrote:
    > Hello
    >
    > I was reading thru some (yellowing!) mimeographed introductory notes on C
    > and I found code like the below.
    >
    > main(argc,argv)
    > int argc;
    > char *argv[];
    > {
    > char *malloc();
    > char *strcpy();
    > int puts();
    > void free();
    >
    > char *s;
    > if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
    > strcpy(s,"Hello, ");
    > strcpy(s+7,argv[1]);
    > puts(s);
    > free(s);
    > }else{
    > puts("Program failed, unknown error");
    > }
    > }
    >
    > I'm trying to work out what's happening. At the start of the function, I
    > guess the parameters are being converted into local variables, maybe a
    > way to get pass-by-value in C?


    No - that's a non-prototyped function definition. They're still
    supported by the C standard, but they've been obsolete ever since the
    introduction of function prototypes in C90. Function prototypes turn on
    various mandatory diagnostics, but as far as the actual behavior of the
    code, that declaration is equivalent to:

    main(int argc, char *argv[])

    Note that no return type is specified. This used to be permitted; the
    function was implicitly declared as returning an 'int'. However,
    "implicit int" has not been supported since C99.

    > Also what is the advantage of declaring the string and memory functions
    > locally to the function?


    You are allowed to provide your own declaration of some standard library
    functions, so long as it is compatible with the correct declaration.
    However, it's always a bad idea, even when permitted. You're much better
    off #including the appropriate standard headers. In this case, those are
    strings.h, stdio.h, and stdlib.h.

    The advantage is very small, or possibly even negative: it restricts the
    scope of those declarations to the body of main(). This allows
    definition of identifiers with the same name and a different meaning
    outside of main(), but that would still only work if they did not have
    external linkage.

    One disadvantage is that a declarations may become incorrect, even if it
    originally was correct. That is in fact the case in this code. At one
    time, before the first standard, malloc() returned a char*. However,
    since the first standard malloc() has returned a void*. However, since
    char* and void* are required to have the same representation and
    alignment requirements, they are intended to be equivalent for this kind
    of use; it would take a pretty perverse (but still fully conforming)
    implementation of C for that to be a real problem.
    James Kuyper, Jul 30, 2012
    #4
  5. On 30-Jul-12 14:35, Jon Howson wrote:
    > I was reading thru some (yellowing!) mimeographed introductory notes on C
    > and I found code like the below.
    >
    > main(argc,argv)
    > int argc;
    > char *argv[];
    > {
    > char *malloc();
    > char *strcpy();
    > int puts();
    > void free();
    >
    > char *s;
    > if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
    > strcpy(s,"Hello, ");
    > strcpy(s+7,argv[1]);
    > puts(s);
    > free(s);
    > }else{
    > puts("Program failed, unknown error");
    > }
    > }
    >
    > I'm trying to work out what's happening. At the start of the function, I
    > guess the parameters are being converted into local variables, maybe a
    > way to get pass-by-value in C?
    >
    > Also what is the advantage of declaring the string and memory functions
    > locally to the function?


    This is K&R C, which had a different syntax for arguments and no
    standard headers. To restate it in ANSI C:

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

    int main(int argc, char *argv[]) {
    char *s;

    if (argc>1 && (s = malloc(strlen(argv[1]) + 8))) {
    strcpy(s, "Hello, ");
    strcpy(s+7, argv[1]);
    puts(s);
    free(s);
    } else {
    puts("Program failed, unknown error");
    }
    }

    A modern coder would just use printf(), but perhaps that hadn't been
    taught yet or would have obscured what the text was trying to teach
    about string manipulation--or printf() was considered excessive for such
    a trivial task on the day's limited machines.

    Unless you're interested in software archaeology, I would recommend you
    discard any text that uses K&R C, which has been obsolete since ANSI C
    was published in 1989.

    S

    --
    Stephen Sprunk "God does not play dice." --Albert Einstein
    CCIE #3723 "God is an inveterate gambler, and He throws the
    K5SSS dice at every possible opportunity." --Stephen Hawking
    Stephen Sprunk, Jul 30, 2012
    #5
  6. Jon Howson <> writes:
    > I was reading thru some (yellowing!) mimeographed introductory notes on C
    > and I found code like the below.
    >
    > main(argc,argv)
    > int argc;
    > char *argv[];


    This is an old-style function definition. It was the only form
    available before the 1989 ANSI C standard introduced prototypes
    (borrowed from C++). It's still legal -- except for the omission of the
    return type of main, which has been invalid since C99.

    The modern equivalent is:

    int main(int argc, char *argv[]) { ... }

    which has the advantage that it makes the parameter types visible to
    callers (which is typically less relevant for main than for other
    functions).

    > {
    > char *malloc();
    > char *strcpy();
    > int puts();
    > void free();
    >
    > char *s;
    > if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
    > strcpy(s,"Hello, ");
    > strcpy(s+7,argv[1]);
    > puts(s);
    > free(s);
    > }else{
    > puts("Program failed, unknown error");
    > }
    > }
    >
    > I'm trying to work out what's happening. At the start of the function, I
    > guess the parameters are being converted into local variables, maybe a
    > way to get pass-by-value in C?


    As I wrote above, it's an old-style definition. (Parameters are really
    local variables anyway; they differ from other local variables in how
    they're declared and in the way they're initialized with the values of
    the arguments given in the call.)

    > Also what is the advantage of declaring the string and memory functions
    > locally to the function?


    None. The right way to get those declarations is:

    #include <stdlib.h> /* malloc, free */
    #include <string.h> /* strcpy */
    #include <stdio.h> /* puts */

    which also has the advantage of providing prototypes, so the compiler
    can diagnose calls with incorrect arguments.

    The code as posted will probably work with modern compilers (as long as
    they permit the old-style "implicit int" rule, dropped by C99), but it's
    not a good example for writing new code.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Jul 30, 2012
    #6
  7. Jon Howson

    Joe Pfeiffer Guest

    Jon Howson <> writes:

    > Hello
    >
    > I was reading thru some (yellowing!) mimeographed introductory notes on C
    > and I found code like the below.
    >
    > main(argc,argv)
    > int argc;
    > char *argv[];
    > {
    > char *malloc();
    > char *strcpy();
    > int puts();
    > void free();
    >
    > char *s;
    > if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
    > strcpy(s,"Hello, ");
    > strcpy(s+7,argv[1]);
    > puts(s);
    > free(s);
    > }else{
    > puts("Program failed, unknown error");
    > }
    > }
    >
    > I'm trying to work out what's happening. At the start of the function, I
    > guess the parameters are being converted into local variables, maybe a
    > way to get pass-by-value in C?


    Do you mean this part?

    > main(argc,argv)
    > int argc;
    > char *argv[];
    > {


    That's just the way parameters used to be declared. It means exactly
    the same thing as

    main(int argc, char *argv[])

    > Also what is the advantage of declaring the string and memory functions
    > locally to the function?


    I can't think of one off-hand.
    Joe Pfeiffer, Jul 31, 2012
    #7
  8. Jon Howson

    Mark Bluemel Guest

    On 30/07/2012 21:09, Stephen Sprunk wrote:

    > Unless you're interested in software archaeology, I would recommend you
    > discard any text that uses K&R C, which has been obsolete since ANSI C
    > was published in 1989.


    A little harsh - I think that K&R 1st edition is worth keeping if you
    have a copy.
    Mark Bluemel, Jul 31, 2012
    #8
  9. Jon Howson

    Kleuske Guest

    On Tue, 31 Jul 2012 08:34:42 +0100, Mark Bluemel saw fit to publish the
    following:

    > On 30/07/2012 21:09, Stephen Sprunk wrote:
    >
    >> Unless you're interested in software archaeology, I would recommend you
    >> discard any text that uses K&R C, which has been obsolete since ANSI C
    >> was published in 1989.

    >
    > A little harsh - I think that K&R 1st edition is worth keeping if you
    > have a copy.


    Not really smart, either. It may just be a collectors item.I would pay
    money for an original K&R manual, if only for sentimental reasons.

    For educational purposes, however, I'm with Sprunk.
    Kleuske, Jul 31, 2012
    #9
  10. Jon Howson

    Eric Sosman Guest

    On 7/31/2012 3:29 PM, Kleuske wrote:
    > On Tue, 31 Jul 2012 08:34:42 +0100, Mark Bluemel saw fit to publish the
    > following:
    >
    >> On 30/07/2012 21:09, Stephen Sprunk wrote:
    >>
    >>> Unless you're interested in software archaeology, I would recommend you
    >>> discard any text that uses K&R C, which has been obsolete since ANSI C
    >>> was published in 1989.

    >>
    >> A little harsh - I think that K&R 1st edition is worth keeping if you
    >> have a copy.

    >
    > Not really smart, either. It may just be a collectors item.I would pay
    > money for an original K&R manual, if only for sentimental reasons.


    US$3.44 plus $3.99 shipping on Ebay. Get 'em while they're hot.

    (No, I'm not selling mine.)

    > For educational purposes, however, I'm with Sprunk.


    Seconded, or maybe thirded. The language has changed a lot
    in the three and a half decades since K&R came off the presses,
    and many code samples in K&R, though exemplary (by definition!)
    at the time would nowadays be considered poor style if not just
    plain broken.

    "Whan that Aprill, with his shoures soote
    The droghte of March hath perced to the roote
    And bathed every veyne in swich licour,
    Of which vertu engendred is the flour ..."

    A classic of English literature, but not much help in learning
    English. Just so with K&R: The language has moved on.

    --
    Eric Sosman
    d
    Eric Sosman, Jul 31, 2012
    #10
  11. Jon Howson

    Tim Rentsch Guest

    Jon Howson <> writes:

    > Hello
    >
    > I was reading thru some (yellowing!) mimeographed introductory notes on C
    > and I found code like the below.
    >
    > main(argc,argv)
    > int argc;
    > char *argv[];
    > {
    > char *malloc();
    > char *strcpy();
    > int puts();
    > void free();
    >
    > char *s;
    > if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
    > strcpy(s,"Hello, ");
    > strcpy(s+7,argv[1]);
    > puts(s);
    > free(s);
    > }else{
    > puts("Program failed, unknown error");
    > }
    > }
    >
    > I'm trying to work out what's happening. At the start of the function, I
    > guess the parameters are being converted into local variables, maybe a
    > way to get pass-by-value in C? [snip]


    Just a few clarifications/additions to points made in other responses.

    1. This style of parameter declaration (dating from more than 30
    years ago, before C was standardized) is labelled 'obsolescent'
    by the Standard. That means it's accepted now but don't count
    on it being so in future revisions of the Standard. (On the other
    hand it has been obsolescent for more than 20 years now.)

    2. The newer (ie, current) style of parameter declarations is called
    "prototypes", as for example

    int main( int argc, char *argv[] ){ ... }

    3. The prototype style of parameter declaration allows (and requires)
    any call after the prototype declaration to have the types of its
    arguments checked against the parameter types given in the prototype.
    In contrast, the old style of parameter declaration does not do
    such type checking (and the Standard says specifically that such
    type checking will not be done).

    4. The argument values are passed the same way in both cases,
    except that, when old-style parameter declarations are used,
    the parameter types are adjusted in some cases to reflect the
    lack of knowledge about the types used. For example, a parameter
    declared as 'char' will be passed as 'int' (or 'unsigned int' in
    some implementations).

    5. Except for the change of types mentioned in (4), how parameters
    are used in function bodies is the same for both old-style and
    the prototype form of parameter declaration.

    6. Most people advise using the prototype declaration style when
    writing any new code. I myself follow that advice in most cases
    (and refrain now from trying to explain any of the exceptions).

    7. For existing code using the older-style (aka "K&R style")
    declarations, the most obvious benefit of changing it over to the
    prototype form is better type checking. There are however two
    gotcha's worth noting. One, the higher level of type checking
    could produce compilation errors even though the code in question
    works fine. (Of course it's also possible the code doesn't work
    fine, and the type checking showed a problem.) Two, because of
    the different rules for how parameter types work, it's possible
    for a change that looks innocuous at face value to turn working
    code into non-working code (and probably also the reverse, but I
    think that's even less likely). So those changes are not ones
    that should be made completely mindlessly and automatically.

    8. (I'm skipping the question about the local function declarations
    for malloc(), etc, because I don't have anything to add about
    that here.)
    Tim Rentsch, Sep 7, 2012
    #11
  12. Tim Rentsch <> writes:
    [...]
    > 3. The prototype style of parameter declaration allows (and requires)
    > any call after the prototype declaration to have the types of its
    > arguments checked against the parameter types given in the prototype.
    > In contrast, the old style of parameter declaration does not do
    > such type checking (and the Standard says specifically that such
    > type checking will not be done).

    [...]

    Checking is not *required*, but an implementation is free to perform it
    anyway.

    For example:

    void func(arg)
    int arg;
    {
    /* ... */
    }

    ...

    func("foo");

    The call has undefined behavior. No diagnostic is required (and I
    don't think I've seen a compiler warn about this), but (a) compilers
    are free to warn about anything they like, and (b) given undefined
    behavior that's detectable at compile time, a compiler is free to
    treat it as a fatal error.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Sep 8, 2012
    #12
  13. Jon Howson

    Tim Rentsch Guest

    Keith Thompson <> writes:

    > Tim Rentsch <> writes:
    > [...]
    >> 3. The prototype style of parameter declaration allows (and requires)
    >> any call after the prototype declaration to have the types of its
    >> arguments checked against the parameter types given in the prototype.
    >> In contrast, the old style of parameter declaration does not do
    >> such type checking (and the Standard says specifically that such
    >> type checking will not be done).

    > [...]
    >
    > Checking is not *required*, but an implementation is free to perform it
    > anyway. [snip example]


    Talking about function calls, 6.5.2.2 p 8 says "the number and
    types of arguments are not compared with those of the parameters
    in a function definition that does not include a function
    prototype declarator."

    You're right that implementations are free to issue any
    diagnostics they want. At the same time, what is in those
    diagnostics cannot depend on the results of comparisons
    that implementations are prohibited from making.
    Tim Rentsch, Sep 8, 2012
    #13
  14. Tim Rentsch <> writes:
    > Keith Thompson <> writes:
    >> Tim Rentsch <> writes:
    >> [...]
    >>> 3. The prototype style of parameter declaration allows (and requires)
    >>> any call after the prototype declaration to have the types of its
    >>> arguments checked against the parameter types given in the prototype.
    >>> In contrast, the old style of parameter declaration does not do
    >>> such type checking (and the Standard says specifically that such
    >>> type checking will not be done).

    >> [...]
    >>
    >> Checking is not *required*, but an implementation is free to perform it
    >> anyway. [snip example]

    >
    > Talking about function calls, 6.5.2.2 p 8 says "the number and
    > types of arguments are not compared with those of the parameters
    > in a function definition that does not include a function
    > prototype declarator."


    The full paragraph is:

    No other conversions are performed implicitly; in particular,
    the number and types of arguments are not compared with those
    of the parameters in a function definition that does not include
    a function prototype declarator.

    > You're right that implementations are free to issue any
    > diagnostics they want. At the same time, what is in those
    > diagnostics cannot depend on the results of comparisons
    > that implementations are prohibited from making.


    My suspicion is that the intent was not to forbid making those
    comparisons, just that any argument conversions should not depend
    on such comparisons. It certainly does *say* that the comparisons
    are not performed, but it's in the context of implicit conversions.

    It doesn't make much sense to forbid a compiler to perform
    comparisons don't necessarily have any effect. And as long as
    old-style function definitions are in the language, it would be nice
    if some compilers could warn about calls with mismatched arguments.

    Off to comp.std.c.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Sep 8, 2012
    #14
  15. On 9/8/12 8:09 AM, Tim Rentsch wrote:
    > Keith Thompson <> writes:
    >
    >> Tim Rentsch <> writes:
    >> [...]
    >>> 3. The prototype style of parameter declaration allows (and requires)
    >>> any call after the prototype declaration to have the types of its
    >>> arguments checked against the parameter types given in the prototype.
    >>> In contrast, the old style of parameter declaration does not do
    >>> such type checking (and the Standard says specifically that such
    >>> type checking will not be done).

    >> [...]
    >>
    >> Checking is not *required*, but an implementation is free to perform it
    >> anyway. [snip example]

    >
    > Talking about function calls, 6.5.2.2 p 8 says "the number and
    > types of arguments are not compared with those of the parameters
    > in a function definition that does not include a function
    > prototype declarator."
    >
    > You're right that implementations are free to issue any
    > diagnostics they want. At the same time, what is in those
    > diagnostics cannot depend on the results of comparisons
    > that implementations are prohibited from making.
    >


    The implementation is not prohibited from making any comparisons that it
    might want to make, and can say what ever it wants to say, as long as it
    prints at least some sort of diagnostic message for a program that
    violates a constraint.

    It is free to emit a message for anything it wants, even for things that
    are "valid code". It is still conforming to issue warnings for things
    like unneeded parenthesis or missing unneeded parenthesis that some
    coding style might suggest be included due to people often getting
    things wrong.

    The resulting program output from the implementation (only required if
    the program violates no constraints) must produce the standard defined
    behavior (for as long as the standard defines the behavior, possibly
    with some implementation defined behavior when that is mandated by the
    standard). Since calling a non-prototyped function with the wrong
    parameter types is not a constraint violation, the compiler can not use
    this as grounds to not produce a resulting program (although when/if the
    call occurs it creates undefined behavior), but it is free to make what
    ever warnings it wants.
    Richard Damon, Sep 8, 2012
    #15
  16. Jon Howson

    Tim Rentsch Guest

    Richard Damon <> writes:

    > On 9/8/12 8:09 AM, Tim Rentsch wrote:
    >> Keith Thompson <> writes:
    >>
    >>> Tim Rentsch <> writes:
    >>> [...]
    >>>> 3. The prototype style of parameter declaration allows (and requires)
    >>>> any call after the prototype declaration to have the types of its
    >>>> arguments checked against the parameter types given in the prototype.
    >>>> In contrast, the old style of parameter declaration does not do
    >>>> such type checking (and the Standard says specifically that such
    >>>> type checking will not be done).
    >>> [...]
    >>>
    >>> Checking is not *required*, but an implementation is free to perform it
    >>> anyway. [snip example]

    >>
    >> Talking about function calls, 6.5.2.2 p 8 says "the number and
    >> types of arguments are not compared with those of the parameters
    >> in a function definition that does not include a function
    >> prototype declarator."
    >>
    >> You're right that implementations are free to issue any
    >> diagnostics they want. At the same time, what is in those
    >> diagnostics cannot depend on the results of comparisons
    >> that implementations are prohibited from making.

    >
    > The implementation is not prohibited from making any comparisons that it
    > might want to make, and can say what ever it wants to say, as long as it
    > prints at least some sort of diagnostic message for a program that
    > violates a constraint. [snip elaboration]


    If you're arguing that the wording of 6.5.2.2 p 8 allows more
    than one interpretation, and that interpretation might not
    disallow such checking, I agree that aspect is debatable.

    If you're arguing that implementations are always free to issue
    diagnostics, regardless of how the restrictions in 6.5.2.2 p 8 are
    worded, I can't agree with that -- the license to issue diagnostics
    does not trump the obligation to meet all other requirements in the
    Standard, whatever they are, for all conforming implementations.
    Tim Rentsch, Dec 17, 2012
    #16
    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. Jon

    app vars and cache vars

    Jon, Dec 14, 2004, in forum: ASP .Net
    Replies:
    3
    Views:
    375
  2. Linuxguy123
    Replies:
    7
    Views:
    655
    Paddy O'Loughlin
    Feb 20, 2009
  3. caccolangrifata
    Replies:
    18
    Views:
    378
    Chris Torek
    Jul 22, 2011
  4. Dirk Einecke

    GET-parameters an local vars

    Dirk Einecke, May 15, 2004, in forum: Ruby
    Replies:
    4
    Views:
    85
    Dirk Einecke
    May 15, 2004
  5. Barry
    Replies:
    9
    Views:
    423
    Ara.T.Howard
    Sep 15, 2005
Loading...

Share This Page