Trim trailing lines and remove blank lines in C
Well designed code.




  I am a bit suspicious of that realloc idiom for reallocing the buffer if it gets full. Is it right?


I see no errors.

Nit: I'd rather see newsize declared locally.  It is only used there.

// newsize = size + BUFSIZ;
size_t newsize = size + BUFSIZ;




Other

Unneeded code

The initial allocation is not needed.  realloc() in the loop can handle starting from 0

//size = BUFSIZ;
//if (buf == NULL)
//    if ((buf = malloc(size)) == NULL)
//      err(EXIT_FAILURE, "malloc");
size = 0;


Perhaps there is a concern about ...

fwrite(NULL, 1, 0, stdout); 


... yet given the spec "If size or nmemb is zero, fwrite returns zero and the state of the stream remains unchanged.", I see no issue.

Either way I'd consider the micro-optimization below, just for the sake of avoiding the fwite() function call as i==0 will be quite common.  (Usually I do not encourage any micro-op - I'll make an exception here)

// fwrite(buf, 1, i, stdout);
if (i &gt; 0) fwrite(buf, 1, i, stdout);


Checking output

Code does not check the return values of fwrite(), putchar(c).  Robust code would and report the problem - even if it is rare.

Missing free()

Free the allocation, else trim() leaks memory.

free(buf);


ferror()

Good that code checks this.

main() always reports success

main() employs the implied return 0;.  Yet failure is possible.

Given code has exitval = EXIT_FAILURE;, I'd expect main() to return exitval; in the end.

Initialize

Rather than declare then assign lines later, consider declare with initialization.

// int status;
// ...
// status = BEG;
int status = BEG;

// size_t size;
// ...
// size = BUFSIZ;
size_t size = BUFSIZ;


Same for i.