using write() instead of puts() in a trivial program?

C

Charles Ulrich

Greetings,

I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I am
not willing to accept that the person who committed this code is a fool,
thus I have to conclude that I just don't understand why exactly the
following isn't good enough for such a trivial program:

puts("This account is currently not available.");

Perhaps write() would be appropriate in a program which did intensive
amounts of output, but the meat of this program is literally 1 line long.

I tried doing a Google search, but as you can imagine, the commonality of
the "write" keyword throws off any results that might be useful, so I was
wondering some kind, generous person here could clue me in on what's going
on here. I'd greatly appreciate it.

Charles Ulrich
 
J

Joona I Palaste

Charles Ulrich said:
Greetings,
I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:
#define MESSAGE "This account is currently not available.\n"
int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}
This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I am
not willing to accept that the person who committed this code is a fool,
thus I have to conclude that I just don't understand why exactly the
following isn't good enough for such a trivial program:
puts("This account is currently not available.");
Perhaps write() would be appropriate in a program which did intensive
amounts of output, but the meat of this program is literally 1 line long.
I tried doing a Google search, but as you can imagine, the commonality of
the "write" keyword throws off any results that might be useful, so I was
wondering some kind, generous person here could clue me in on what's going
on here. I'd greatly appreciate it.

If this is supposed to be a program that, when used for a user's shell,
displays the notice and then terminates, then I have to share your
confusion. Using a simple puts() call instead of a non-standard write()
call, even followed with a non-standard _exit() call, would have been
much simpler. Perhaps you could track down the writer of this program
and ask him why he wrote it that way?
 
A

Alexander Bartolich

begin followup to Charles Ulrich:
int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

write(2) is a system call.
_exit(2) is a system call.
[...] I just don't understand why exactly the following isn't
good enough for such a trivial program:

puts("This account is currently not available.");

puts(3) is part of the C run time library.

By using nothing but system calls the total size of the resulting
binary can be minimized. See option -nostdlib of gcc.
 
T

Tom St Denis

Joona I Palaste said:
If this is supposed to be a program that, when used for a user's shell,
displays the notice and then terminates, then I have to share your
confusion. Using a simple puts() call instead of a non-standard write()
call, even followed with a non-standard _exit() call, would have been
much simpler. Perhaps you could track down the writer of this program
and ask him why he wrote it that way?

Probably links in less code? Let's try this out

test1.c
#include <stdio.h>
int main(void) { puts("hello world"); return 0; }

vs.

test2.c
#include <unistd.h>
int main(void) { write(0, "hello world\n", 12); return 0; }

vs.

test3.c
#include <stdio.h>
int main(void) { printf("hello world\n"); return 0; }

All compile and execute fine in Linux... [compiled with -O2 -s]

tombox root # ls -l test?
-rwxr-xr-x 1 root root 3020 Jan 12 15:43 test1
-rwxr-xr-x 1 root root 3040 Jan 12 15:43 test2
-rwxr-xr-x 1 root root 3020 Jan 12 15:46 test3

So it turns out puts/printf is smaller ;-)

I guess the BSD guy either had a really specific reason [e.g. part of the
kernel? not in this case...] since puts is simpler and smaller!

Personally I'd just printf since I use it for all output ;-)

Tom
 
K

Keith Thompson

Charles Ulrich said:
I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD.
[...]

Presumably the FreeBSD nologin program doesn't need to be portable to
anything other than FreeBSD. Given that constraint, I suppose there's
not much advantage in using the portable puts() rather than the
relatively non-portable write().

Having said that, it's not how I would have written it. puts() or
printf() would make for more legible code.
 
P

Peteris Krumins

Greetings,

I hope my greenness isn't showing too bad by asking this, but I ran
across this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I
am not willing to accept that the person who committed this code is a
fool, thus I have to conclude that I just don't understand why exactly
the following isn't good enough for such a trivial program:

puts("This account is currently not available.");

Perhaps write() would be appropriate in a program which did intensive
amounts of output, but the meat of this program is literally 1 line
long.

I tried doing a Google search, but as you can imagine, the commonality
of the "write" keyword throws off any results that might be useful, so
I was wondering some kind, generous person here could clue me in on
what's going on here. I'd greatly appreciate it.

Charles Ulrich

Finding an answer to such silly problem is the same as asking and trying
to find an answer to a problem like "I have to go to the shop which is
across a street, I chose to go by bike. Maybe I should have chosen to
go to the shop by my shiny car, my shiny helicopter, my elephant?".


P.Krumins
 
E

Eric Sosman

Charles said:
Greetings,

I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I am
not willing to accept that the person who committed this code is a fool,
thus I have to conclude that I just don't understand why exactly the
following isn't good enough for such a trivial program:

puts("This account is currently not available.");

Various people have speculated about the pros and
cons and possible motivations for this code, but no one
yet seems to have mentioned the more interesting aspect:
the code outputs one more character than it (probably)
intends.

If the trailing '\0' is actually required for some
peculiar reason, that's unusual enough that a comment
in the source should be mandatory. If (as seems more
likely) the trailing '\0' is a bug, the code stands as
an object lesson in why stupid optimizations are stupid.
 
C

CBFalconer

Keith said:
Charles Ulrich said:
I hope my greenness isn't showing too bad by asking this, but I
ran across this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD.
[...]

Presumably the FreeBSD nologin program doesn't need to be portable
to anything other than FreeBSD. Given that constraint, I suppose
there's not much advantage in using the portable puts() rather than
the relatively non-portable write().

Having said that, it's not how I would have written it. puts() or
printf() would make for more legible code.

This may have to run in circumstances where the C initialization
cannot function, thus making stdout unavailable.
 
C

Charles Ulrich

Finding an answer to such silly problem is the same as asking and trying
to find an answer to a problem like "I have to go to the shop which is
across a street, I chose to go by bike. Maybe I should have chosen to
go to the shop by my shiny car, my shiny helicopter, my elephant?".


P.Krumins

It's pretty easy to say that in retrospect, isn't it? But look at it from
my point of view. I'm just now emerging from being a complete novice in C
and I stumbled across some code that made little sense given its
elementary job. I am at too low a skill level to be deciding for myself
what constitutes a "silly problem" and what does not. _That's why I asked._

Thanks and gratitude to those who provided helpful input.

Charles Ulrich
 
S

Severian

begin followup to Charles Ulrich:
int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

write(2) is a system call.
_exit(2) is a system call.
[...] I just don't understand why exactly the following isn't
good enough for such a trivial program:

puts("This account is currently not available.");

puts(3) is part of the C run time library.

By using nothing but system calls the total size of the resulting
binary can be minimized. See option -nostdlib of gcc.

Also, when the C standard library is shared, and its file is corrupt
or inaccessible, minimum programs can run.

- Sev
 
G

glen herrmannsfeldt

Keith Thompson wrote:

(snip)
Presumably the FreeBSD nologin program doesn't need to be portable to
anything other than FreeBSD. Given that constraint, I suppose there's
not much advantage in using the portable puts() rather than the
relatively non-portable write().
Having said that, it's not how I would have written it. puts() or
printf() would make for more legible code.


In the days of dynamic link libraries, it is important that some
programs be able to run without dynamic linking. Now, it could
use the static library, but it will be a lot smaller not to.

-- glen
 
D

Dan Pop

In said:
I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I am
not willing to accept that the person who committed this code is a fool,
thus I have to conclude that I just don't understand why exactly the
following isn't good enough for such a trivial program:

puts("This account is currently not available.");

Perhaps write() would be appropriate in a program which did intensive
amounts of output, but the meat of this program is literally 1 line long.

write() is also appropriate for small programs that are supposed to be
statically linked, while puts() is likely to draw a lot of baggage from
the stdio library. The latter also burns more CPU cycles: in such a
program it is nothing more than a fairly expensive wrapper for a write()
call. The usage of _exit() instead of exit() also indicates a concern
for minimising the number of CPU cycles used by the program, which
explains why the application was not written as a shell script:

#!/bin/sh
echo "This account is currently not available."
exit 1

So, your answer is: the author wanted a standalone application with zero
overhead in terms of disk space and cpu cycles. It's hard to tell whether
his concerns were justified or not, considering that nologin is not even
a standard Unix utility.

Dan
 
A

Alan Balmer

Finding an answer to such silly problem is the same as asking and trying
to find an answer to a problem like "I have to go to the shop which is
across a street, I chose to go by bike. Maybe I should have chosen to
go to the shop by my shiny car, my shiny helicopter, my elephant?".

Not exactly. In this case, it's "I notice that you took the bike to
the shop rather than the elephant. Was there a reason for that?"

It's a legitimate question, and can provide valuable insight - some of
the best advice a novice can get is "read other people's code." Asking
why a presumably experienced programmer chose one method over another
can be informative and educational. I even consider it topical -
discussing the pros and cons of using standard C rather than the
non-standard write function.

In this case, the best answer is "ask the programmer." Many
open-source programmers are happy to discuss their code. If the OP
wants guesses, my guess would be that the executable using "write" is
smaller with the proper linker options.
 
D

Default User

Alan said:
It's a legitimate question, and can provide valuable insight - some of
the best advice a novice can get is "read other people's code." Asking
why a presumably experienced programmer chose one method over another
can be informative and educational. I even consider it topical -
discussing the pros and cons of using standard C rather than the
non-standard write function.


I'm on the opposite side of that fence, I don't think reading other
people's code is good for newbies. They don't have the experience base
to filter what they are reading. They are far better off sticking with
texts, standards and their own projects until such time as their skills
develop.

Reading code, to me, doesn't really improve programming skills. It
improves the skill of reading code, which is in itself a useful and
important skill if you are going to work in the industry.




Brian Rodenborn
 
D

Dan Pop

In said:
Reading code, to me, doesn't really improve programming skills. It
improves the skill of reading code, which is in itself a useful and
important skill if you are going to work in the industry.

Reading well written code does improve programming skills, because you
get to learn better ways of doing certain things.

Dan
 
A

Alan Balmer

I'm on the opposite side of that fence, I don't think reading other
people's code is good for newbies. They don't have the experience base
to filter what they are reading.

Which is why they come here to ask ;-)
They are far better off sticking with
texts,

Texts are not necessarily a source of good programming practices. Many
of the popular ones misinform.
standards

I may be wrong, but I don't see the standard as being useful for new
programmers. Once "how" turns into "why" and "why not", the standard
is good, but that requires some experience.
and their own projects until such time as their skills
develop.

That's probably the best, *providing* they get proper feedback.
Otherwise, just developing their own projects can do more harm than
good.
Reading code, to me, doesn't really improve programming skills. It
improves the skill of reading code, which is in itself a useful and
important skill if you are going to work in the industry.
If I don't know how to do something, and I read someone else's code
which does it, it improves my programming skills.I think reading *good* code is educational. Maybe we need an
"approved" body of reading material <g>. It may well be that random
bits of BSD source code don't qualify.
 
R

Richard Heathfield

Alan said:
Not exactly. In this case, it's "I notice that you took the bike to
the shop rather than the elephant. Was there a reason for that?"

Don't be silly. Why would anyone take a bike to an elephant?

<g,d&r>
 
D

Default User

Dan said:
Reading well written code does improve programming skills, because you
get to learn better ways of doing certain things.


But that usually requires a sufficient skill level to be able to take
advantage of the new knowledge. Newbies can't tell good code from bad,
good practices from crap, nifty new techniques from hacky, unsafe
shortcuts. It also encourages cut-n-paste cargo cultism.




Brian Rodenborn
 
C

CBFalconer

Richard said:
Don't be silly. Why would anyone take a bike to an elephant?

So the elephant could learn to ride it, and make than 'anyone' a
pot of money in the circus.

<d & r II>
 
D

Dan Pop

In said:
But that usually requires a sufficient skill level to be able to take
advantage of the new knowledge.

We're talking about *improving* programming skills, not about *acquiring*
them, right?
Newbies can't tell good code from bad,
good practices from crap, nifty new techniques from hacky, unsafe
shortcuts.

Newbies can ask advanced programmers for pieces of good C code, that
can be used for this purpose. Few people are learning C alone on a
desert island...
It also encourages cut-n-paste cargo cultism.

Not in my experience. The main purpose of the exercise is to get ideas
about how to code in C, rather than finding solutions to a concrete
problem. For the latter, people usually go to algorithms books with
examples in C.

Dan
 

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

Forum statistics

Threads
474,135
Messages
2,570,784
Members
47,342
Latest member
KelseyK737

Latest Threads

Top