C - getting filenames from stdin/stdout for use in getopt

  • Thread starter Enharmonics
  • Start date
  • Tags
    Unix
In summary: Returns:array of PIXELs containing the uncompressed 24-bit bmp from filename.*/#endif /* BMPLIB_H */The .c source file:#include <stdio.h>#include <stdlib.h>#include <string.h>#include "bmptool.h"BITMAPFILEHEADER bfh;BITMAPINFOHEADER bih;void main(){int rows, cols, bitmap;if
  • #1
Enharmonics
29
2

Homework Statement



The assignment I'm doing involves manipulating an image stored in a 24-bit uncompressed BMP file. We were provided with two pre-written files (a header and a .c source file) that contain functions that read and write the image file, as well as a flip() function (mirrors the image) that was meant as an example (so we could better understand the way to manipulate the image).

The main point of the assignment was to write two functions, enlarge() and rotate(), which enlarge the image by some integer scale factor or rotate the image clockwise or counterclockwise by some degree. I've got that part done already. My program works perfectly, except for this (excerpt from the instructions):

The program should take the follow command-line options:

% bmptool [-s scale | -r degree | -f ] [-o output_file] [input_file]

Where -s means to scale the image by the given 'scale' (a positive integer: 1, 2, 3, ...), -r means to rotate the image by 'degree' (which must be a multiple of 90, clockwise if positive, counter-clockwise if negative), and -f means to flip the image horizontally.

You are required to use getopt() to process the command-line.

If '-o output_file' is missing, use standard output. If 'input_file' is missing, use standard input. The options to use standard input or standard output will only be used when chaining commands. Make sure the program returns 0 on success. In that case, one can 'chain' the commands using pipes, like:

% bmptool -s 4 1.bmp | bmptool -r -90 | bmptool -f -o 2.bmp

What I'm confused about is the italicized bit. I asked my professor, and she clarified some things:

1. The first command in a chain MUST provide an input file name, and the last command in a chain MUST provide an output file name.

2. If a command does not provide an output file name, then "the output goes to stdout as a binary stream", and "if it goes to stdout as a binary stream, the next one [here I presume she means the next command] in the chain can then read it from stdin" [sic].

The trouble is, I don't know how to do this or even understand how it works. This was never explained in class, and my textbook doesn't really cover how it works either (I guess because it's not really a C-specific thing).

The first thing I don't understand is how I could write something to stdout before the first iteration (i.e. the first command in the chain) of my program terminates, then read that same something in from stdin on the next iteration (i.e. the next command). I've been doing some Googling, and I managed to find this thread:

https://stackoverflow.com/questions...-reading-from-stdout-unix-linux-c-programming

In which multiple users seem to echo the sentiment that stdin and stdout are in fact "the same file descriptor", or "point to the same place", such that writing something to stdout would store that something in the same place that stdin would try to read from (a buffer of some kind)? Is that correct?

Another thing I don't understand is what function(s) I'm supposed to use to make the output "go to stdout as a binary stream". Our code is meant to represent the image as an array of PIXELs, which are a pre-defined struct of unsigned chars (see the header file for struct definition).

Is it possible to write an array of structs to stdout/read it from stdin? If so, how? I can't find any functions in the C documentation that can do this. I don't even know where to look.

Homework Equations

The Attempt at a Solution



I'll include my program's main() (where the getopt parsing happens, as well as file writing and so on) just in case anyone wants to see it. I've left out a significant chunk of the code where the enlarge(), rotate(), and flip() functions are defined since they're working properly and not really relevant to the problem at hand anyway.

I'll go ahead and toss in the code from the header file my professor provided as well.

The header file:

Code:
#ifndef BMPLIB_H
#define BMPLIB_H

typedef struct {
  unsigned short padding;
  unsigned short bfType;
  unsigned int bfSize;
  unsigned short bfReserved1;
  unsigned short bfReserved2;
  unsigned int bfOffBits;
} BITMAPFILEHEADER;

typedef struct {
  unsigned int biSize;
  int biWidth;
  int biHeight;
  unsigned short biPlanes;
  unsigned short biBitCount;
  unsigned int biCompression;
  unsigned int biSizeImage;
  unsigned int biXPelsPerMeter;
  unsigned int biYPelsPerMeter;
  unsigned int biClrUsed;
  unsigned int biClrImportant;
} BITMAPINFOHEADER;

typedef struct {
  unsigned char r;
  unsigned char g;
  unsigned char b;
} PIXEL;

#define DEFAULT_BITMAP_OFFSET 1078

/* Read an uncompressed 24-bit bmp from a file named 'filename' (if
   null, it's standard input); return the number of 'rows', the number
   of 'cols', and the 'bitmap' as an array of PIXELs. The function
   return 0 if successful. */
int readFile (char* filename, int* rows, int* cols, PIXEL** bitmap);/* Write an uncompressed 24-bit bmp to a file named 'filename' (if
   null, it's standard output); the dimension of the bmp is the number
   of 'rows' by the number of 'cols', and the 'bitmap' contains an
   array of PIXELs.  The function return 0 if successful. */
int writeFile (char* filename, int rows, int cols, PIXEL* bitmap);

/* Read bmp file header from file 'fd', return the number of 'rows',
   the number of 'cols', and the 'start' position of the bitmap. The
   function returns 0 if successful. */
int readHeader(int fd, int *rows, int *cols, unsigned int *start);

/* Write bmp file header to file 'fd'; the dimention of the bitmap is
   the number of 'rows' by the number of 'cols', and it starts at the
   'start' position. The function returns 0 if successful. */
int writeHeader(int fd, int rows, int cols, unsigned int start);

/* Read the 'bitmap' from file 'fd'; the dimention of the bitmap is
   the number of 'rows' by the number of 'cols', and it starts at the
   'start' position. The function returns 0 if successful. */
int readBits(int fd, PIXEL* bitmap, int rows, int cols, unsigned int start);

/* Write the 'bitmap' to file 'fd'; the dimention of the bitmap is the
   number of 'rows' by the number of 'cols', and it starts at the
   'start' position. The function returns 0 if successful. */
int writeBits(int fd, int rows, int cols, PIXEL* bitmap, unsigned int start);

#endif /*BMPLIB_H*/

My main():

Code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bmplib.h"

#define STRING_SIZE 100

// The flags for case sensitivity
// and an output file. Declared
// as globals so they can be used
// in auxiliary functions without
// being passed

int sflag = 0, rflag = 0, fflag = 0, oflag = 0;

// These are intended to represent the boolean
// values true and false (there's no native bool
// data type in C, apparently)

const int TRUE = 1;
const int FALSE = 0;

// Type alias for the bool type

typedef int bool;

// ----- FUNCTION PROTOTYPES -----

int enlarge(PIXEL* original, int rows, int cols, int scale,
            PIXEL** new, int* newrows, int* newcols);

int rotate(PIXEL* original, int rows, int cols, int rotation,
           PIXEL** new, int* newrows, int* newcols);

int main(int argc, char **argv)
{

    // GETOPT CODE FROM CLASS, MODIFIED
    // TO FIT ASSIGNMENT SPECIFICATIONS

    extern char *optarg;
    extern int optind;
    int c, err = 0;    // Stores the multiplier for the scale option

    int scale = 0;

    // Stores the degree value for the degree option

    int degrees = 0;

    // These arrays will store the input/output file
    // directories

    char inFileDirectory[STRING_SIZE];
    char outFileDirectory[STRING_SIZE];
   
    // This will be used when validating the input/output
    // files
    bool done = FALSE;
   
    // Input/output files that will be validated
    FILE *inFile;
    FILE *outFile;

    static char usage[] = "Usage: %s [-s scale | -r degree | -f] [-o output_file] [input_file]\n";

    while ((c = getopt(argc, argv, "s:r:fo:")) != -1)
        switch (c) {
            case 's':
                sflag = 1;

                // If a scale value was entered,
                // convert it to an integer and
                // store it in scaleValue
                   scale = atoi(optarg);
                break;
            case 'r':
                rflag = 1;

                // If a degree value was entered,
                // convert it to an integer and store
                // it in degrees
                    degrees = atoi(optarg);
                break;
            case 'f':
                fflag = 1;
                break;
            case 'o':
                oflag = 1;

                // If an output file name
                // was entered, copy it
                // to fileDirectory
                if (optarg != NULL)
                {
                    strcpy(outFileDirectory, argv[optind]);
                   
                    // Attempt to open the output file
                    // immediately
                   
                    //outFile = fopen(outFileDirectory, "w+");
                }

                // Otherwise, no output file name
                // was entered, so use standard output
                // as per assignment instructions
                else
                {

                }
                break;
            default:
                err = 1;
                break;
        }

    if (err) {
        // Generic error message

        printf("ERROR: Invalid input.\n");

        fprintf(stderr, usage, argv[0]);
        exit(1);
    }

    // ----- BMPTOOL CODE STARTS HERE -----
   
    // If argv[optind] isn't NULL, that means the user
    // entered an input file as an option, so copy it into inFileDirectory
   
    if (argv[optind] != NULL)
    {
        strcpy(inFileDirectory, argv[optind]);
    }

    // Otherwise, the user didn't enter an input file name, so use
    // standard input as per assignment instructions

    /*

    // Otherwise, use standard input to get the input file name
    else
    {
        // Do/while loop to validate the input file
        do
        {
            printf("Please enter the directory of your BMP file: ");
            fgets(inFileDirectory, STRING_SIZE, stdin);
           
            // Attempt to open the input file
            inFile = fopen(inFileDirectory, "r+");
           
            // If the file wasn't opened successfully,
            // notify the user
       
            if (!inFile)
            {
                printf("Invalid file directory or file does not exist.\n");
            }
           
            // Otherwise, we have a valid input file, so terminate the loop
            else
            {
                done = TRUE;
            }
        } while (!done);
    }
   
    // Check if the outFile was successfully
    // opened within getopt -o option handling
   
    if (!outFile)
    {
        // If not, prompt the user for input
        // until we have a valid output file
        do
        {   
            printf("Please enter the directory to output a BMP file to:");
            fgets(outFileDirectory, STRING_SIZE, stdout);
           
            // Attempt to open the output file
            outFile = fopen(outFileDirectory, "w+");
           
            if (!outFile)
            {
                printf("Invalid file directory.\n");
            }
           
            // Otherwise, we have a valid output file, so terminate the loop
            else
            {
                done = TRUE;
            }
        } while (!done);
    }

     */
   
    // Original row and columns
    int r, k;
   
    // Original and new bmp image pointers
    PIXEL *b, *nb;

    // Scale only

    if (sflag && !rflag && !fflag)
    {
        // New row and columns. Given as pointers because they're pointers in the
        // declaration of enlarge and rotate
        int nr, nk;

        // Read in the image file
        readFile("example.bmp", &r, &k, &b);
       
        // Scale the image
        enlarge(b, r, k, scale, &nb, &nr, &nk);
       
        // Write the scaled image file
        writeFile("result.bmp", nr, nk, nb);
    }
   
    // Rotate only

    if (rflag && !sflag && !fflag)
    {
        int nr, nk;
       
        // Read in the image file
        readFile("example.bmp", &r, &k, &b);
   
        // Rotate the image
        rotate(b, r, k, degrees, &nb, &nr, &nk);
       
        // Write the rotated image file
        writeFile("result.bmp", nr, nk, nb);
    }

    // Flip only
   
    if (fflag && !sflag && !rflag)
    {
        // Read in the image file
        readFile("example.bmp", &r, &k, &b);
       
        // Flip the image
        flip(b, &nb, r, k);
       
        // Write the flipped image file
        writeFile("result.bmp", r, k, nb);
       
        return 0;
    }
   
    // Scale and rotate only
   
    if (sflag && rflag && !fflag)
    {
        int nr, nk;
       
        // First, perform the scaling
       
        readFile("example.bmp", &r, &k, &b);
        enlarge(b, r, k, scale, &nb, &nr, &nk);
        writeFile("result.bmp", nr, nk, nb);
       
        // Now perform the rotation
       
        readFile("result.bmp", &r, &k, &b);
        rotate(b, r, k, degrees, &nb, &nr, &nk);
        writeFile("result.bmp", nr, nk, nb);
       
        return 0;
    }
   
    // Scale and flip only
   
    if (sflag && fflag && !rflag)
    {
        int nr, nk;
       
        // First, perform the scaling
        readFile("example.bmp", &r, &k, &b);
        enlarge(b, r, k, scale, &nb, &nr, &nk);
        writeFile("result.bmp", nr, nk, nb);
       
        // Next, perform the flip
        readFile("result.bmp", &r, &k, &b);
        flip(b, &nb, r, k);
        writeFile("result.bmp", r, k, nb);
       
        return 0;
    }

    // Rotate and flip only
   
    if (rflag && fflag && !sflag)
    {
        int nr, nk;
       
        // First, perform the rotation
        readFile("example.bmp", &r, &k, &b);
        rotate(b, r, k, degrees, &nb, &nr, &nk);
        writeFile("result.bmp", r, k, nb);
       
        // Now perform the flip
        readFile("result.bmp", &r, &k, &b);
        flip(b, &nb, r, k);
        writeFile("result.bmp", r, k, nb);
       
        return 0;
    }
   
    // Scale, rotate and flip
   
    if (sflag && rflag && fflag)
    {
        int nr, nk;

        // First, perform the scaling

        readFile("example.bmp", &r, &k, &b);
        enlarge(b, r, k, scale, &nb, &nr, &nk);
        writeFile("result.bmp", nr, nk, nb);

        // Now perform the rotation

        readFile("result.bmp", &r, &k, &b);
        rotate(b, r, k, degrees, &nb, &nr, &nk);
        writeFile("result.bmp", nr, nk, nb);

        // Finally, perform the flip

        readFile("result.bmp", &r, &k, &b);
        flip(b, &nb, r, k);
        writeFile("result.bmp", r, k, nb);

        return 0;
    }

    free(b);
    free(nb);

    return 0;

}
 
Physics news on Phys.org
  • #2
You can write the bitmap char by char to stdout and also read it from there.
If used in a chain, the first program will just push pixel by pixel to stdout which is stdin for the next program. If you push a file to stdout you can close the program after the whole file has been written there. If you read a file from stdin you can process it as soon as reading the input (the bitmap) from stdin is done.

This is not C-specific, this is a very useful feature of shells. You can pass the output of one program directly to the next one without storing it in a file in between. This also means your program doesn't have to do more than one operation at a time.
 
  • #3
mfb said:
You can write the bitmap char by char to stdout and also read it from there.
If used in a chain, the first program will just push pixel by pixel to stdout which is stdin for the next program. If you push a file to stdout you can close the program after the whole file has been written there. If you read a file from stdin you can process it as soon as reading the input (the bitmap) from stdin is done.

This is not C-specific, this is a very useful feature of shells. You can pass the output of one program directly to the next one without storing it in a file in between. This also means your program doesn't have to do more than one operation at a time.

So I was reading through the implementation of the read/write methods given by my professor to see if maybe there was a clue there that could help me figure out how to do this. Here's the code:

Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include "bmplib.h"

// THIS METHOD IS NOT NEEDED FOR THE ASSIGNMENT

static int myread(int fd, char* buf, unsigned int size)
{
  int r = 0;
  while(r < size) {
    int x = read(fd, &buf[r], size-r);
    if(x < 0) return x;
    else r += x;
  }
  return size;
}

// THIS METHOD IS NOT NEEDED FOR THE ASSIGNMENT

static int mywrite(int fd, char* buf, unsigned int size)
{
  int w = 0;
  while(w < size) {
    int x = write(fd, &buf[w], size-w);
    if(x < 0) return x;
    else w += x;
  }
  return size;
}

int readFile (char *filename, int *rows, int *cols, PIXEL** bitmap)
{
  int fd, ret;
  unsigned int start;
 
  if(filename) {
    if((fd = open(filename, O_RDONLY)) < 0) {
      perror("Can't open bmp file to read");
      return -1;
    }
  } else fd = STDIN_FILENO;

  ret = readHeader (fd, rows, cols, &start);
  if(ret) return ret;

  *bitmap = (PIXEL*)malloc(sizeof(PIXEL)*(*rows)*(*cols));
  ret = readBits (fd, *bitmap, *rows, *cols, start);
  if(ret) return ret;

  if(filename) close(fd);

  return 0;
}

int writeFile (char* filename, int rows, int cols, PIXEL* bitmap)
{
  int fd, ret;
  unsigned int start = DEFAULT_BITMAP_OFFSET;

  if(filename) {
    if((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) {
      perror("Can't open bmp file to write");
      return -1;
    }
  } else fd = STDOUT_FILENO;

  ret = writeHeader (fd, rows, cols, start);
  if(ret) return ret;

  ret = writeBits(fd, rows, cols, bitmap, start);
  if(ret) return ret;

  if(filename) close(fd);

  return 0;
}

int readHeader(int fd, int *rows, int *cols, unsigned int *start)
{
  BITMAPFILEHEADER bmfh;
  BITMAPINFOHEADER bmih;

  if(myread(fd, ((char*)&bmfh)+2, sizeof(bmfh)-2) <= 0) {
    perror("Can't read BITMAPFILEHEADER");
    return -2;
  }
  if(myread(fd, (char*)&bmih, sizeof(bmih)) <= 0) {
    perror("Can't read BITMAPINFOHEADER");
    return -3;
  }

  if(bmih.biCompression != 0) {
    fprintf(stderr, "Can't read compressed bmp");
    return -4;
  }
  if(bmih.biBitCount != 24) {
    fprintf(stderr, "Can't handle bmp other than 24-bit");
    return -5;
  }

  *rows = bmih.biHeight;
  *cols = bmih.biWidth;
  *start = bmfh.bfOffBits;

  return 0;
}

int writeHeader(int fd, int rows, int cols, unsigned int start)
{
  unsigned int fileSize;
  unsigned int headerSize;
  unsigned int paddedCols;
  BITMAPFILEHEADER bmfh;
  BITMAPINFOHEADER bmih;

  memset (&bmfh, 0, sizeof(bmfh));
  memset (&bmih, 0, sizeof(bmih));

  paddedCols = ((cols/4)*4 !=cols ? ((cols+4)/4)*4 : cols);
  headerSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
  fileSize = rows*paddedCols*sizeof(PIXEL)+headerSize;

  bmfh.bfType = 0x4D42;
  bmfh.bfSize = fileSize;
  bmfh.bfReserved1 = 0;
  bmfh.bfReserved2 = 0;
  bmfh.bfOffBits = start;

  bmih.biSize = 40;
  bmih.biWidth  = cols;
  bmih.biHeight = rows;
  bmih.biPlanes = 1;
  bmih.biBitCount = 24;
  bmih.biCompression = 0;
  bmih.biSizeImage = 0;
  bmih.biXPelsPerMeter  = 0;
  bmih.biYPelsPerMeter = 0;
  bmih.biClrUsed = 0;
  bmih.biClrImportant = 0;

  if(mywrite(fd, ((char*)&bmfh)+2, sizeof(bmfh)-2) < 0) {
    perror("Can't write BITMAPFILEHEADER");
    return -2;
  }
  if(mywrite(fd, (char*)&bmih, sizeof(bmih)) < 0) {
    perror("Can't write BITMAPINFOHEADER");
    return -3;
  }

  return 0;
}

int readBits(int fd, PIXEL* bitmap, int rows, int cols, unsigned int start)
{
  int row;
  char padding[3];
  int padAmount;
  char useless[DEFAULT_BITMAP_OFFSET];

  padAmount = ((cols * sizeof(PIXEL)) % 4) ? (4 - ((cols * sizeof(PIXEL)) % 4)) : 0;

  start -= sizeof(BITMAPFILEHEADER)-2+sizeof(BITMAPINFOHEADER);
  if(start > 0 && myread(fd, useless, start) < 0) {
    perror("Can't lseek to bitmap");
    return -6;
  }
  /*
  if(lseek (fd, start, SEEK_SET) < 0) {
    perror("Can't lseek to bitmap");
    return -6;
  }
  */

  for (row=0; row < rows; row++) {
    if(myread(fd, (char*)(bitmap+(row*cols)), cols*sizeof(PIXEL)) < 0) {
      perror("Can't read bitmap");
      return -7;
    }
    if(padAmount > 0) {
      if(myread(fd, padding, padAmount) < 0) {
    perror("Can't read bitmap");
    return -8;
      }
    }
  }

  return 0;
}
 
int writeBits(int fd, int rows, int cols, PIXEL* bitmap, unsigned int start)
{
  int row;
  char padding[3];
  int padAmount;
  char useless[DEFAULT_BITMAP_OFFSET];

  padAmount = ((cols * sizeof(PIXEL)) % 4) ? (4 - ((cols * sizeof(PIXEL)) % 4)) : 0;
  memset(padding, 0, 3);

  start -= sizeof(BITMAPFILEHEADER)-2+sizeof(BITMAPINFOHEADER);
  if(start > 0) {
    memset(useless, 0, start);
    if(mywrite(fd, useless, start) < 0) {
      perror("Can't lseek to bitmap");
      return -6;
    }
  }
  /*
  if(lseek (fd, start, SEEK_SET) < 0) {
    perror("Can't lseek to bitmap");
    return -6;
  }
  */

  for (row=0; row < rows; row++) {
    if(mywrite(fd, (char*)(bitmap+(row*cols)), cols*sizeof(PIXEL)) < 0) {
      perror("Can't write bitmap");
      return -7;
    }
    if(padAmount > 0) {
      if(mywrite(fd, padding, padAmount) < 0) {
    perror("Can't write bitmap");
    return -8;
      }
    }
  }

  return 0;
}

Since readFile and writeFile are the only functions that are explicitly called when reading/writing the image file (the other methods, i.e. readBits, writeBits, readHeader, writeHeader, etc appear to be auxiliary methods that readFile and writeFile call to actually do the reading and writing), I started with those. While I was reading through the code, I noticed a few lines (I'll bold them below) which I found particularly interesting:

Code:
int readFile (char *filename, int *rows, int *cols, PIXEL** bitmap)
{
  int fd, ret;
  unsigned int start;
 
[B]  if(filename) {
    if((fd = open(filename, O_RDONLY)) < 0) {
      perror("Can't open bmp file to read");
      return -1;
    }
  } else fd = STDIN_FILENO;[/B]

  ret = readHeader (fd, rows, cols, &start);
  if(ret) return ret;

  *bitmap = (PIXEL*)malloc(sizeof(PIXEL)*(*rows)*(*cols));
  ret = readBits (fd, *bitmap, *rows, *cols, start);
  if(ret) return ret;

  if(filename) close(fd);

  return 0;
}

int writeFile (char* filename, int rows, int cols, PIXEL* bitmap)
{
  int fd, ret;
  unsigned int start = DEFAULT_BITMAP_OFFSET;

 [B] if(filename) {
    if((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) {
      perror("Can't open bmp file to write");
      return -1;
    }
  } else fd = STDOUT_FILENO;[/B]

  ret = writeHeader (fd, rows, cols, start);
  if(ret) return ret;

  ret = writeBits(fd, rows, cols, bitmap, start);
  if(ret) return ret;

  if(filename) close(fd);

  return 0;
}

The headers of these functions explain in part why I've been so confused about how to do this. Basically, these functions are supposed to take filenames (in the form of C-strings) as arguments, but what I'd be writing stdout/reading from stdin would be an array of pixels (each of which is a struct comprised of unsigned char members).

However, unless I'm wrong, the if/else statements in bold make it so I don't actually have to write to stdout or read from stdin explicitly. If, for example, filename (the char array) is uninitialized when I pass it to readFile, then the

Code:
if (filename)

would fail and the else would execute:

Code:
else fd = STDIN_FILENO;

And since STDIN_FILENO is the file descriptor of standard input, it seems like the reading process would continue with stdin as the directory that's being read (and a similar argument would hold for writeFile and STDOUT_FILENO)? Is that correct?

========> EDIT : I went ahead and tested my theory by changing a single one of my methods (the one for scaling an image, enlarge()) to simply use the inFileDirectory and outFileDirectory char arrays that would normally store the input and output file directories parsed from getopt.

Changed main() below (new code written in bold):

Code:
int main(int argc, char **argv)
{
    extern char *optarg;
    extern int optind;
    int c, err = 0;

    // Stores the multiplier for the scale option
    int scale = 0;

    // Stores the degree value for the degree option
    int degrees = 0;

    // These arrays will store the input/output file
    // directories
    char inFileDirectory[STRING_SIZE];
    char outFileDirectory[STRING_SIZE];
  
    // Input/output files that will be validated
    FILE *inFile;
    FILE *outFile;

    static char usage[] = "Usage: %s [-s scale | -r degree | -f] [-o output_file] [input_file]\n";

    while ((c = getopt(argc, argv, "s:r:fo:")) != -1)
        switch (c) {
            case 's':
                sflag = 1;

                // If a scale value was entered,
                // convert it to an integer and
                // store it in scaleValue
                   scale = atoi(optarg);
                break;
            case 'r':
                rflag = 1;

                // If a degree value was entered,
                // convert it to an integer and store
                // it in degrees
                    degrees = atoi(optarg);
                break;
            case 'f':
                fflag = 1;
                break;
            case 'o':
                oflag = 1;

                // If an output file name
                // was entered, copy it
                // to fileDirectory
                if (optarg != NULL)
                {
                    strcpy(outFileDirectory, argv[optind]);
                }
                break;
            default:
                err = 1;
                break;
        }

    if (err) {
        // Generic error message

        printf("ERROR: Invalid input.\n");

        fprintf(stderr, usage, argv[0]);
        exit(1);
    }

    // ----- BMPTOOL CODE STARTS HERE -----
  
    // If argv[optind] isn't NULL, that means the user
    // entered an input file as an option, so copy it into inFileDirectory
    if (argv[optind] != NULL)
    {
        strcpy(inFileDirectory, argv[optind]);
    }
  
    // Original row and columns
    int r, k;

    // New row and columns (to be used w/ enlarge function)
    int nr, nk;
  
    // Original and new bmp image pointers
    PIXEL *b, *nb;

    // Scale only

    if (sflag && !rflag && !fflag)
    {
        // Read in the image file
        //readFile("example.bmp", &r, &k, &b);
       [B] readFile(inFileDirectory, &r, &k, &b);[/B]
      
        // Scale the image

        // The enlarge function can fail if the scale
        // provided is not a positive integer; in that
        // case, we prompt error (return -1)

        if (enlarge(b, r, k, scale, &nb, &nr, &nk) == -1)
        {
            return -1;
        }
      
        // Write the scaled image file
        //writeFile("result.bmp", nr, nk, nb);
        [B]writeFile(outFileDirectory, nr, nk, nb);[/B]
    }

// Everything else is the same

}

To my surprise, not only did this NOT completely break the program, it actually almost worked... kind of.

For your convenience, the command line usage again:

% bmptool [-s scale | -r degree | -f ] [-o output_file] [input_file]

So, first I tested a single command that contained both an input file name AND an output file name:

./bmptool -s 2 -o 2.bmp 1.bmp

This executed with no problems; however, instead of outputting the modified BMP image to a new file called 2.bmp, it just overwrote 1.bmp (which is the input file).

Next, I tested chained commands:

./bmptool -s 2 1.bmp | ./bmptool -o 2.bmp

Executing this resulted in a Segmentation Fault. Interestingly, a new file WAS created as a result of executing this, but it wasn't named 2.bmp (like it was supposed to be) and instead of containing the modified BMP image, it just contained garbage text (weird symbols, letters, etc).

I'm not sure why either of these things is happening. I've written several programs that successfully took an output and input file placed directly next to each other as in

./bmptool -s 2 -o 2.bmp 1.bmp

And I've never had it overwrite the input file instead of creating a new file with the name specified by the output file option before.

As for the segmentation fault, I have no idea why it's happening.
 
Last edited:
  • #4
Did you check if outFileDirectory is set properly?
Garbage text is what you get if you look at a bmp file in a text editor.
 

What is the purpose of using getopt in C?

The getopt function in C allows for command-line arguments to be passed to a program in a structured and organized manner. This makes it easier for the program to process and respond to the arguments.

How do I get filenames from standard input and output for use in getopt?

In order to get filenames from standard input and output for use in getopt, you can use the fopen function to open a file and then use the resulting file pointer as an argument in the getopt function.

Can I use getopt for both reading and writing files?

Yes, getopt can be used for both reading and writing files. By using the fopen function to open a file with the appropriate mode (e.g. "r" for reading or "w" for writing), you can then pass the file pointer to getopt to process the file.

What happens if I do not provide the necessary arguments for getopt?

If you do not provide the necessary arguments for getopt, the program will typically display an error message and terminate. It is important to ensure that all required arguments are provided when using getopt.

Are there any alternative methods to using getopt for handling command-line arguments?

Yes, there are alternative methods for handling command-line arguments in C, such as using the argc and argv parameters in the main function or using the getopt_long function for more complex argument parsing. However, getopt is a commonly used and efficient method for handling command-line arguments.

Similar threads

  • Engineering and Comp Sci Homework Help
Replies
5
Views
2K
  • MATLAB, Maple, Mathematica, LaTeX
Replies
4
Views
3K
Back
Top