/* SSRC - byte-swap sequential record counts in Fortran unformatted files.
 * Do NOT swap the data in the records.
 * This program assumes that Fortran sequential unformatted files have the
 * form  <count> <data> <count>
 * where <count> is a 4-byte integer equal to the number of bytes in <data>.
 * This program assumes that "int" is a 32-bit type.  Lahey and similar
 * PC Fortrans usually limit record sizes to 2 GB, so a 32-bit int suffices
 * to handle all cases.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ------------------------------------------------------------------ */
/* SWAPINT - byte-swap the input value.
 */
int swapint(int val)
{
    int loc = val;
    unsigned char *c = (unsigned char *) &loc;
    unsigned char temp;

    temp = c[3];
    c[3] = c[0];
    c[0] = temp;
    temp = c[2];
    c[2] = c[1];
    c[1] = temp;
    return(loc);
}
/* ------------------------------------------------------------------ */
/* RWCOUNT - read, swap, and write the record byte count.
 * Return value is:
 *  >= 0 : the record size in bytes
 *  -1: EOF on input
 *  -2: input error
 *  -3: negative header count value
 *  -4: error writing to outbuf
 */
int rwcount(FILE *inbuf, FILE *outbuf)
{
    int count;
    size_t nrds = fread(&count, sizeof(count), 1, inbuf);

    if(nrds != 1)   /* oops; perhaps EOF */
       return(feof(inbuf) ? -1 : -2);
    count = swapint(count);
    if(count < 0)
       return(-3);
    return(fwrite(&count, sizeof(count), 1, outbuf) == 1 ? count : -4);
}
/* ------------------------------------------------------------------ */
/* PROCESS - process an input file.
 * Return value is 0 if no errors occurred.
 * Otherwise print a descriptive diagnostic and return a nonzero value.
 */
int process(const char *infn, const char *outfn)
{
    FILE *inbuf, *outbuf;
    size_t recno = 1;
    int count;
    
    if(strcmp(infn, "-") == 0)  {
       inbuf = stdin;
       infn = "standard input";
    } else if((inbuf = fopen(infn, "rb")) == NULL) {
       perror(infn);
       return(-1);
    }
    if(outfn == NULL)  {
       outbuf = stdout;
       outfn = "standard output";
    } else if((outbuf = fopen(outfn, "wb")) == NULL) {
       perror(outfn);
       return(-1);
    }
    while((count = rwcount(inbuf, outbuf)) >= 0)  {
       unsigned char *buf = malloc(count);
       if(buf == NULL)  {
          fprintf(stderr, "%s: memory allocation failed on record %lu\n",
             infn, (unsigned long) recno);
          return(1);
       }
       if(fread(buf, 1, count, inbuf) != count)  {
          fprintf(stderr, "%s: unexpected short record %lu\n", 
             infn, (unsigned long) recno);
          return(1);
       }
       if(fwrite(buf, 1, count, outbuf) != count)  {
          fprintf(stderr, "%s: error writing record %lu\n",
             outfn, (unsigned long) recno);
          return(1);
       }
       if(rwcount(inbuf, outbuf) != count)  {
          fprintf(stderr, "%s: record markers don't match, record %lu\n",
             infn, (unsigned long) recno);
          return(1);
       }
       free(buf);
       recno++;
    }
    switch(count)  {
    case -3:
       fprintf(stderr, "%s: negative record size at record %lu\n",
          infn, (unsigned long) recno);
       return(1);
    case -2:
       fprintf(stderr, "%s: error reading record length, record %lu\n",
          infn, (unsigned long) recno);
       return(1);
    case -1:
       return(0);   /* normal return: EOF on input */
    default:
       fprintf(stderr, "%s: errro reading record %lu\n",
          infn, (unsigned long) recno);
       return(1);
   }
}
/* ------------------------------------------------------------------ */
int main(int argc, char **argv)
{
   if(argc == 1)
      return(process("-", NULL));
   else
      return(process(argv[1], NULL));
}