I have written a reimplementation of cat that will be compliant with most scripts, and that is as small and fast as possible. Can anybody suggest some size improvements for it?
#include <stdio.h> /* we need printf, fprintf, FILE,
* fopen, fclose and that sort of
* stuff */
#include <stdlib.h> /* for exit, EXIT_SUCCESS, and
* EXIT_FAILURE */
int main(int argc, char *argv[])
{
FILE *fp; /* file to cat */
short int ch; /* character to print */
unsigned short int cp; /* currently printing argv[?] */
unsigned short int failed = 0; /* how many failed to print */
if (argc < 2) { /* if not enough args act as pipe */
for (;;) { /* infinite loop */
if ((ch = fgetc(stdin)) == EOF) {
exit(EXIT_SUCCESS);
} else {
printf("%c", ch);
}
}
}
for (cp = 1; cp <= argc - 1; cp++) { /* for each file in cli args */
fp = fopen(argv[cp], "r"); /* open the file */
if(fp == NULL) {
fprintf(stderr, "%s: cannot open %s\n", argv[0], argv[cp]);
failed++; /* increment the number of failed */
continue; /* go back to beginning */
}
while ((ch = fgetc(fp)) != EOF) {
printf("%c", ch);
}
fclose(fp); /* close the file */
}
exit(failed); /* exit with the number of files */
} /* that failed */
I'm sorry that the indentation is messed up, I use tabs in my code.
In clang version 3.5.0 on Ubuntu the generated binary size is 5200 bytes with these options:
-Oz -fomit-frame-pointer -s
Improvements
I added some more error checking, added -u and - and also wrote a function that writes out a file to avoid repeating myself. Also changed short int to int, printf to putchar, and some other things.
#include <stdio.h> /* we need printf, fprintf, FILE,
* fopen, fclose and that sort of
* stuff */
#include <stdlib.h> /* for exit, EXIT_SUCCESS, and
* EXIT_FAILURE */
#include <string.h> /* need strcmp */
void write_file(char *file, char *progname) /* function to print individual file */
{
FILE *fp; /* file to write */
int ch; /* temporary variable */
if (strcmp(file, "-") == 0) { /* check if using stdin */
fp = stdin; /* use standard input */
} else {
fp = fopen(file, "r"); /* open file for writing */
}
if (fp == NULL) { /* error in opening file */
fprintf(stderr, "%s: cannot open %s\n", progname, file);
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF) {
if (putchar(ch) == EOF) {
fprintf(stderr, "%s: can't read %s\n", progname, file);
exit(EXIT_FAILURE);
}
}
if (ferror(fp) != 0) { /* IO error, perhaps? */
fprintf(stderr, "%s: error reading %s\n", progname, file);
exit(EXIT_FAILURE);
}
if (strcmp(file, "-") != 0) { /* close the file unless stdin */
fclose(fp);
}
return;
}
int main(int argc, char *argv[])
{
unsigned short int cp = 1; /* currently printing argv[?] */
if (argc < 2) { /* act as pipe */
write_file("-", argv[0]);
exit(EXIT_SUCCESS);
}
if (strcmp(argv[1], "-u") == 0) { /* support -u option for SUS compat */
cp++; /* increment the argv pointer var */
}
for (; cp <= argc - 1; cp++) { /* for each file in cli args */
write_file(argv[cp], argv[0]); /* print the file */
}
exit(EXIT_SUCCESS);
}
-
and-u
arguments; you could start by reading the POSIX spec. "fast and small", does that mean with or without linked libraries? \$\endgroup\$-
would be pretty necessary though. Are you stuck with an ancient compiler? \$\endgroup\$printf("%c", ch);
->fputc(ch, fp);
orputchar(ch);
\$\endgroup\$short int ch;
shoud beint ch;
EOF
is not guaranteed to fit in ashort
\$\endgroup\$