Linux cat command in C
Use Standard Constants When You Can  

The stdio.h header file contains a definition of BUFSIZ. This is a good constant to use for input and output buffers.

In the stdlib.h header file are 2 constants that are useful when calling exit or returning from main, these are EXIT_FAILURE and EXIT_SUCCESS. These constants are universal, even if the underlying operating system expects different numbers for success or failure this constants will provide the correct value.

DRY Code
There is a programming principle called the Don't Repeat Yourself principle. The principle is this, if you find yourself repeating code, it might be better to create a function or sub-program and call that function where the code is repeated. This reduces the amount of code in a module and makes it easier to read. What is more beneficial than the readability is the fact that you only have to write and debug that code once. Another benefit of puting repeating code into a function is that if the code needs to be changed, there is only one place to change it.
.

Code that repeats a lot in this program:

    printf("     %d  %s", index, buffer);
    index++;


While this is only one line of code, making a function out of it reduces the possibility of errors such as missing spaces or too many spaces.

All of the output could be in their own functions:

void formattedLineOut(int *index, char buffer[])
{
    printf("     %d  %s", *index, buffer);
    *index++;
}


The function formattedLineOut is receiving a reference to index rather than
the value. This way the function doesn't have to have a return value for
index.

void bprint(int *index, char buffer[])
{
    if (strcmp(s, "\n") == 0) {
        printf(" %s", s);
    }
    else {
        formattedLineOut(index, s);
    }
}

void outputLine(int *index, char buffer[], int nflag, int bflag)
{
    if (nflag)
    {
        formattedLineOut(index, buffer);
    }
    else if (bflag)
    {
        bprint(index, buffer);
    }
    else
    {
        printf("%s", buffer);
    }
}

int readFile(char* filename, FILE* fp, int index, int bflag, int nflag) {
    char ch;
    char s[BUF_SIZE];
    if (fp == NULL) { //in case the file doesn't exist
        printf("%s: No such file or directory\n", filename);
        exit(1);
    }

    while ((fgets(s, BUF_SIZE, fp))) {
        outputLine(&amp;index, s, bflag, nflag)
    }

    return index;
}


Match fclose() With fopen()  

The function readFile has multiple calls to fclose(). This can lead to bugs,
it might be better to call fclose() after readFile returns in the calling
function. This will also allow readFile to handle stdin, since stdin is a file pointer.