Is there an easier way to work with pointers (sample code included)

Discussion in 'C Programming' started by walter.preuninger@gmail.com, Mar 8, 2006.

  1. Guest

    Is there an easier way to code the cmp procedure without going thru all
    the pointer manipulations?

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

    int cmp(const void *i, const void *j)
    {
    void *p1, *p2;
    char **s1, **s2;
    p1=(void *) i;
    p2=(void *) j;
    s1=(void *) p1;
    s2=(void *) p2;
    return strcmp(*s1,*s2);
    }

    int main(void)
    {
    char string0[]="zebra";
    char string1[]="hello";
    char string2[]="goodbye";

    char *base[3];

    base[0]=(char *)string0;
    base[1]=(char *)string1;
    base[2]=(char *)string2;

    printf("\nbase[0] %s",base[0]);
    printf("\nbase[1] %s",base[1]);
    printf("\nbase[2] %s\n",base[2]);

    qsort(&base,3,sizeof(base[0]),(void *)cmp);

    printf("\nbase[0] %s",base[0]);
    printf("\nbase[1] %s",base[1]);
    printf("\nbase[2] %s\n",base[2]);
    }


    TIA,

    Walter
    , Mar 8, 2006
    #1
    1. Advertising

  2. Al Balmer Guest

    On 8 Mar 2006 10:22:53 -0800, wrote:

    >Is there an easier way to code the cmp procedure without going thru all
    >the pointer manipulations?
    >
    >#include <stdlib.h>
    >#include <string.h>
    >
    >int cmp(const void *i, const void *j)
    >{
    >void *p1, *p2;
    >char **s1, **s2;
    >p1=(void *) i;
    >p2=(void *) j;
    >s1=(void *) p1;
    >s2=(void *) p2;
    >return strcmp(*s1,*s2);
    >}


    int cmp(const void *i, const void *j)
    {
    char *s1 = i;
    char *s2 = p;

    return strcmp(s1, s2);
    }
    Is that what you're trying to do?

    BTW, please properly indent code you post. Your compiler may not care,
    but people reading it do.

    --
    Al Balmer
    Sun City, AZ
    Al Balmer, Mar 8, 2006
    #2
    1. Advertising

  3. wrote:
    > Is there an easier way to code the cmp procedure without going thru all
    > the pointer manipulations?


    In addition to an excessive number of useless casts, you have left out a
    key header.
    Yes, you are trying too hard. Compare the code below to your code,
    which follows it:

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

    int compare_strings(const void *i, const void *j)
    {
    return strcmp(*(char **) i, *(char **) j);
    }

    void showstrs(size_t n, char *s[n])
    {
    size_t i;
    for (i = 0; i < n; i++)
    printf("s[%u] %s\n", (unsigned) i, s);
    }

    int main(void)
    {
    char *base[] = { "zebra", "hello", "goodbye" };
    size_t n = sizeof base / sizeof *base;
    showstrs(n, base);
    putchar('\n');
    qsort(base, n, sizeof *base, compare_strings);
    showstrs(n, base);
    return 0;
    }

    [output]
    s[0] zebra
    s[1] hello
    s[2] goodbye

    s[0] goodbye
    s[1] hello
    s[2] zebra


    [OP's code]

    > #include <stdlib.h>
    > #include <string.h>
    >
    > int cmp(const void *i, const void *j)
    > {
    > void *p1, *p2;
    > char **s1, **s2;
    > p1=(void *) i;
    > p2=(void *) j;
    > s1=(void *) p1;
    > s2=(void *) p2;
    > return strcmp(*s1,*s2);
    > }
    >
    > int main(void)
    > {
    > char string0[]="zebra";
    > char string1[]="hello";
    > char string2[]="goodbye";
    >
    > char *base[3];
    >
    > base[0]=(char *)string0;
    > base[1]=(char *)string1;
    > base[2]=(char *)string2;
    >
    > printf("\nbase[0] %s",base[0]);
    > printf("\nbase[1] %s",base[1]);
    > printf("\nbase[2] %s\n",base[2]);
    >
    > qsort(&base,3,sizeof(base[0]),(void *)cmp);
    >
    > printf("\nbase[0] %s",base[0]);
    > printf("\nbase[1] %s",base[1]);
    > printf("\nbase[2] %s\n",base[2]);
    > }
    >
    >
    > TIA,

    ^^^
    Are you a bill-collector or a script-kiddie?
    Martin Ambuhl, Mar 8, 2006
    #3
  4. In article <>,
    <> wrote:
    >Is there an easier way to code the cmp procedure without going thru all
    >the pointer manipulations?
    >
    >#include <stdlib.h>
    >#include <string.h>
    >
    >int cmp(const void *i, const void *j)
    >{
    >void *p1, *p2;
    >char **s1, **s2;
    >p1=(void *) i;
    >p2=(void *) j;
    >s1=(void *) p1;
    >s2=(void *) p2;
    >return strcmp(*s1,*s2);
    >}

    [... the rest of the code snipped ...]

    As a rule, the use of casts in a code is an indication that
    something is wrong. There are exceptions to this rule, but in
    general a cast should raise a red flag in your mind. Here is
    a modified version of your code without casts. It is shorte
    and should be easier to understand and debug:

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

    int cmp(const void *a, const void *b)
    {
    char * const *aa = a;
    char * const *bb = b;
    return strcmp(*aa, *bb);
    }

    int main(void)
    {
    char string0[] = "zebra";
    char string1[] = "hello";
    char string2[] = "goodbye";
    char *base[3];

    base[0] = string0;
    base[1] = string1;
    base[2] = string2;

    qsort(base, (sizeof base)/(sizeof base[0]), sizeof base[0], cmp);

    printf("base[0] = %s\n",base[0]);
    printf("base[1] = %s\n",base[1]);
    printf("base[2] = %s\n",base[2]);

    return EXIT_SUCCESS;
    }

    --
    Rouben Rostamian
    Rouben Rostamian, Mar 8, 2006
    #4
  5. Flash Gordon Guest

    Al Balmer wrote:
    > On 8 Mar 2006 10:22:53 -0800, wrote:
    >
    >> Is there an easier way to code the cmp procedure without going thru all
    >> the pointer manipulations?
    >>
    >> #include <stdlib.h>
    >> #include <string.h>
    >>
    >> int cmp(const void *i, const void *j)
    >> {
    >> void *p1, *p2;
    >> char **s1, **s2;
    >> p1=(void *) i;
    >> p2=(void *) j;
    >> s1=(void *) p1;
    >> s2=(void *) p2;
    >> return strcmp(*s1,*s2);
    >> }

    >
    > int cmp(const void *i, const void *j)
    > {
    > char *s1 = i;
    > char *s2 = p;


    Don't you mean:
    const char *s1 = i;
    const char *s2 = j;

    > return strcmp(s1, s2);
    > }
    > Is that what you're trying to do?
    >
    > BTW, please properly indent code you post. Your compiler may not care,
    > but people reading it do.


    Definitely.
    --
    Flash Gordon, living in interesting times.
    Web site - http://home.flash-gordon.me.uk/
    comp.lang.c posting guidelines and intro:
    http://clc-wiki.net/wiki/Intro_to_clc
    Flash Gordon, Mar 8, 2006
    #5
  6. Al Balmer wrote:
    [...qsort compare function on string array...]
    > int cmp(const void *i, const void *j)
    > {
    > char *s1 = i;
    > char *s2 = p;
    >
    > return strcmp(s1, s2);
    > }

    [...]

    Any reason you can't simply do:

    int cmp(const void *i, const void *j)
    {
    return strcmp(i,j);
    }

    --
    +-------------------------+--------------------+-----------------------------+
    | Kenneth J. Brody | www.hvcomputer.com | |
    | kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
    +-------------------------+--------------------+-----------------------------+
    Don't e-mail me at: <mailto:>
    Kenneth Brody, Mar 8, 2006
    #6
  7. Malcolm Guest

    <> wrote in message
    news:...
    > Is there an easier way to code the cmp procedure without going thru all
    > the pointer manipulations?
    >
    > #include <stdlib.h>
    > #include <string.h>
    >
    > int cmp(const void *i, const void *j)
    > {
    > void *p1, *p2;
    > char **s1, **s2;
    > p1=(void *) i;
    > p2=(void *) j;
    > s1=(void *) p1;
    > s2=(void *) p2;
    > return strcmp(*s1,*s2);
    > }
    >
    > int main(void)
    > {
    > char string0[]="zebra";
    > char string1[]="hello";
    > char string2[]="goodbye";
    >
    > char *base[3];
    >
    > base[0]=(char *)string0;
    > base[1]=(char *)string1;
    > base[2]=(char *)string2;
    >
    > printf("\nbase[0] %s",base[0]);
    > printf("\nbase[1] %s",base[1]);
    > printf("\nbase[2] %s\n",base[2]);
    >
    > qsort(&base,3,sizeof(base[0]),(void *)cmp);
    >
    > printf("\nbase[0] %s",base[0]);
    > printf("\nbase[1] %s",base[1]);
    > printf("\nbase[2] %s\n",base[2]);
    > }
    >
    >
    > TIA,
    >
    > Walter
    >


    Sorting a plain array of strings is usually pretty pointless.
    Do something like this

    typedef struct
    {
    char name[64];
    char *description;
    /* probably lots of other data associated with animals here */
    } ANIMAL;

    int cmp(const void *e1, const void *e2)
    {
    const ANIMAL *a1 = e1;
    const ANIMAL *a2 = e2;

    return strcmp(al->name, a2->name);
    }

    That's not too burdensome.
    --
    Buy my book 12 Common Atheist Arguments (refuted)
    $1.25 download or $6.90 paper, available www.lulu.com
    Malcolm, Mar 8, 2006
    #7
  8. Default User Guest

    wrote:

    > Is there an easier way to code the cmp procedure without going thru
    > all the pointer manipulations?
    >
    > #include <stdlib.h>
    > #include <string.h>
    >
    > int cmp(const void *i, const void *j)
    > {
    > void *p1, *p2;
    > char **s1, **s2;
    > p1=(void *) i;
    > p2=(void *) j;
    > s1=(void *) p1;
    > s2=(void *) p2;
    > return strcmp(*s1,*s2);
    > }



    In addition to what all the others have said, you should almost NEVER
    cast away const from a pointer. That's a major indicator that there's a
    design flaw.



    Brian
    Default User, Mar 8, 2006
    #8
  9. Micah Cowan Guest

    "Default User" <> writes:

    > wrote:
    >
    > > Is there an easier way to code the cmp procedure without going thru
    > > all the pointer manipulations?
    > >
    > > #include <stdlib.h>
    > > #include <string.h>
    > >
    > > int cmp(const void *i, const void *j)
    > > {
    > > void *p1, *p2;
    > > char **s1, **s2;
    > > p1=(void *) i;
    > > p2=(void *) j;
    > > s1=(void *) p1;
    > > s2=(void *) p2;
    > > return strcmp(*s1,*s2);
    > > }

    >
    >
    > In addition to what all the others have said, you should almost NEVER
    > cast away const from a pointer. That's a major indicator that there's a
    > design flaw.


    The exception would be when you're implementing something like
    strchr(); where you want your parameter list to indicate a guarantee
    that you won't change the string, but you don't want to force the
    caller to make that same guarantee (or do the cast there).
    Micah Cowan, Mar 8, 2006
    #9
  10. , le 08/03/2006 a écrit :
    > Is there an easier way to code the cmp procedure without going thru all
    > the pointer manipulations?


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

    #define AFFICHE printf("\nbase[0] %s""\nbase[1] %s""\nbase[2] %s\n",\
    base[0],\
    base[1],\
    base[2])

    typedef char* chaine;
    typedef int(*f_comp_t)(const void*, const void*);


    int cmp_chaine(const chaine* i, const chaine* j)
    {
    return strcmp(*i,*j);
    }

    int main(void)
    {
    char string0[]="zebra", string1[]="hello", string2[]="goodbye";
    chaine base[] = {string0, string1, string2};
    AFFICHE;
    qsort(base,3,sizeof(chaine),(f_comp_t)cmp_chaine);
    AFFICHE;
    return 0;
    }

    Or:

    /* .... */

    int main(void)
    {
    char string0[]="zebra", string1[]="hello", string2[]="goodbye";
    chaine base[] = {string0, string1, string2};
    f_comp_t cmp = (f_comp_t)cmp_chaine;
    AFFICHE;
    qsort(base,3,sizeof(chaine),cmp);
    AFFICHE;
    return 0;
    }

    --
    Pierre Maurette
    Pierre Maurette, Mar 9, 2006
    #10
  11. Al Balmer Guest

    On Wed, 08 Mar 2006 19:28:13 +0000, Flash Gordon
    <> wrote:

    >Al Balmer wrote:
    >> On 8 Mar 2006 10:22:53 -0800, wrote:
    >>
    >>> Is there an easier way to code the cmp procedure without going thru all
    >>> the pointer manipulations?
    >>>
    >>> #include <stdlib.h>
    >>> #include <string.h>
    >>>
    >>> int cmp(const void *i, const void *j)
    >>> {
    >>> void *p1, *p2;
    >>> char **s1, **s2;
    >>> p1=(void *) i;
    >>> p2=(void *) j;
    >>> s1=(void *) p1;
    >>> s2=(void *) p2;
    >>> return strcmp(*s1,*s2);
    >>> }

    >>
    >> int cmp(const void *i, const void *j)
    >> {
    >> char *s1 = i;
    >> char *s2 = p;

    >
    >Don't you mean:
    > const char *s1 = i;
    > const char *s2 = j;
    >

    Yup. <g> You can tell how often I use const. The compiler would have
    reminded me.

    >> return strcmp(s1, s2);
    >> }
    >> Is that what you're trying to do?
    >>
    >> BTW, please properly indent code you post. Your compiler may not care,
    >> but people reading it do.

    >
    >Definitely.


    --
    Al Balmer
    Sun City, AZ
    Al Balmer, Mar 9, 2006
    #11
  12. Default User Guest

    Micah Cowan wrote:

    > "Default User" <> writes:


    > > In addition to what all the others have said, you should almost
    > > NEVER cast away const from a pointer. That's a major indicator that
    > > there's a design flaw.

    >
    > The exception would be when you're implementing something like
    > strchr(); where you want your parameter list to indicate a guarantee
    > that you won't change the string, but you don't want to force the
    > caller to make that same guarantee (or do the cast there).


    I guess I'm not following. You can always pass a non-const pointer
    through a const parameter. The caller wouldn't need to cast, nor would
    the implementer.

    In the previous code, the OP had const parameters and then blithely
    cast the const away inside the function.



    Brian
    Default User, Mar 9, 2006
    #12
  13. Default User said:

    > Micah Cowan wrote:
    >
    >> "Default User" <> writes:

    >
    >> > In addition to what all the others have said, you should almost
    >> > NEVER cast away const from a pointer. That's a major indicator that
    >> > there's a design flaw.

    >>
    >> The exception would be when you're implementing something like
    >> strchr(); where you want your parameter list to indicate a guarantee
    >> that you won't change the string, but you don't want to force the
    >> caller to make that same guarantee (or do the cast there).

    >
    > I guess I'm not following. You can always pass a non-const pointer
    > through a const parameter. The caller wouldn't need to cast, nor would
    > the implementer.


    No, think about strchr for a second (and don't worry too much about whether
    I've got the implementation right - focus on the types):

    char *strchr(const char *s, int ch)
    {
    char *p = (char *)s; /* either you cast it here... */
    while(*p != '\0' && *p != ch)
    {
    ++p;
    }
    if(*p == '\0')
    {
    p = NULL;
    }
    return p;
    }

    char *strchr(const char *s, int ch)
    {
    while(*s != '\0' && *s != ch)
    {
    ++s;
    }
    if(*s == '\0')
    {
    s = NULL;
    }
    return (char *)s; /*... or you cast it here */
    }

    > In the previous code, the OP had const parameters and then blithely
    > cast the const away inside the function.


    Sure, and your point was perfectly valid. I think we're moving on a bit from
    there now.


    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
    Richard Heathfield, Mar 9, 2006
    #13
  14. pete Guest

    wrote:
    >
    > Is there an easier way to code the cmp procedure without going thru all
    > the pointer manipulations?
    >
    > #include <stdlib.h>
    > #include <string.h>
    >
    > int cmp(const void *i, const void *j)
    > {
    > void *p1, *p2;
    > char **s1, **s2;
    > p1=(void *) i;
    > p2=(void *) j;
    > s1=(void *) p1;
    > s2=(void *) p2;
    > return strcmp(*s1,*s2);
    > }
    >
    > int main(void)
    > {
    > char string0[]="zebra";
    > char string1[]="hello";
    > char string2[]="goodbye";
    >
    > char *base[3];
    >
    > base[0]=(char *)string0;
    > base[1]=(char *)string1;
    > base[2]=(char *)string2;
    >
    > printf("\nbase[0] %s",base[0]);
    > printf("\nbase[1] %s",base[1]);
    > printf("\nbase[2] %s\n",base[2]);
    >
    > qsort(&base,3,sizeof(base[0]),(void *)cmp);
    >
    > printf("\nbase[0] %s",base[0]);
    > printf("\nbase[1] %s",base[1]);
    > printf("\nbase[2] %s\n",base[2]);
    > }


    /* BEGIN new.c */

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

    int cmp(const void *i, const void *j)
    {
    return strcmp(i, j);
    }

    int main(void)
    {
    char string0[] = "zebra";
    char string1[] = "hello";
    char string2[] = "goodbye";
    char *base[3];

    base[0] = string0;
    base[1] = string1;
    base[2] = string2;
    printf("base[0] %s\n", base[0]);
    printf("base[1] %s\n", base[1]);
    printf("base[2] %s\n", base[2]);
    putchar('\n');
    qsort(base, sizeof base / sizeof*base, sizeof*base, cmp);
    printf("base[0] %s\n",base[0]);
    printf("base[1] %s\n",base[1]);
    printf("base[2] %s\n",base[2]);
    return 0;
    }

    /* END new.c */


    --
    pete
    pete, Mar 9, 2006
    #14
  15. wrote:
    > Is there an easier way to code the cmp procedure without going thru all
    > the pointer manipulations?
    >
    > #include <stdlib.h>
    > #include <string.h>
    >
    > int cmp(const void *i, const void *j)
    > {
    > void *p1, *p2;
    > char **s1, **s2;
    > p1=(void *) i;
    > p2=(void *) j;
    > s1=(void *) p1;
    > s2=(void *) p2;
    > return strcmp(*s1,*s2);
    > }
    >
    > int main(void)
    > {
    > char string0[]="zebra";
    > char string1[]="hello";
    > char string2[]="goodbye";
    >
    > char *base[3];
    >
    > base[0]=(char *)string0;
    > base[1]=(char *)string1;
    > base[2]=(char *)string2;
    >
    > printf("\nbase[0] %s",base[0]);
    > printf("\nbase[1] %s",base[1]);
    > printf("\nbase[2] %s\n",base[2]);
    >
    > qsort(&base,3,sizeof(base[0]),(void *)cmp);
    >
    > printf("\nbase[0] %s",base[0]);
    > printf("\nbase[1] %s",base[1]);
    > printf("\nbase[2] %s\n",base[2]);
    > }


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

    #define COUNT 3
    #define MAX_LEN 32

    char strings[COUNT][MAX_LEN] = {"zebra", "hello", "goodbye"};

    void show(char strings[][MAX_LEN])
    {
    int k;

    for (k = 0; k < COUNT; k++) printf("%s, ", strings[k]);
    printf("\n");
    }


    int main(void)
    {
    show(strings);
    qsort(strings, COUNT, MAX_LEN,
    (int (*)(const void *, const void *)) strncmp);
    show(strings);
    return 0;
    }


    August

    --
    I am the "ILOVEGNU" signature virus. Just copy me to your
    signature. This email was infected under the terms of the GNU
    General Public License.
    August Karlstrom, Mar 9, 2006
    #15
  16. pete wrote:
    >
    > /* BEGIN new.c */
    >
    > #include <stdlib.h>
    > #include <string.h>
    > #include <stdio.h>
    >
    > int cmp(const void *i, const void *j)
    > {
    > return strcmp(i, j);
    > }
    >
    > int main(void)
    > {
    > char string0[] = "zebra";
    > char string1[] = "hello";
    > char string2[] = "goodbye";
    > char *base[3];
    >
    > base[0] = string0;
    > base[1] = string1;
    > base[2] = string2;
    > printf("base[0] %s\n", base[0]);
    > printf("base[1] %s\n", base[1]);
    > printf("base[2] %s\n", base[2]);
    > putchar('\n');
    > qsort(base, sizeof base / sizeof*base, sizeof*base, cmp);
    > printf("base[0] %s\n",base[0]);
    > printf("base[1] %s\n",base[1]);
    > printf("base[2] %s\n",base[2]);
    > return 0;
    > }
    >
    > /* END new.c */


    One thing that AFAIK hasn't been pointed out in the thread is there is
    no language guarantee that this will sort the strings into the order:

    goodbye
    hello
    zebra

    --
    Peter
    Peter Nilsson, Mar 9, 2006
    #16
  17. Default User Guest

    Richard Heathfield wrote:

    > Default User said:


    > > I guess I'm not following. You can always pass a non-const pointer
    > > through a const parameter. The caller wouldn't need to cast, nor
    > > would the implementer.


    > return (char *)s; /*... or you cast it here */


    Bah, I didn't think it through to the return. Never mind.




    Brian
    Default User, Mar 9, 2006
    #17
  18. Malcolm Guest

    "Richard Heathfield" <> wrote
    > No, think about strchr for a second (and don't worry too much about
    > whether
    > I've got the implementation right - focus on the types):
    >
    > char *strchr(const char *s, int ch)
    >

    This is where the hacked in status of const as a late addition to the
    language beocmes apparent.
    const should taint everything it points to, and all pointers derived from
    it. But that would be a major reengineering exercise.
    --
    Buy my book 12 Common Atheist Arguments (refuted)
    $1.25 download or $6.90 paper, available www.lulu.com
    Malcolm, Mar 9, 2006
    #18
  19. Jordan Abel Guest

    On 2006-03-09, Malcolm <> wrote:
    > "Richard Heathfield" <> wrote
    >> No, think about strchr for a second (and don't worry too much about
    >> whether
    >> I've got the implementation right - focus on the types):
    >>
    >> char *strchr(const char *s, int ch)
    >>

    > This is where the hacked in status of const as a late addition to the
    > language beocmes apparent.
    > const should taint everything it points to, and all pointers derived from
    > it. But that would be a major reengineering exercise.


    typeof would allow this, after a fashion:

    #define strchr(x,c) (typeof x)strchr(x,c)
    Jordan Abel, Mar 9, 2006
    #19
  20. On 2006-03-09, pete <> wrote:
    > [...]
    > int cmp(const void *i, const void *j)
    > {
    > return strcmp(i, j);
    > }
    >
    > [...]
    > char *base[3];
    > base[0] = string0;
    > base[1] = string1;
    > base[2] = string2;
    > [...]
    > qsort(base, sizeof base / sizeof*base, sizeof*base, cmp);


    Several people have posted code like this now, all apparently without
    noticing that it's wrong.

    base is an array of pointers to char. This is passed to qsort. qsort
    calls cmp with two pointers, each of which points *to an element of
    the array*.

    Those void *'s in the argument of cmp are "really" char **, not char *,
    and passing them to strcmp produces undefined behavior.

    It so happens that you (and the OP) have laid out the program so that
    on most platforms strcmp will reach a zero byte somewhere before
    faulting, and the conventional layout of automatic storage is such
    that strcmp interpreting the addresses as strings will generate the
    expected output sort order, so just running the program once to check
    it is misleading.

    cmp needs to be

    int cmp(const void *i0, const void *j0) {
    const char *const *i = i0;
    const char *const *j = j0;
    return strcmp(*i, *j);
    }

    HTH.

    --
    - David A. Holland
    (the above address works if unscrambled but isn't checked often)
    David Holland, Mar 9, 2006
    #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. Norman Ackroyd
    Replies:
    1
    Views:
    526
  2. MetalOne
    Replies:
    4
    Views:
    323
    MetalOne
    Feb 22, 2004
  3. Jim S

    There must be an easier way

    Jim S, Feb 14, 2007, in forum: HTML
    Replies:
    3
    Views:
    392
    John Hosking
    Feb 15, 2007
  4. John Henry
    Replies:
    19
    Views:
    387
    Dennis Lee Bieber
    Dec 1, 2006
  5. Michael W. Ryder
    Replies:
    6
    Views:
    107
    Michael W. Ryder
    Jan 21, 2008
Loading...

Share This Page