Reading a string of unknown size

W

websnarf

Al said:
I've found that all cut-and-paste errors can be eliminated by avoiding
cut-and-paste.

Yes, and in fact all programming errors can be eliminated by avoiding
programming.
 
W

websnarf

Richard said:
(e-mail address removed) said:

I don't agree that comments are superfluous. *You* said the structure was
superfluous, and "superfluous" means "redundant, unnecessary" (look in a
dictionary if you don't believe me).

Huh? I don't have a problem with the definition. Comments describe
what the code is doing (redundant, since the source itself does that)
and are ignored by the compiler (unnecessary). So what is your
problem? (Note that this does not imply that Comments are a bad
thing.)
If it's useful, how can it be redundant?

Well let's just pause and think about this for a second.

Most CPU caches have parity bits, or ECC which are *REDUNDANT* to the
raw data its already carrying. So the question is, how can parity bits
or ECC be useful? Perhaps we need a research project to figure that
out. You'll find the same thing on hard disks and CD Roms.

TCP/IP uses a ones completement checksum on its packets. This checksum
is obviously derivable from the rest of the data its delivering.

There are well known algorithms, in fact called "Cyclic Redundancy
Check". Interesting that people would waste time developing these
algorithms if they weren't useful. Do you think these algorithms
belong in the same category as bogosort or the brainf---- computer
language?

In C or Pascal, you usually have a { or begin that matches the start of
a program block. Without any loss of correct grammar parsing, you
could obviously just drop those. So they should be considered
redundant.

When you teach a grade school student to spell, or some rules of
arithmetic or whatever, you commonly do so through the process of
repetition. This act is of course redundant, since you aren't doing
anything one time you didn't do an earlier time. So is it useful for
students to repeat exercises like this even though it is redundant?
Taking a step forward again, "it works fine" is a fabulous *baseline* for
writing code.

Bare minimum requirements are fabulous?
[...] For example, fopen works fine, and I use it quite happily,
without worrying that my code is somehow of a low standard just because it
uses something that works fine.

But obfuscated code works fine too.
[...] Using void pointers correctly does not
imply fragile code.

Well that's not exactly what I was saying. It becomes a place were an
error can *hide*. Fragile code is usually much better because it
breaks at the drop of a hat, and so you can isolate and debug it
easily, or with moderate testing.

With a void * pointer, you can allocate the wrong thing to it. If you
inadvertantly underallocate and you have good heap debugging facilities
then maybe you can get away with a short debug session. But if you
*over* allocate and this causes you to run out of memory -- now what
are you going to do? The bug may have happened many *days* earlier
during a long run of something.
[...] Using void pointers incorrectly is a losing strategy.
But so is using casts incorrectly. So is using fopen incorrectly. So is
using *anything* incorrectly.

So you live in such a dichometric universe that you can't see anything
other than black and white? Either something is wrong or it isn't, and
you are not even concerned at all with the path you take in getting
from wrong to right?
Yes - in fact I've almost certainly done so right here in comp.lang.c. And
I've debugged screwed-up macro calls, too. So?


"Think first, compute later" is always a good plan.

No, that's not a plan at all. Its a mantra, and an unachievable ideal.
People do not think with perfection, so this will inevitably lead to
manifestions of thoughtless code. How about a more serious plan: "Use
your tools to assist you in ferreting out bugs before they happen to
the greatest degree possible".
[...] I generally don't bother
too much with debuggers nowadays. They are occasional ports in a storm,
that's all.

So you don't deal with large amounts of other people's code?
[...] It can hide the non-inclusion of it's prototype,

On *SOME* older generation compilers. No modern compiler fails to
give a warning about this regardless of the cast.

So if anyone comes up with a counter-example, you can simply claim
that it's not a "modern" compiler. ("True Scotsman" argument.)

For development?

Sure. Just because an implementation doesn't give one particular
diagnostic message that Paul Hsieh thinks it should, that doesn't mean
it's a Bad Compiler.

Right ... but we're several posts into this, and you couldn't even come
up with one?

Why bother?
Indeed.

[...] The diagnostic message is not required by the Standard, so it
makes no sense to me to insist to compiler-writers that they provide it.

Right. Unfortunately, I never been able to successfully compile
anything using the standard. I usually use a compiler.
[...] So trying to make C type safe is a bit like
trying to make Ook! object-oriented.

Interesting observation. If you intersect C and C++ what are you left
with?

Either poor C, poor C++, or syntax errors.

There's some intellectual honesty for you. Incidentally, the answer is
a syntactical subset of C (but functionally equivalent to C itself).
I have no problem with free automated assistance, but free automated
dictation is another matter. Whether a pointer of type <foo> is meaningful
when interpreted as if it were a pointer of type <bar> is something that
I'll judge for myself.

So why have any type safety at all?
No, you smell comp.lang.c, which is about C, not C++. If you want to discuss
C++, there's a whole nother newsgroup for that.

When did I say I wanted to discuss C++? When did I imply this? What
is leading you to this ridiculous statement? You can't read complete
sentences, that you didn't even snip out. That's a blindness very
common to fundamentalism.
[...] And if you want to discuss
programming in general, there's a newsgroup for that, too.

I'm not taking direction from you or anyone about where I post.
Oh, okay, I see what you mean.

It took this many posts?
[...] I thought you were talking about types, not
objects. The reason I didn't "get it" immediately is probably because I
find it quicker to type p = malloc(n * sizeof *p); than to invoke a copy
operation, a move operation, a paste operation, and two edits. Copy-paste
is expensive compared to typing when the amount to be copied is low, and
silly when the amount to be copied is high (because you're missing an
opportunity for re-factoring).

Enter the bizzaro world of Richard Heathfield's editting mind. You
usually end up doing this when you copy entire routines that are
similar in nature, but need to rework the insides of it a bit to match
different signatures and types. C doesn't have templates you know.
Perhaps I need more practice. I generally don't manage to make debugging
last more than a few minutes.

Yeah, it only takes you days to understand what is meant by a
copy-paste error. You'll excuse me if I am skeptical of your claim.
 
C

Clever Monkey

Well this is what he said: "Fortran compilers are usually even better
than that." (responding my statement about compiler optimizers and
warnings). You see his *attempt* at sarcasm doesn't work unless he is
able to establish this premise. Otherwise, his statement doesn't make
any sense at all.
Well, among high-performance computational folks, Fortran *is*
considered a better compiler, and not just because Fortran compilers can
optimize far better than many C compilers.

There are a lot of spilt pixels out there comparing benchmarks for
typical heavy computational work.
 
C

Clever Monkey

Richard said:
(e-mail address removed) said:
Ok, [Chris Torek has] made two gross mistakes in premise. But he
clearly has a lot of experience, and at least some skill as far as I can
tell.
As far as I can tell, he has more of both than you do. And that's likely to
be the perception amongst others here too. That doesn't mean he's
infallible. But if you and he disagree over something, common sense and
experience will lead me to assume that he's right and you're wrong, unless
you can come up with some extraordinarily convincing counter-evidence. So
far, you have not done so.

Given a previous discussion the two of us were in, I have to resist
making the ridiculously obvious retort at this point. Let me put it in
the most polite way as possible -- do you really think that your
personal deference to him is somehow supposed to mean something to me?
If it helps, let me tell you that I am a naturally anti-authoritarian
person. I strongly feel that pedistals are meant to be knocked over
(they serve no other real purpose).
The possibility that he's right and you're either wrong or misinterpreting
what he's said.

Uh ... he said Fortran was better than C (at optimization and/or
diagnostics). No matter how much you delete this quote, he still said
it. If he can make a strong case for the diagnostics, then I will
concede that he just wasn't being clear (but bizarrely intentionally
so). As for the optimizations, he's barking up the wrong tree if he
wants to try to present that case to me.
Uh, again, Fortran is a better tool for some sorts of work. This is in
part because the compilers can optimize better (i.e., due to the way
explicit pointers are implemented) and the diagnostics are more robust.

I certainly am not getting into a holy war over this, but in general it
is well accepted that Fortran emits much faster code, especially for
some sorts of computational work. And it is also generally accepted
that it is easier to make fast, optimized code without resorting to
special compilers, optimized libraries or clever optimization techniques.

There are plenty of split pixels out there comparing benchmarks and
discussing this stuff.

The details can be argued /ad infinitum/, but simply asserting that
Fortran might be better than C in terms of automatic optimizations and
robust diagnostics is not some crazy unfounded assumption. It reflects
a fair amount of scholarly evidence and years of experience.
 
R

Richard Heathfield

(e-mail address removed) said:
Huh? I don't have a problem with the definition. Comments describe
what the code is doing (redundant, since the source itself does that)

Good comments do more than merely describe what the code is doing - they
describe /why/ the code is doing it. They summarise, explain, and inform,
at a level that is not constrained by syntax rules. They also record other
useful information (e.g. algorithm sources, author info, and the like) that
cannot reasonably be shoehorned into the C code itself.
and are ignored by the compiler (unnecessary).

"Ignored by the compiler" and "unnecessary" are two very different concepts.
The one does not imply the other.
So what is your problem?

I'm not the one with the problem.
(Note that this does not imply that Comments are a bad thing.)
Noted.


Well let's just pause and think about this for a second.

Most CPU caches have parity bits, or ECC which are *REDUNDANT* to the
raw data its already carrying. So the question is, how can parity bits
or ECC be useful? Perhaps we need a research project to figure that
out. You'll find the same thing on hard disks and CD Roms.

Parity bits are not redundant. They act as a check on the integrity of the
data.
TCP/IP uses a ones completement checksum on its packets. This checksum
is obviously derivable from the rest of the data its delivering.

Same example, dressed in different clothes. Same answer.
There are well known algorithms, in fact called "Cyclic Redundancy
Check".

Sounds like a misnomer to me.

In C or Pascal, you usually have a { or begin that matches the start of
a program block. Without any loss of correct grammar parsing, you
could obviously just drop those. So they should be considered
redundant.

From another perspective, however, the language requires them to be present
in a correct program, and so they are far from redundant.

<Lots of silly stuff snipped - so silly that no comment other than this is
necessary>
[...] Using void pointers correctly does not
imply fragile code.

Well that's not exactly what I was saying. It becomes a place were an
error can *hide*. Fragile code is usually much better because it
breaks at the drop of a hat, and so you can isolate and debug it
easily, or with moderate testing.

If you prefer fragile code, that's up to you. I prefer a bit more
robustness.
[...] Using void pointers incorrectly is a losing strategy.
But so is using casts incorrectly. So is using fopen incorrectly. So is
using *anything* incorrectly.

So you live in such a dichometric universe that you can't see anything
other than black and white? Either something is wrong or it isn't, and
you are not even concerned at all with the path you take in getting
from wrong to right?

I can see things in many colours, but sometimes things /are/ black and
white. Now, the whole void * thing is not actually black and white, because
there are many people who aren't necessarily going to use them properly,
and perhaps such people - if they're not prepared to learn how to use them
properly - would be better off avoiding them. Personally, I think it's
better to learn how to use them properly.
No, that's not a plan at all. Its a mantra, and an unachievable ideal.

Thinking before computing is unachievable? I cannot agree with that.
People do not think with perfection, so this will inevitably lead to
manifestions of thoughtless code.

Now who's thinking in black and white? No, the imperfection of people's
thought will not inevitably lead to manifestations of thoughtless code, but
rather to manifestations of code written by a less than perfect thinker.
How about a more serious plan: "Use
your tools to assist you in ferreting out bugs before they happen to
the greatest degree possible".

Provided they don't get in my way, sure. But that means dropping the "to the
greatest degree possible" bit. The greatest degree possible is "don't write
the program", which is a good indication of where an extreme will take you.
[...] I generally don't bother
too much with debuggers nowadays. They are occasional ports in a storm,
that's all.

So you don't deal with large amounts of other people's code?

ROTFL! Yes, I deal with large amounts of other people's code. No, I don't
often use a debugger when doing so. Sometimes, yes, but usually, no.

[...] To some people, type safety is a straitjacket.

Well to some people type safety is free automated assistance.

I have no problem with free automated assistance, but free automated
dictation is another matter. Whether a pointer of type <foo> is
meaningful when interpreted as if it were a pointer of type <bar> is
something that I'll judge for myself.

So why have any type safety at all?

I view type safety as a guide, rather than a dictator. Guides can be useful.
When did I say I wanted to discuss C++? When did I imply this? What
is leading you to this ridiculous statement?

Your words:
You can't read complete
sentences, that you didn't even snip out.

See above.
That's a blindness very common to fundamentalism.

Who is the fundamentalist here?
[...] And if you want to discuss
programming in general, there's a newsgroup for that, too.

I'm not taking direction from you or anyone about where I post.
Evidently.
Oh, okay, I see what you mean.

It took this many posts?

It'll take a great many more, it seems, before *you* see what *I* mean, so I
guess I'm ahead of the game.
 
M

Malcolm

Enter the bizzaro world of Richard Heathfield's editting mind. You
usually end up doing this when you copy entire routines that are
similar in nature, but need to rework the insides of it a bit to match
different signatures and types. C doesn't have templates you know.
Join my campaign for 64-bit ints.
Then there will be no need for templates, since all numbers (well, integers)
will be represented in the same way.
 
R

Richard Heathfield

Malcolm said:
Join my campaign for 64-bit ints.
Then there will be no need for templates, since all numbers (well,
integers) will be represented in the same way.

Even 18446744073709551617 ?
 
W

websnarf

Clever said:
Uh, again, Fortran is a better tool for some sorts of work. This is in
part because the compilers can optimize better (i.e., due to the way
explicit pointers are implemented) and the diagnostics are more robust.

If you can make a strong case for the diagnostics, then fine. Fortran
doesn't have type specific problems, so what precisely is Fortran
bringing to the table in terms of diagnostics. Does it detect and
notify the programmer about numerically unstable code, or what? (I am
familliar what Fortran compilers only in the code they generate; I'm
not much of a practicioner of the language itself.)
I certainly am not getting into a holy war over this, but in general it
is well accepted that Fortran emits much faster code, especially for
some sorts of computational work. And it is also generally accepted
that it is easier to make fast, optimized code without resorting to
special compilers, optimized libraries or clever optimization techniques.

Both Intel and Microsoft now support auto-vectorizors in their C
compilers. Both compilers support a "no aliasing" flag and Intel
supports restrict. These compilers are not special (they are both
mainstream) and there are no special techniques. The code should
appear comparable to the equivalent Fortran code.
There are plenty of split pixels out there comparing benchmarks and
discussing this stuff.

The details can be argued /ad infinitum/, but simply asserting that
Fortran might be better than C in terms of automatic optimizations and
robust diagnostics is not some crazy unfounded assumption. It reflects
a fair amount of scholarly evidence and years of experience.

This represents obsolete experience. The most clear example of this is
the x86 platform. The fastest Fortran compilers come from Intel (or
near fastest, I don't know exactly what the status of the latest Lahey
or PathScale compilers are like). However, this compiler uses the same
back end for compiling both C and Fortran. It is crucial to observe
that the Intel compiler uses the identical intermediate => vector
translators for both languages. From a language point of view, the
only lacking feature from C is the aliasing problem. However, Intel
includes both a "no alias" flag as well as the "restrict" keyword from
C99. But once again both languages eventually translate the "no alias"
feature equivalently down to the intermediate language. Thus this
leaves no room for Fortran to outperform C; you can always write your C
code to be able to leverage any optimization technique available to the
Fortran front end.

Now on the flip side, things are not equal. Fortran has a very
different interface to its integer semantics. For example, its integer
shift is highly generalized to include negative and positive shifting
(and I believe it has to correctly saturate as well). The Fortran
compiler has no opportunity to be able to assume the shift count
variable is either positive or negative. This what is rendered as a
single and very parallelizable instruction in C, ends up taking about 8
instructions in Fortran. Fortran also does not have a concept of
pointers or unions. Thus for certain data structures where those are
optimal ways of implementing them, In Fortran you are forced to
implement "work-a-likes" (i.e., pretend an array is a heap, and just
seperate variables by storage even if they never have overlapping
lifetimes) that the compiler is unlikely to be able to simplify down to
the C equivalent.

WIth any objective analysis, the details don't look too good for
Fortran. The places where it had an advantage in the past, were of a
historical and engineering effort nature. Vectorizors have been ported
to C compilers nowadays, so the major ace up Fortran's sleeve is gone.


Realistically one cannot support a case that suggests that Fortran
continues to be a language that is faster than C. It used to be true
for pure array/floating point, and it was never true for integer or
data structure code. Now its no longer true for array/floating point
code.
 
O

Old Wolf

Well let's just pause and think about this for a second.

The problem is that Paul Hsieh thinks "redundant" means "useless".

In fact, the English word "redundant" means exactly that, in the
minds of many people.

But in computer science, "redundant" means that it performs a
function which is already being performed by something else.
This may or may not be useless.

For example, companies pay for expensive servers which are
entirely redundant. This is so that if the main server dies then
the redundant one can become the main one.

(I'm sure you know all this, but it seems to have sprung into
several long messages to try and resolve it).
 
K

Keith Thompson

Malcolm said:
Join my campaign for 64-bit ints.
Then there will be no need for templates, since all numbers (well, integers)
will be represented in the same way.

No.
 
B

Beliavsky

(e-mail address removed) wrote:

Now on the flip side, things are not equal. Fortran has a very
different interface to its integer semantics. For example, its integer
shift is highly generalized to include negative and positive shifting
(and I believe it has to correctly saturate as well). The Fortran
compiler has no opportunity to be able to assume the shift count
variable is either positive or negative. This what is rendered as a
single and very parallelizable instruction in C, ends up taking about 8
instructions in Fortran. Fortran also does not have a concept of
pointers or unions.

Fortran 90 and later versions do have pointers, but they differ from
those of C.

WIth any objective analysis, the details don't look too good for
Fortran. The places where it had an advantage in the past, were of a
historical and engineering effort nature. Vectorizors have been ported
to C compilers nowadays, so the major ace up Fortran's sleeve is gone.

Fortran 2003 is a higher-level language than C (this does not
necessarily mean better), especially in its handling of
multidimensional arrays, and I think its real competition is C++ among
compiled programming languages and Matlab/Octave/Scilab and
Python+Numpy among interpreted languages.
 
W

websnarf

Old said:
The problem is that Paul Hsieh thinks "redundant" means "useless".

Please read the thread attributions more carefully. You are targetting
the wrong person.
In fact, the English word "redundant" means exactly that, in the
minds of many people.

But in computer science, "redundant" means that it performs a
function which is already being performed by something else.
This may or may not be useless.

This is precisely the point I am making. Exactly. You have just read
the attributions of the threads incorrectly. Redundancy is, in fact, a
feature. People often pay an extremely high premium for it. Go tell
that to the other guy.
 
W

websnarf

Richard said:
(e-mail address removed) said:

Good comments do more than merely describe what the code is doing - they
describe /why/ the code is doing it. They summarise, explain, and inform,
at a level that is not constrained by syntax rules. They also record other
useful information (e.g. algorithm sources, author info, and the like) that
cannot reasonably be shoehorned into the C code itself.

In other words they are a redundant and unnecessary reexpression of the
algorithm (i.e., technically superfluous) which happen to also serve
another purpose. Just because you happen to have another purpose for
them doesn't relieve them of their redundancy status.
Parity bits are not redundant. They act as a check on the integrity of the
data.

Straight from the school of Frank Luntz and his ilk. They check the
integrity of the data *AND* they are redundant. That's the whole
fricking point! The redundancy itself is the feature. If they weren't
redundant, then they wouldn't be serving their intended purpose.
Same example, dressed in different clothes. Same answer.


Sounds like a misnomer to me.

You think its misnamed? Are you crazy? Its a very precise decription
for what it is.
From another perspective, however, the language requires them to be present
in a correct program, and so they are far from redundant.

<Lots of silly stuff snipped - so silly that no comment other than this is
necessary>

You of course snipped the different examples, instead of the ones that
you said were repeats.
[...] Using void pointers correctly does not
imply fragile code.

Well that's not exactly what I was saying. It becomes a place were an
error can *hide*. Fragile code is usually much better because it
breaks at the drop of a hat, and so you can isolate and debug it
easily, or with moderate testing.

If you prefer fragile code, that's up to you. I prefer a bit more
robustness.

I prefer code with perfect robustness. If code is fragile, I will get
to that goal faster. If code has hidden errors, even though you could
technically call them more robust than fragile code, then that's worse.
Any serious engineer will always prefer the nice hard crash at the
least provocation to heisenbugs or errors encoded in pseudo-correct
code that is just dying on some inadvertant semantic.
Thinking before computing is unachievable? I cannot agree with that.

Its not practically achievable to have every line of code thoughtfully
considered. Unless you are ok with 3 lines of code produced a day or
something like that. And this sort of thing does not mask the greater
issue of normal error rates.
Now who's thinking in black and white? No, the imperfection of people's
thought will not inevitably lead to manifestations of thoughtless code, but
rather to manifestations of code written by a less than perfect thinker.
From a code production point of view, this is not a distinction with
any relevance. How a bug gets into your code is far less important
than *if* the bug gets in there.
Provided they don't get in my way, sure.

Yeah, and your mindless obstinance is real conducive to this.
[...] But that means dropping the "to the
greatest degree possible" bit. The greatest degree possible is "don't write
the program", which is a good indication of where an extreme will take you.

Anything to twist words to mean things I clearly cannot possibly mean.
Casting malloc doesn't inhibit your ability to program.
Your words:

Ok, I don't see the part where I say I want to discuss C++. Nor is it
implied.
See above.

I see it -- I wrote it, and I remember what I wrote. I have not
suggested the discussion of C++ here.
Who is the fundamentalist here?

The one that puts forth ideas that don't match ordinary parsing of
facts. You suggested redundancy means something other than repetition
(you suggested it meant non-uselessness), and you don't see a
distinction between C++ compilers and the C++ language.
 
P

pete

Tonio said:
I have to read characters from stdin and save them in a string. The
problem is that I don't know how much characters will be read.

/* BEGIN line_to_string.c */

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

struct list_node {
struct list_node *next;
void *data;
};

int line_to_string(FILE *fp, char **line, size_t *size);
int list_fputs(FILE *stream, struct list_node *node);
void list_free(struct list_node *node, void (*free_data)(void *));
struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data);

int main(void)
{
struct list_node *head, *tail;
int rc;
char *buff_ptr;
size_t buff_size;
long unsigned line_count;

#if 1
buff_size = 0;
buff_ptr = NULL;
#else
buff_size = 100;
buff_ptr = malloc(buff_size);
if (buff_ptr == NULL) {
puts("malloc trouble!");
exit(EXIT_FAILURE);
}
#endif

tail = head = NULL;
line_count = 0;
puts(
"\nThis program makes and prints a list of all the lines\n"
"of text entered from standard input.\n"
"Just hit the Enter key to end,\n"
"or enter any line of characters to continue."
);
while ((rc = line_to_string(stdin, &buff_ptr, &buff_size)) > 1) {
++line_count;
tail = string_node(&head, tail, buff_ptr);
if (tail == NULL) {
break;
}
puts(
"\nJust hit the Enter key to end,\n"
"or enter any other line of characters to continue."
);
}
switch (rc) {
case EOF:
if (buff_ptr != NULL && strlen(buff_ptr) > 0) {
puts("rc equals EOF\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = string_node(&head, tail, buff_ptr);
}
break;
case 0:
puts("realloc returned a null pointer value");
if (buff_size > 1) {
puts("rc equals 0\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = string_node(&head, tail, buff_ptr);
}
break;
default:
break;
}
if (line_count != 0 && tail == NULL) {
puts("Node allocation failed.");
puts("The last line entered didn't make it onto the list:");
puts(buff_ptr);
}
free(buff_ptr);
puts("\nThe line buffer has been freed.\n");
printf("%lu lines of text were entered.\n", line_count);
puts("They are:\n");
list_fputs(stdout, head);
list_free(head, free);
puts("\nThe list has been freed.\n");
return 0;
}

int line_to_string(FILE *fp, char **line, size_t *size)
{
int rc;
void *p;
size_t count;

count = 0;
while ((rc = getc(fp)) != EOF) {
++count;
if (count + 2 > *size) {
p = realloc(*line, count + 2);
if (p == NULL) {
if (*size > count) {
(*line)[count] = '\0';
(*line)[count - 1] = (char)rc;
} else {
ungetc(rc, fp);
}
count = 0;
break;
}
*line = p;
*size = count + 2;
}
if (rc == '\n') {
(*line)[count - 1] = '\0';
break;
}
(*line)[count - 1] = (char)rc;
}
if (rc != EOF) {
rc = count > INT_MAX ? INT_MAX : count;
} else {
if (*size > count) {
(*line)[count] = '\0';
}
}
return rc;
}

void list_free(struct list_node *node, void (*free_data)(void *))
{
struct list_node *next_node;

while (node != NULL) {
next_node = node -> next;
free_data(node -> data);
free(node);
node = next_node;
}
}

int list_fputs(FILE *stream, struct list_node *node)
{
while (node != NULL) {
if (fputs(node -> data, stream) == EOF
|| putc('\n', stream) == EOF)
{
break;
}
node = node -> next;
}
return node == NULL ? '\n' : EOF;
}

struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data)
{
struct list_node *node;

node = malloc(sizeof *node);
if (node != NULL) {
node -> next = NULL;
node -> data = malloc(strlen(data) + 1);
if (node -> data != NULL) {
if (*head == NULL) {
*head = node;
} else {
tail -> next = node;
}
strcpy(node -> data, data);
} else {
free(node);
node = NULL;
}
}
return node;
}

/* END line_to_string.c */
 
R

Richard Bos

Old Wolf said:
The problem is that Paul Hsieh thinks "redundant" means "useless".

In fact, the English word "redundant" means exactly that, in the
minds of many people.

Those many people are just as wrong as Paul, then.

Richard
 
C

CBFalconer

pete said:
/* BEGIN line_to_string.c */
.... snip 180 lines of code ...

I think the heart code of ggets is somewhat simpler. See:

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

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

#define INITSIZE 112 /* power of 2 minus 16, helps malloc */
#define DELTASIZE (INITSIZE + 16)

enum {OK = 0, NOMEM};

int fggets(char* *ln, FILE *f)
{
int cursize, ch, ix;
char *buffer, *temp;

*ln = NULL; /* default */
if (NULL == (buffer = malloc(INITSIZE))) return NOMEM;
cursize = INITSIZE;

ix = 0;
while ((EOF != (ch = getc(f))) && ('\n' != ch)) {
if (ix >= (cursize - 1)) { /* extend buffer */
cursize += DELTASIZE;
if (NULL == (temp = realloc(buffer, (size_t)cursize))) {
/* ran out of memory, return partial line */
buffer[ix] = '\0';
*ln = buffer;
return NOMEM;
}
buffer = temp;
}
buffer[ix++] = ch;
}
if ((EOF == ch) && (0 == ix)) {
free(buffer);
return EOF;
}

buffer[ix] = '\0';
if (NULL == (temp = realloc(buffer, (size_t)ix + 1))) {
*ln = buffer; /* without reducing it */
}
else *ln = temp;
return OK;
} /* fggets */
 
R

Richard Bos

Well, this still has the potential for cut and paste errors unless you
macrofy the whole line.

Unless, of course, you're clever enough not to cut and paste.
If you don't macrofy, then you risk error no matter what.

Heh. The typical complaint of a sorry typist.
So let us take a more serious approach compare macros which prevent any
mismatch errors:

#define scaredOfCPlusPlus(var,count) var = malloc(count*sizeof *var)

Gosh, what a sane name for a macro. You must be a popular cow-orker.
#define newThing(type,count) (type *) malloc (count * sizeof (type))

So you can say var = newThing(char *, 512), and if the type is wrong,
the compiler tells you.

Right. But now change the type of your pointer. Say, from a LinkedList *
tp a Binary_Tree *. Happens, you know. Programs evolve. So do data sets.
Some programmers, apparently, never, alas. But the clever ones program
for maintainability, not for not-having-to-think-up-frontness.
The scaredOfCPlusPlus(,) macro works fine, but doesn't look familliar,

And this would be why, again? Oh, right, because good programmers don't
abuse the preprocessor like that.
And, of course, the real difference is that the first compiles straight
in C++, and the second is just an error.

That's a pretty good argument in comp.lang.c++. Guess where we're not?
I have found that in general the C++ optimizers and warnings are better
for the C++ mode of my compilers than the C mode.

Gosh, you think? C++ better at C++ than other languages - film at 11:00.


All your arguments make perfect sense _if_, and only if, you start by
accepting your premises that C++ is a better language than C, that all
programmers are stupid, and that getting a program to compile is more
important than getting it to work. Wise programmers accept none of those
premises.

Richard
 
E

Eric Sosman

Richard said:
Those many people are just as wrong as Paul, then.

<off-topic distance="extreme">

No, they're just four centuries behind the times. "Redundant"
did not always carry the implication of "unnecessary," but only of
"repeated," or more prosaically "iterated." John Milton described
the serpent of Eden as a dazzlingly beautiful creature (how else
could it have tempted Eve?), with its coils "floating redundant" in
glistening display. He did not mean by this that the serpent had
too many coils, or more coils than it needed, but only that it had
lots of coils.

(This factoid came to me by way of the story "The Djinn in the
Nightingale's Eye" by A.S. Byatt, a tale as delightfully beautiful
as the serpent it mentions. Much more pleasant to read than the
Standard, I promise.)

</off-topic>
 
P

pete

CBFalconer said:
... snip 180 lines of code ...

I think the heart code of ggets is somewhat simpler. See:

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

Maybe, but they work differently.
ggets allocates a new buffer every time that it's called.

while (0 == fggets(&line, infile)) {
fprintf(stderr, "%4d %4d\n", ++cnt, (int)strlen(line));
(void)puts(line);
free(line);
}

line_to_string was designed to be called from a loop.
The number of allocation calls made within line_to_string
while reading a text file,
is a function of the length of of the longest line of the text file
and completely independant of how many lines are in the file.

while ((rc = line_to_string(stdin, &buff_ptr, &buff_size)) > 1) {
++line_count;
tail = string_node(&head, tail, buff_ptr);
if (tail == NULL) {
break;
}
puts(
"\nJust hit the Enter key to end,\n"
"or enter any other line of characters to continue."
);
}

If there's a zillion lines in a text file
and the longest line is only 100 bytes,
then string_to_line will only call realloc 100 times,
if the initial values of the buff_ptr and buff_size
are NULL and 0.

I've rewritten main().
If INITIAL_BUFFER_SIZE were to be defined as 100,
then to read the same zillion line text file mentioned above,
malloc would be called only once,
and realloc would not be called at all.

#define INITIAL_BUFFER_SIZE 0 /* Can be any number */

int main(void)
{
struct list_node *head, *tail;
int rc;
char *buff_ptr;
size_t buff_size;
long unsigned line_count;

buff_size = INITIAL_BUFFER_SIZE;
buff_ptr = malloc(buff_size);
if (buff_ptr == NULL && buff_size != 0) {
puts("malloc trouble!");
exit(EXIT_FAILURE);
}
tail = head = NULL;
line_count = 0;
puts(
"\nThis program makes and prints a list of all the lines\n"
"of text entered from standard input.\n"
"Just hit the Enter key to end,\n"
"or enter any line of characters to continue."
);
while ((rc = line_to_string(stdin, &buff_ptr, &buff_size)) > 1) {
 

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,780
Messages
2,569,611
Members
45,280
Latest member
BGBBrock56

Latest Threads

Top