adapting getline

G

Guest

The -ansi switch makes gcc conform to an obsolete version of the ISO C
standard,

s/an obsolete/a still widly used/

which Sossman, Heathfield and some other posters here prefer
because they want C to remain as a museum piece rather than a living
language.

Either omit -ansi altogether (to get the default GNU C dialect), or use
-std=c99 (to get the /current/ version of the ISO C Standard).

Sadly C99 is *still* not widely implemented and if you want maximal
portability (which some people do) the your best bet is C89/C90.

For instance, gcc does not actually implement full c99. So -std=99
actually implements *most* of c99. If this is good enough for you
then
go with it.

Twink has his own troll-like axe to grind. Review his posts.
 
K

Keith Thompson

Sadly C99 is *still* not widely implemented and if you want maximal
portability (which some people do) the your best bet is C89/C90.
[...]

Actually, your best bet is the intersection of C89/C90 and C99. For
example, avoid using C99-specific keywords as identifiers, and don't
rely on implicit int or implicit function declarations.
 
C

CBFalconer

luserXtrog said:
.... snip ...

OTOH, you might have better luck leaving off the -ansi part.
I have a suspicion that mingw had to deviate quite a bit
in order to interface with m$windows.
The -O2 was the important part.

That may apply to compiling some things, but not this getline.
Leaving -ansi -pedantic in gcc command lines will familiarize you
with what is non-standard C, because you will usually get
complaints.
 
C

CBFalconer

Richard said:
CBFalconer said:
.... snip ...


Check out the original message (to which you replied), in which the
OP writes a getline routine that is almost a direct quote from P69
of K&R2. Furthermore, almost an hour BEFORE you posted your comment
about size_t, the OP had written that he took it from the clc wiki
solutions to K&R2 exercises (some of which observe the self-imposed
constraint that they don't use C features not yet introduced at the
point in K&R2 where the exercise appears). And OVER an hour before
you posted your comment about size_t, the OP pointed out that he is
following the development in K&R.

I notice that you are unable to point out any reference to K&R in
the message to which I replied. So I suggest again, cease this
silly meandering and misuse of the newsgroup.
By "grow up", do you mean "stop finding all my mistakes"?

No, I mean cease this silly prating. Act more like an adult. You
are not old enough to indulge in second childhood.
 
C

CBFalconer

Franken said:
In Dread Ink, the Grave Hand of Eric Sosman Did Inscribe:
Franken said:
I like good ascii art, [...]

Then take your enthusiasms to alt.ascii-art, please. Keep
flogging them here, and I for one will give you no more help.

I don't need your help, Eric.

Chew on my too-long sig:

You have an extremely poor attitude. PLONK.
 
A

Antoninus Twink

Leaving -ansi -pedantic in gcc command lines will familiarize you
with what is non-standard C, because you will usually get
complaints.

In this case, the complaint was about line comments (aka // comments).

They have been perfectly Standard for ten years now.

Just because you want to live in the dark ages and adhere to a version
of the Standard that was issued when the Berlin Wall was still standing,
there's no reason to inflict your bloody-mindedness on everyone else.

Idiot.
 
A

Antoninus Twink

You are not old enough to indulge in second childhood.

Unlike you, of course.

Are you back in diapers these days, or does the nurse have to change
your sheets every morning?
 
K

kid joe

Unlike you, of course.

Are you back in diapers these days, or does the nurse have to change
your sheets every morning?

Hi Antoninus,

My grandfather was incontinent before he died, and I don't think it's a
laughing matter.

Cheers,
Joe


--

...................... o _______________ _,
` Good Evening! , /\_ _| | .-'_|
`................, _\__`[_______________| _| (_|
] [ \, ][ ][ (_|
 
K

Keith Thompson

kid joe said:
On Tue, 21 Apr 2009 22:09:26 +0000, Antoninus Twink wrote: [the usual]
Hi Antoninus,

My grandfather was incontinent before he died, and I don't think it's a
laughing matter.

Use a killfile, and you'll never have to see anything he posts.

Or at least don't post followups, so the rest of us don't have to see
it. In other words, please don't feed the troll.
--

...................... o _______________ _,
` Good Evening! , /\_ _| | .-'_|
`................, _\__`[_______________| _| (_|
] [ \, ][ ][ (_|

If at all possible, you need a space after the "--" delimiter, so it's
"-- " rather than "--". I understand that some Usenet clients make
this difficult or impossible.

You might consider dropping the blank line at the top of your
signature.

These are both minor points.
 
K

kid joe

Use a killfile, and you'll never have to see anything he posts.

Or at least don't post followups, so the rest of us don't have to see
it. In other words, please don't feed the troll.

Hi Keith,

I've learned alot from Antoninus' posts, mostly about offtopic things
though... Its a pity he sometimes posts negative and provocative messages.
--

...................... o _______________ _,
` Good Evening! , /\_ _| | .-'_|
`................, _\__`[_______________| _| (_|
] [ \, ][ ][ (_|

If at all possible, you need a space after the "--" delimiter, so it's
"-- " rather than "--". I understand that some Usenet clients make
this difficult or impossible.

You might consider dropping the blank line at the top of your
signature.

These are both minor points.

I believe I have it fixed now.

Cheers,
Joe
 
K

Keith Thompson

kid joe said:
I've learned alot from Antoninus' posts, mostly about offtopic things
though... Its a pity he sometimes posts negative and provocative messages.

Personally, I've found that nothing he posts is worth having to read
the negative stuff. YMMV. But in any case, please don't quote the
negative stuff and make the rest of us read it.

[regarding your signature ...]
I believe I have it fixed now.


--
...................... o _______________ _,
` Good Morning! , /\_ _| | .-'_|
`................, _\__`[_______________| _| (_|
] [ \, ][ ][ (_|

Very nearly. You still have one or two extraneous blank lines at the
bottom.

Thank you for your efforts.
 
F

Franken Sense

In Dread Ink, the Grave Hand of CBFalconer Did Inscribe:
Franken said:
In Dread Ink, the Grave Hand of Eric Sosman Did Inscribe:
Franken Sense wrote:

I like good ascii art, [...]

Then take your enthusiasms to alt.ascii-art, please. Keep
flogging them here, and I for one will give you no more help.

I don't need your help, Eric.

Chew on my too-long sig:

You have an extremely poor attitude. PLONK.

This reminds me of the last time my mom tried to spank me.
--
Frank

Mistakes are a part of being human. Appreciate your mistakes for what they
are: precious life lessons that can only be learned the hard way. Unless
it's a fatal mistake, which, at least, others can learn from.
~~ Al Franken,
 
F

Franken Sense

In Dread Ink, the Grave Hand of Eric Sosman Did Inscribe:
Franken said:
In Dread Ink, the Grave Hand of Eric Sosman Did Inscribe:
Franken Sense wrote:
I like good ascii art, [...]
Then take your enthusiasms to alt.ascii-art, please.
Keep flogging them here, and I for one will give you no
more help.

I don't need your help, Eric.

Then I shan't waste time offering it. Good-bye.

I didn't mean for us to get off on the wrong foot, and I doubt you did too.
You informed much of my early study with C.

You may have said "Such enthusiams find a better treatment ... ."

Anyways, if Eric doesn't want to have fun with C, then he makes his
decisions. Looking for criticisms with this:

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

#define text_length 8192
#define my_file "text42.txt"

int getline(FILE *, char *, int );
struct tnode *add_to_tree( struct tnode *, char * );
void print_tree( struct tnode * );
struct tnode *tree_alloc( void );
char *dupstr( char * );

struct tnode {
char *text;
int count;
unsigned long lineNumber;
struct tnode *left;
struct tnode *right;
};

int main( void ) {
struct tnode *root_node;
char text[text_length];
FILE *fp;
int len;

unsigned long lineNumber = 0;
root_node = NULL;

if ((fp = fopen(my_file, "r")) == NULL ) {
fprintf(stderr, "can't open file\n");
exit(EXIT_FAILURE);
}



while((len = getline(fp, text, text_length)) > 0)
{
++ lineNumber;
printf("%lu, %s", lineNumber, text);
root_node = add_to_tree( root_node, text);
}

print_tree( root_node );
return 0;
}


/* adds the text onto the tree */
struct tnode *add_to_tree( struct tnode *tree, char *pc )
{
int match;

if( tree == NULL )
{
tree = tree_alloc();
tree->text = dupstr( pc );
tree->count = 1;
tree->left = tree->right = NULL;
}
else if( 0 == (match = strcmp( pc, tree->text )) )
{
++tree->count;
}
else if( match > 0 )
{
tree->right = add_to_tree( tree->right, pc );
}
else if( match < 0 )
{
tree->left = add_to_tree( tree->left, pc );
}

return tree;

}


/* prints the tree */
void print_tree( struct tnode *tree )
{
if ( tree )
{
print_tree(tree->left);
printf("%30s -- %4d\n", tree->text, tree->count);
print_tree(tree->right);
}

}

/* allocate memory for a node */
struct tnode *tree_alloc( void )
{
return malloc( sizeof( struct tnode ) );
}

char *dupstr( char *pc )
{
char *pc2;

pc2 = malloc( strlen(pc) + 1 );

if( pc )
{
strcpy( pc2, pc );
}

return pc2;
}

/* getline: get line into s, return length */
int getline(FILE *fp, char *s, int lim)
{
char *p;
int c;


p = s;
while (--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
*p++ = c;
if (c == '\n')
*p++ = c;
*p = '\0';
return (p - s);
}

// gcc arnold4.c -Wall -o out

Cheers,
 
B

Barry Schwarz

On Wed, 22 Apr 2009 22:38:48 -0700, Franken Sense

snip
decisions. Looking for criticisms with this:

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

#define text_length 8192
#define my_file "text42.txt"

int getline(FILE *, char *, int );
struct tnode *add_to_tree( struct tnode *, char * );
void print_tree( struct tnode * );
struct tnode *tree_alloc( void );
char *dupstr( char * );

struct tnode {
char *text;
int count;
unsigned long lineNumber;
struct tnode *left;
struct tnode *right;
};

It would make your code easier to read if you would indent.
int main( void ) {
struct tnode *root_node;
char text[text_length];
FILE *fp;
int len;

unsigned long lineNumber = 0;
root_node = NULL;

if ((fp = fopen(my_file, "r")) == NULL ) {
fprintf(stderr, "can't open file\n");
exit(EXIT_FAILURE);
}

But please do so in a consistent fashion.
while((len = getline(fp, text, text_length)) > 0)
{
++ lineNumber;
printf("%lu, %s", lineNumber, text);

getline terminates properly if the input is too long but you do not
inform the user of the condition.

If the input did not fit in text, there will be no new line to
separate this output from the next one.
root_node = add_to_tree( root_node, text);
}

print_tree( root_node );
return 0;
}


/* adds the text onto the tree */
struct tnode *add_to_tree( struct tnode *tree, char *pc )
{
int match;

if( tree == NULL )
{
tree = tree_alloc();

tree_alloc can return NULL.
tree->text = dupstr( pc );

tree could be NULL.

dupstr can return NULL also.
tree->count = 1;
tree->left = tree->right = NULL;
}
else if( 0 == (match = strcmp( pc, tree->text )) )

tree->text could be NULL.
{
++tree->count;
}
else if( match > 0 )
{
tree->right = add_to_tree( tree->right, pc );
}
else if( match < 0 )
{
tree->left = add_to_tree( tree->left, pc );
}

return tree;

}


/* prints the tree */
void print_tree( struct tnode *tree )
{
if ( tree )
{
print_tree(tree->left);
printf("%30s -- %4d\n", tree->text, tree->count);

tree->text could be NULL.

If the string is less than 30 characters, you will double space your
output. If more, then single space.
print_tree(tree->right);
}

}

/* allocate memory for a node */
struct tnode *tree_alloc( void )
{
return malloc( sizeof( struct tnode ) );
}

char *dupstr( char *pc )
{
char *pc2;

pc2 = malloc( strlen(pc) + 1 );

if( pc )

pc can never be NULL; I assume you meant pc2 here.
{
strcpy( pc2, pc );
}

return pc2;
}

/* getline: get line into s, return length */
int getline(FILE *fp, char *s, int lim)
{
char *p;
int c;


p = s;
while (--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
*p++ = c;
if (c == '\n')

Why are some parenthetical expressions adjacent to the parentheses and
others separated by a space?
*p++ = c;
*p = '\0';
return (p - s);

These parentheses are superfluous.
 
K

Keith Thompson

Franken Sense said:
I assume the NULL happens when dupstr can't malloc, but I don't see what I
can do about it.
[...]

Yes, there are no really good responses to running out of memory. In
some cases, you might be able to abort just the current action and let
the program continue running, particularly in interactive programs.
In other cases, it might even make sense to fall back to a less
memory-intensive algorithm (though typically if you have such an
algorithm you'll just use it in the first place).

But at least you can abort the program with an error message. That's
almost always better than ignoring the error and trying to continue.

ptr = malloc(count * sizeof *ptr);
if (ptr == NULL) {
fputs("Malloc failed", stderr);
exit(EXIT_FAILURE);
}
 
B

Barry Schwarz

In Dread Ink, the Grave Hand of Barry Schwarz Did Inscribe:
snip


I'm counting on tree being NULL once. I think this addresses the
tree_alloc:
if( tree == NULL )
{
tree = tree_alloc();
if (tree_alloc != NULL)

I'm pretty sure you meant tree here, not tree_alloc.
{
tree->text = dupstr( pc );
tree->count = 1;
tree->left = tree->right = NULL;
}

I assume the NULL happens when dupstr can't malloc, but I don't see what I
can do about it.

You can test for it before you attempt to dereference it. Or you
could test for it at allocation time and set tree_text to point to a
string literal, something like "***dupstr failed when trying to
allocate space for this node's text***\n"

snip
Thanks for your comments, Barry. Here's the latest version:
snip

/* getline: get line into s, return length */
int getline(FILE *fp, char *s, int lim)
{
char *p;
int c;
p = s;
while (--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
*p++ = c;
if (c == '\n')
*p++ = c;
*p = '\0';
return p - s;
}

// gcc arnold5.c -Wall -o out

I have at least one question here. As I was cleaning up getline, I
realized that I don't know how to space it because I don't know what's
going on.

Is this equivalent to the above:

/* getline: get line into s, return length */
int getline(FILE *fp, char *s, int lim)
{
char *p;
int c;
p = s;
while (--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
{
*p++ = c;
}

The only difference I see is the opening and closing braces above and
below so I think they are equivalent.
if (c == '\n')
{
*p++ = c;
}
*p = '\0';
return p - s;
}

My preferred spacing style is

int getline(...)
{
char ...;
int ...;
p = s;
while (...)
*p++ = c;
if (...)
*p++ = c;
*p = '\0';
return ...;
}

Some here suggest always using braces for the range of an if/while/for
and I don't object as long as it is consistent.
 
F

Franken Sense

In Dread Ink, the Grave Hand of Barry Schwarz Did Inscribe:
On Wed, 22 Apr 2009 22:38:48 -0700, Franken Sense

getline terminates properly if the input is too long but you do not
inform the user of the condition.

I'm counting on the line being less than 8192 characters, and I'm the user.
If the input did not fit in text, there will be no new line to
separate this output from the next one.

This output is only temporary. Once I really think I've shaken the bugs
out, I'll remove this.
tree_alloc can return NULL.
tree could be NULL.
dupstr can return NULL also.
tree->text could be NULL.

I'm counting on tree being NULL once. I think this addresses the
tree_alloc:
if( tree == NULL )
{
tree = tree_alloc();
if (tree_alloc != NULL)
{
tree->text = dupstr( pc );
tree->count = 1;
tree->left = tree->right = NULL;
}

I assume the NULL happens when dupstr can't malloc, but I don't see what I
can do about it.
If the string is less than 30 characters, you will double space your
output. If more, then single space.

I changed the format specifier to %s, and I think it looks better.
pc can never be NULL; I assume you meant pc2 here.

I think this looks right now:

char *dupstr( char *pc )
{
char *pc2;
pc2 = malloc( strlen(pc) + 1 );
if( pc2 )
{
strcpy( pc2, pc );
}
return pc2;
}
These parentheses are superfluous.

I just realized that this returns the length.

Thanks for your comments, Barry. Here's the latest version:

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

#define text_length 8192
#define my_file "text42.txt"

int getline(FILE *, char *, int );
struct tnode *add_to_tree( struct tnode *, char * );
void print_tree( struct tnode * );
struct tnode *tree_alloc( void );
char *dupstr( char * );

struct tnode
{
char *text;
int count;
unsigned long lineNumber;
struct tnode *left;
struct tnode *right;
};

int main( void )
{
struct tnode *root_node;
char text[ text_length ];
FILE *fp;
int len;

unsigned long lineNumber = 0;
root_node = NULL;

if ((fp = fopen(my_file, "r")) == NULL )
{
fprintf(stderr, "can't open file\n");
exit(EXIT_FAILURE);
}

while((len = getline(fp, text, text_length)) > 0)
{
++ lineNumber;
printf("%lu, %s", lineNumber, text);
root_node = add_to_tree( root_node, text);
}

print_tree( root_node );
return 0;
}


/* adds the text onto the tree */
struct tnode *add_to_tree( struct tnode *tree, char *pc )
{
int match;
if( tree == NULL )
{
tree = tree_alloc();
if (tree_alloc != NULL)
{
tree->text = dupstr( pc );
tree->count = 1;
tree->left = tree->right = NULL;
}
}
else if( 0 == (match = strcmp( pc, tree->text )) )
{
++tree->count;
}
else if( match > 0 )
{
tree->right = add_to_tree( tree->right, pc );
}
else if( match < 0 )
{
tree->left = add_to_tree( tree->left, pc );
}
return tree;
}


/* prints the tree */
void print_tree( struct tnode *tree )
{
if ( tree != NULL)
{
print_tree(tree->left);
printf("%s -- %4d\n", tree->text, tree->count);
print_tree(tree->right);
}
}

/* allocate memory for a node */
struct tnode *tree_alloc( void )
{
return malloc( sizeof( struct tnode ) );
}

char *dupstr( char *pc )
{
char *pc2;
pc2 = malloc( strlen(pc) + 1 );
if( pc2 )
{
strcpy( pc2, pc );
}
return pc2;
}

/* getline: get line into s, return length */
int getline(FILE *fp, char *s, int lim)
{
char *p;
int c;
p = s;
while (--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
*p++ = c;
if (c == '\n')
*p++ = c;
*p = '\0';
return p - s;
}

// gcc arnold5.c -Wall -o out

I have at least one question here. As I was cleaning up getline, I
realized that I don't know how to space it because I don't know what's
going on.

Is this equivalent to the above:

/* getline: get line into s, return length */
int getline(FILE *fp, char *s, int lim)
{
char *p;
int c;
p = s;
while (--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
{
*p++ = c;
}
if (c == '\n')
{
*p++ = c;
}
*p = '\0';
return p - s;
}

Eighty degrees and 18 holes of golf of today and a steak waiting for me in
the kitchen. Life is good.
 
K

Keith Thompson

Franken Sense said:
There's two mallocs that could fail.

char *dupstr( char *pc )
{
char *pc2;
pc2 = malloc( strlen(pc) + 1 );
if (pc2 == NULL)
{
fputs("Malloc failed", stderr);
exit(EXIT_FAILURE);
}
if( pc2 )

This test is redundant; if you reach this point, you know that pc2 is
non-null.
{
strcpy( pc2, pc );
}
return pc2;
}

How do I unfurl this, so as to do likewise?

/* allocate memory for a node */
struct tnode *tree_alloc( void )
{
return malloc( sizeof( struct tnode ) );
}

I've been able to create trees big enough to make this a serious issue.

struct tnode *tree_alloc( void )
{
struct tnode *result = malloc(sizeof *result);
if (result == NULL) {
fputs("Malloc failed", stderr);
exit(EXIT_FAILURE);
}
return result;
}

Or tree_alloc could be left as it is, and the caller could be given
the responsibility of handling failures; likewise for dupstr. If
these functions are only ever going to be used as part of this
program, and your only strategy for handling allocation failures is to
abort immediately, then having the exit in the functions is ok, at
least for now. If they're going to become more general-purpose
library routines, though, they should return an error indication (such
as a null pointer) to the caller, and let the caller handle it.

Earlier I mentioned a few things that can be done in response to an
out-of-memory failure: abort immediately, abort just this part of the
program, fall back to a less memory-intensive algorithm. I forgot
another case: clean up as much as possible and *then* abort. For
example, if your program is an editor, you might try to ensure that
all the user's data is written to disk before aborting.

Bottom line: error handling is hard.
 
F

Franken Sense

In Dread Ink, the Grave Hand of Barry Schwarz Did Inscribe:
On Thu, 23 Apr 2009 18:31:27 -0700, Franken Sense


I'm pretty sure you meant tree here, not tree_alloc.
Thx.
You can test for it before you attempt to dereference it. Or you
could test for it at allocation time and set tree_text to point to a
string literal, something like "***dupstr failed when trying to
allocate space for this node's text***\n"

I think I've got appropriate events now for bad mallocs.
The only difference I see is the opening and closing braces above and
below so I think they are equivalent.

So there is one statement in that while loop?
My preferred spacing style is

int getline(...)
{
char ...;
int ...;
p = s;
while (...)
*p++ = c;
if (...)
*p++ = c;
*p = '\0';
return ...;
}

Some here suggest always using braces for the range of an if/while/for
and I don't object as long as it is consistent.

Yeah, I think I look "poofy" with whitespace after a paren. Can you talk
through the control, because I have clearly not understood it for the
duration of the thread?

// arnold5.c text input using binary tree

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

#define text_length 8192
#define my_file "text42.txt"

int getline(FILE *, char *, int );
struct tnode *add_to_tree( struct tnode *, char * );
void print_tree( struct tnode * );
struct tnode *tree_alloc( void );
char *dupstr( char * );

struct tnode
{
char *text;
int count;
unsigned long lineNumber;
struct tnode *left;
struct tnode *right;
};

int main( void )
{
struct tnode *root_node;
char text[ text_length ];
FILE *fp;
int len;

unsigned long lineNumber = 0;
root_node = NULL;

if ((fp = fopen(my_file, "r")) == NULL )
{
fprintf(stderr, "can't open file\n");
exit(EXIT_FAILURE);
}

while((len = getline(fp, text, text_length)) > 0)
{
++ lineNumber;
printf("%lu, %s", lineNumber, text);
root_node = add_to_tree( root_node, text);
}

print_tree( root_node );
return 0;
}


/* adds the text onto the tree */
struct tnode *add_to_tree( struct tnode *tree, char *pc )
{
int match;
if( tree == NULL )
{
tree = tree_alloc();
if (tree != NULL)
{
tree->text = dupstr( pc );
tree->count = 1;
tree->left = tree->right = NULL;
}
}
else if( 0 == (match = strcmp( pc, tree->text )) )
{
++tree->count;
}
else if( match > 0 )
{
tree->right = add_to_tree( tree->right, pc );
}
else if( match < 0 )
{
tree->left = add_to_tree( tree->left, pc );
}
return tree;
}


/* prints the tree */
void print_tree( struct tnode *tree )
{
if ( tree != NULL)
{
print_tree(tree->left);
printf("%s -- %4d\n", tree->text, tree->count);
print_tree(tree->right);
}
}

/* allocate memory for a node */
struct tnode *tree_alloc( void )
{
return malloc( sizeof( struct tnode ) );
}

char *dupstr( char *pc )
{
char *pc2;
pc2 = malloc( strlen(pc) + 1 );
if (pc2 == NULL)
{
fputs("Malloc failed", stderr);
exit(EXIT_FAILURE);
}
if( pc2 )
{
strcpy( pc2, pc );
}
return pc2;
}

/* getline: get line into s, return length */
int getline(FILE *fp, char *s, int lim)
{
char *p;
int c;
p = s;
while (--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
{
*p++ = c;
}
if (c == '\n')
{
*p++ = c;
}
*p = '\0';
return p - s;
}

// gcc arnold5.c -Wall -o out


--
Frank

The biases the media has are much bigger than conservative or liberal.
They're about getting ratings, about making money, about doing stories that
are easy to cover.
~~ Al Franken,
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,226
Latest member
KristanTal

Latest Threads

Top