Size improvements for cat reimplementation 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 &lt;stdio.h&gt;              /* we need printf, fprintf, FILE,
                         * fopen, fclose and that sort of
                         * stuff */
#include &lt;stdlib.h&gt;             /* 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 &lt; 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 &lt;= 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 &lt;stdio.h&gt;              /* we need printf, fprintf, FILE,
                         * fopen, fclose and that sort of
                         * stuff */
#include &lt;stdlib.h&gt;             /* for exit, EXIT_SUCCESS, and
                         * EXIT_FAILURE */
#include &lt;string.h&gt;             /* 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 &lt; 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 &lt;= argc - 1; cp++) {      /* for each file in cli args */
        write_file(argv[cp], argv[0]);  /* print the file */
    }

    exit(EXIT_SUCCESS);
}
Do not repeat yourself. Copying stdin doesn't differ from copying any other file. There is no need to special case stdin as you do at lines 15-21.
Avoid naked loops. Every loop does an important job that deserves a name. This is strong enough reason to factor it out as a function:
Expect errors


fgetc may fail. Once fgetc returns EOF, call feof or ferror to see what has actually happened.
printf may fail. Make sure to detect (printf returns -1) and report (analyze errno) errors.

Handle options. At least recognize a standalone - as a fake file name referring to stdin.