tadm123
tadm123

Reputation: 8793

C, using argv[] for command line arguments

I'm having trouble with this program, it's working perfectly when I use stdin but when I modify it to get characters from the command line it doesn't. I know I'm doing something wrong but don't know what, any help would be greatly appreciated.

Description and code:

/* Program prints the date in this form: September 13, 2010
    allow the user to  enter date in either 9-13-2010 or 9/13/2010  
    format, otherwise print 'error'  */

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

char *month(int m)
{
    char *months[]={"January","February","March","April","May",
                "June", "July","August","September","October",
                "November","December"};
    return months[m-1];
}

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

    int m=0,d=0,y=0;

    FILE *fp;

    if((fp=fopen(argv[1],"rb")) == NULL)
    {
        fprintf(stderr,"Couldn't open the file. ");
        exit(EXIT_FAILURE);
    }


    printf("Type a date (mm-dd-yyyy) or (mm/dd/yyyy): \n");

    if(fscanf(fp,"%d%*[/-]%d%*[/-]%d",&m,&d,&y) != 3)  //store characters in variables
        {
            fprintf(stderr, "Not properly formatted.");
            exit(EXIT_FAILURE);
        }

    printf("%s %2d, %4d",month(m),d,y);

    return 0;
}

Input:

01/30/1990

Output:

Couldn't open the file.

Upvotes: 1

Views: 1402

Answers (2)

Marcel
Marcel

Reputation: 3268

Here we have a generic solution. The date can be passed via file (fscanf), via command line (sscanf) or typed (scanf).

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

char* month(int m)
{
    char* months[] = { "January", "February", "March", "April", "May",
        "June", "July", "August", "September", "October",
        "November", "December" };
    return months[m - 1];
}

int main(int argc, char* argv[])
{
    int m = 0, d = 0, y = 0;

    FILE* fp;

    int wrongFormat = 0;

    if (argc > 1) 
    {
        if  ((fp = fopen(argv[1], "rb")) == NULL) 
        {
            if (sscanf(argv[1], "%d%*[/-]%d%*[/-]%d", &m, &d, &y) != 3) 
                wrongFormat = 1;
        }
        else 
        {
            if (fscanf(fp, "%d%*[/-]%d%*[/-]%d", &m, &d, &y) != 3)
                wrongFormat = 1;
        }
    }
    else 
    {
        printf("Type a date (mm-dd-yyyy) or (mm/dd/yyyy): \n");
        if (scanf("%d%*[/-]%d%*[/-]%d", &m, &d, &y) != 3)
            wrongFormat = 1;
    }

    if (wrongFormat) 
    {
        fprintf(stderr, "Not properly formatted.");
        exit(EXIT_FAILURE);
    }

    printf("%s %2d, %4d\n", month(m), d, y);

    return 0;
}

Upvotes: 0

user824425
user824425

Reputation:

I modified your program to fix the issue you were having (and to fix some undefined behaviour, or "UB" for short), but only that:

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

const char *month(int m) {
    const char const *months[] = {
        "January", "February", "March", "April",
        "May", "June", "July", "August",
        "September", "October", "November", "December",
    };

    if (1 <= m && m <= 12) {
        return months[m - 1];
    } else {
        return NULL;
    }
}

int main(int argc, char *argv[]) {
    int m = 0, d = 0, y = 0;

    if (argc == 2) {
        if (sscanf(argv[1], "%d%*[/-]%d%*[/-]%d", &m, &d, &y) != 3) {
            fprintf(stderr, "Not properly formatted.");
            exit(EXIT_FAILURE);
        }

        printf("%s %2d, %4d", month(m), d, y);
    } else {
        fprintf(stderr, "Please provide one date argument to the program, formatted as mm-dd-yyyy or mm/dd/yyyy\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}

What changed?

  • I changed the return type of month(). Its array is backed by string literals, so you shouldn't be able to accidentally modify them in future versions of your program, which would cause UB.
  • I introduced a range check in month(). It now returns a null pointer when when m is too small (e.g. 0/0/0) or too large (e.g. 25/09/2016), preventing some UB.
  • I removed all code regarding opening files. You don't want to open a file based on a file name in argv, you just want to use argv[1] as a string.
  • I introduced a check to see whether argv[1] exists. argc contains the size of argv, and if it's 2, argv contains the program's name and its first command line argument.
  • Again, you don't want to read from a file, but parse a command line argument as a string, so I changed fscanf to sscanf.

Upvotes: 2

Related Questions