zsh: bus error when passing results filename to compile C program - c

In the following C program, there are different input parameters.
One of those parameters, -f is to especify the filename of the results
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define SOURCES 4
#define NUM_T 1000
char *prgname;
char usage[]="%s [-uUdipT] -f filename\n";
int main(int argc, char *argv[])
{
FILE *filefd;
char *fname;
int lt; /* lower threshold */
int ut; /* upper threshold */
int T; /* Actualization time (cells) */
double RDF, RIF; /* Rate Decrease-Increate Factor */
double PCR; /* Peak Cell Rate */
int qlen; /* queue length */
int bitEFCI[SOURCES]; /* EFCI bit */
double ACR [SOURCES]; /* Allowed Cell Rate */
double mean[SOURCES]; /* Mean Cell Rate */
int i, t; /* control variables */
double ACRacc;
prgname = strrchr(argv[0], '/');
if (prgname == (char *)NULL) prgname = argv[0];
else prgname++;
/********************************************/
/* DEFAULT VALUES & INICIALIZATION */
/********************************************/
fname = (char *)malloc(200*sizeof(char));
fname = "abr.res";
ut = 550;
lt = 450;
RDF = 0.4;
RIF = 0.02;
PCR = 150E6;
T = 70;
qlen = 0;
for (i=0; i < SOURCES; i++)
{bitEFCI[i] = 0; ACR[i] = 0.0; mean[i] = 0.0;}
/********************************************/
/* USER COMMAND LINE VALUES */
/********************************************/
while (1)
{
int c;
c = getopt(argc, argv, "u:U:d:i:p:T:f:h");
if (c == EOF) break;
switch (c)
{
case 'h': printf(usage, prgname);
fputs("-h\tHelp\n", stdout);
fputs("-u\tLower threshold (cells)\n", stdout);
fputs("-U\tUpper threshold (cells)\n", stdout);
fputs("-d\tDecrement factor\n", stdout);
fputs("-i\tIncrement factor\n", stdout);
fputs("-p\tPeak cell rate (Mbps)\n", stdout);
fputs("-T\tActualization period (cells)\n", stdout);
fputs("-f\tResults file name\n", stdout);
exit(0);
case 'u': lt = atoi(optarg);
break;
case 'U': ut = atoi(optarg);
break;
case 'd': RDF = atof(optarg);
break;
case 'i': RIF = atof(optarg);
break;
case 'p': PCR = atof(optarg);
break;
case 'T': T = atoi(optarg);
break;
case 'f': strcpy(fname, optarg);
break;
default : fprintf(stderr, usage, prgname); exit(1);
}
}
if ((filefd = fopen(fname, "w")) == NULL)
{perror("fopen"); exit(1);}
/********************************************/
/* MAIN LOOP */
/********************************************/
for (t = 1; t<= NUM_T; t++)
{
ACRacc = 0.0;
fprintf(filefd, "%d\t", t);
for (i=0; i < SOURCES; i++)
{
if (bitEFCI[i] == 0)
ACR[i] = (ACR[i]+PCR*RIF>PCR)?(PCR):(ACR[i]+PCR*RIF);
else ACR[i] *= RDF;
mean[i] += (ACR[i]-mean[i])/(double)t;
ACRacc += ACR[i];
fprintf(filefd, "%4.1f\t%4.1f\t", ACR[i]/1E6, mean[i]/1E6);
}
qlen += (int)(ACRacc*T*424/PCR);
qlen = (qlen<=T*424)?(0):(qlen - T*424);
fprintf(filefd, "%d\n", qlen/424);
for (i=0; i < SOURCES; i++)
{
if (qlen >= ut*424) bitEFCI[i] = 1;
else if (qlen < lt*424) bitEFCI[i] = 0;
}
fflush(filefd);
}
fclose(filefd);
exit(0);
}
I have compiled with
gcc -c abr1.c -Wall
gcc abr1.o -o abr1
but when I run the program
./abr1 -f "result01.res"
zsh: bus error ./abr1 -f "result01.res"
How I can pass the filename to this C program?

This is a problem:
fname = (char *)malloc(200*sizeof(char));
fname = "abr.res";
You malloc space that fname points at, then in the next line tell fname to point at the string literal "abr.res". That creates a memory leak, since you now have nothing pointing to your malloced memory.
Furthermore, with the f option, you use strcpy to write to fname. Modifying a string literal is Undefined Behavior, and could certainly explain the behavior you're seeing.
Instead, I recommend
char fname[200] = "abr.res";
This creates fname as a writable memory area of 200 bytes long, initializes it with "abr.res" as a default value, and doesn't deal with manual memory management.
Per #Laci 's comment, you're vulnerable to a buffer overflow blindly copying user input from the command line to your buffer. Steps should be taken to eliminate that risk. One way is to use strncpy:
case 'f':
strncpy(fname, optarg, sizeof fname);
// one of the dangers of strncpy is it is NOT guaranteed to NUL
// terminate the string, so do that manually just in case
fname[sizeof(fname)-1] = '\0';
break;

Related

Editing/Overwritting and printing contents of an array

Background
I have been trying to write a line editor like UNIX's ed and I've stumbled into a problem when printing an edited line.
Code
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define NOR 0 /* Normal mode for issuing commands, deleting or changing
lines */
#define INS 1 /* Insert mode for writing a line to a file */
#define OUT 2 /* This mode is not really mode but a state to tell the
program to graciously terminate */
int
main(int argc,char**argv)
{
/* argc = 1 || argc = 2 */
u_int32_t LINS, COLS;
u_int32_t curr_line;
u_int32_t curr_col_max[128];
u_int32_t curr_line_max;
int flag_no_file, flag_err, mode, fd_file, i;
char*normal;char**buffer;
ssize_t bytes_read;
/* Atributions */
LINS = 128; COLS = 120;
curr_line = 0; curr_line_max = 0;
normal = malloc(64*sizeof(char));
buffer = (char**) malloc(LINS*sizeof(char *));
for (i = 0; i <= LINS; i++)
buffer[i] = (char *) malloc(COLS*sizeof(char));
flag_err = 0;
flag_no_file = 1;
/* Error Checking and checking for existing file */
if (argc == 2)
{
fd_file = open(argv[1], O_CREAT|O_WRONLY, 777);
if (fd_file < 0)
{
write(2,"?\n", 2);
flag_no_file = 1;
}
else flag_no_file = 0;
}
if (argc > 2) flag_err = 1; /* set so it is only editor name
and file name */
/* Code Logic */
if (flag_err == 0)
{
mode = NOR;
while (mode != OUT)
{
if (mode == NOR)
{
while (mode == NOR)
{
read(0, normal, COLS);
switch(*normal)
{
case 'q': mode = OUT; break;/* exit command */
case 'i': mode = INS; break;/* switch to insert mode */
case 'a': mode = INS;
curr_line_max++;
curr_line++;
if (*(*buffer + curr_line-1) == '\0')
*(*buffer + curr_line-1) = '\n';
break;
case '+': if (curr_line < curr_line_max)
curr_line++; /* if > curr_max */
else write(1,"?\n",2);
break;
case '-': if (curr_line > 0)
curr_line--; /* if < 0 */
else write(1,"?\n",2);
break;
case 'p': if (buffer[curr_line][0] == '\0') write(1,"?\n",2);
else write(1,buffer[curr_line],(int) COLS);
break;
case 'w': if (flag_no_file) write(1,"?\n",2);
else for (i = 0;
i < curr_line_max;
write(fd_file, buffer[i], curr_col_max[i]),
i++
); /* the correct functioning of this
code is dependant
on the correct opening and reading
of file to buffer */
break;
case 'e': fd_file = open(normal, O_CREAT|O_WRONLY, 777);
/* normal must first remove the first two elements*/
if (fd_file < 0) write(1,"?\n",2);
else flag_no_file = 0;
/* Requires passing the contents of the file
to the buffer */
break;
default: write(1, "?\n",2);
}
*normal = '\0'; /* reset the normal buffer */
}
}
if (mode == INS)
{
**(buffer + curr_line) = '\0';
bytes_read = read(0,buffer[curr_line],(int) COLS);
*(*(buffer + curr_line)+bytes_read) = '\0';
/*printf("%ld\n", bytes_read);
fflush(stdin);*/
curr_col_max[curr_line] = bytes_read;
mode = NOR;
}
}
}
free(normal);
for (i=0; i<LINS;free(buffer[i++]));
free(buffer);
return flag_err;
}
Algorithm and simulation of the unexpected output
INTENDED: editing a line should replace the entire line with the new content;
SOLUTION: as usual with C arrays, the next value after '\n' will be replaced with '\0';
PROBLEM: when using the 'p' command to write the line to the screen the contents of what
was edited still remain, thus printing a '\0'(?) character.
The following is the output
$ ./a.out
i
some string
i
some
p
some
tring
q
$
Addendum
I am aware the first line in the insert mode condition can be replaced with
if (**(buffer + curr_line) != '\0')
for (i=0; i<curr_col_max[curr_line];i++)
*(*(buffer + curr_line) + i) = '\0';
Which I believe would fix this problem, however, it doesn't seem optimal at all, as i would much prefer if write only worked until the first '\0' in the buffer.
TLDR
DESIRED - Without replacing any content in the buffer past the first '\0', write only up until there.
I think I figured it out on my own, the fact that write prints out '\0' should've been a red flag to the real issue, indeed I am writing up until COLS, it should be what is stored in the `curr_col_max' array!
write(1, buffer[curr_line], curr_col_max[curr_line]);
would be the solution to this issue, i believe.

Behavior of program changing based on the order of option flags using getopt

I am writing a cat command clone in C and I'm getting weird behavior when I change the order of my option flags.
The -s option flag squeezes double-spaced lines.
The -n option flag numbers every line starting at 1.
I've checked the difference of running my program in the following ways:
$ diff <(./myCat -s spaces.txt) <(cat -s spaces.txt)
no difference
$ diff <(./myCat -n spaces.txt) <(cat -n spaces.txt)
no difference
$ diff <(./myCat -ns spaces.txt) <(cat -ns spaces.txt)
no difference
running ./myCat -sn spaces.txt, however, only squeezes the text and does not number the lines.
Can anyone explain this behavior? I thought the order of option flags did not matter in this case.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv) {
FILE *fp;
const int bufferSize = 4096;
char buffer[bufferSize];
int currentFile = 0;
for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
currentFile = i;
break;
}
}
int bflag = 0, eflag = 0, nflag = 0, sflag = 0;
int opt;
while ((opt = getopt(argc, argv, "bens:?")) != -1) {
switch(opt) {
case 'b':
bflag++;
break;
case 'e':
eflag++;
break;
case 'n':
nflag++;
break;
case 's':
sflag++;
break;
case ':':
printf("option needs a value\n");
exit(1);
case '?':
printf("usage: cat [-bens] [file ...]\n");
exit(1);
}
}
while (currentFile < argc) {
if (currentFile) {
fp = fopen(argv[currentFile], "rb");
if (fp == NULL) {
fprintf(stderr, "%s: %s: No such file or directory",
argv[0], argv[currentFile]);
exit(1);
}
}
int lineNumber = 1;
int lastLineBlank = 0;
while (fgets(buffer, bufferSize, (fp == NULL ? stdin : fp))) {
int length = strlen(buffer);
buffer[length - 1] = '\0';
if (sflag) {
length = strlen(buffer);
int currentLineBlank = (length <= 1) ? 1 : 0;
if (lastLineBlank && currentLineBlank) {
continue;
}
lastLineBlank = currentLineBlank;
}
if (bflag) {
length = strlen(buffer);
if (length >= 1) {
char *tmp = strdup(buffer);
buffer[0] = '\0';
sprintf(buffer, "%*d\t", 6, lineNumber++);
strcat(buffer, tmp);
}
} else
if (nflag) {
char *tmp = strdup(buffer);
buffer[0] = '\0';
sprintf(buffer, "%*d\t", 6, lineNumber++);
strcat(buffer, tmp);
}
if (eflag) {
length = strlen(buffer);
buffer[length] = '$';
buffer[length + 1] = '\0';
}
fprintf(stdout, "%s\n", buffer);
}
fclose(fp);
currentFile++;
}
return 0;
}
This is because of the optstring in getopt:
int getopt(int argc, char *const argv[], const char *optstring);
From the man page, the behaviour of optstring is:
optstring is a string containing the legitimate option
characters. If such a character is followed by a colon, the
option requires an argument, so getopt() places a pointer to the
following text in the same argv-element, or the text of the
following argv-element, in optarg. Two colons mean an option
takes an optional arg; if there is text in the current argv-
element (i.e., in the same word as the option name itself, for
example, "-oarg"), then it is returned in optarg, otherwise
optarg is set to zero.
The statement below means that only s requires an argument:
getopt(argc, argv, "bens:?")
(original code segfault on ./a.out -n and on ./a.out)
It looked like the only way was to use -ns or put -n after the filename. But the GNU libc example for getopt is pretty good - https://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html
I rewrote a bit of the code so that it handles all the flags but still handles filenames properly. It is not perfect, but here it is:
// I removed the for loop before this. It is not required here.
// Please remove the for loop if you use this code.
// only the case ':' has been removed here
while ((opt = getopt(argc, argv, "bens?")) != -1) {
switch(opt) {
case 'b':
bflag++;
break;
case 'e':
eflag++;
break;
case 'n':
nflag++;
break;
case 's':
sflag++;
break;
case '?':
printf("usage: cat [-bens] [file ...]\n");
exit(1);
}
}
// optind is set by getopt.
// It is equal to the position of the immediate argument after the options.
// this works because getopt permutes argv so that all the non-options are at the end
currentFile = optind;
// when no file name is provided
if(currentFile == argc) {
printf("Need a filename!\n");
return 1;
}
The rest of the code remains the same. It works for these examples:
./a.out -sn file
./a.out -ns file
./a.out -s file1 file2
./a.out -s file1 -n file2
This might still have bugs. Please do try it out and let me know if something is wrong.

Why GCC warns me to start main function argument with integer data type? [duplicate]

I'm trying to write a program that can compare two files line by line, word by word, or character by character in C. It has to be able to read in command line options -l, -w, -i or --...
if the option is -l, it compares the files line by line.
if the option is -w, it compares the files word by word.
if the option is --, it automatically assumes that the next argument is the first filename.
if the option is -i, it compares them in a case insensitive manner.
defaults to comparing the files character by character.
It's not supposed to matter how many times the options are input as long as -w and -l aren't inputted at the same time and there are no more or less than two files.
I don't even know where to begin with parsing the command line arguments.
So this is the code that I came up with for everything. I haven't error checked it quite yet, but am I writing things in an overcomplicated manner?
/*
* Functions to compare files.
*/
int compare_line();
int compare_word();
int compare_char();
int case_insens();
/*
* Program to compare the information in two files and print message saying
* whether or not this was successful.
*/
int main(int argc, char* argv[])
{
/* Loop counter */
size_t i = 0;
/* Variables for functions */
int caseIns = 0;
int line = 0;
int word = 0;
/* File pointers */
FILE *fp1, *fp2;
/*
* Read through command-line arguments for options.
*/
for (i = 1; i < argc; i++)
{
printf("argv[%u] = %s\n", i, argv[i]);
if (argv[i][0] == '-')
{
if (argv[i][1] == 'i')
{
caseIns = 1;
}
if (argv[i][1] == 'l')
{
line = 1;
}
if (argv[i][1] == 'w')
{
word = 1;
}
if (argv[i][1] == '-')
{
fp1 = argv[i][2];
fp2 = argv[i][3];
}
else
{
printf("Invalid option.");
return 2;
}
}
else
{
fp1(argv[i]);
fp2(argv[i][1]);
}
}
/*
* Check that files can be opened.
*/
if(((fp1 = fopen(fp1, "rb")) == NULL) || ((fp2 = fopen(fp2, "rb")) == NULL))
{
perror("fopen()");
return 3;
}
else
{
if (caseIns == 1)
{
if(line == 1 && word == 1)
{
printf("That is invalid.");
return 2;
}
if(line == 1 && word == 0)
{
if(compare_line(case_insens(fp1, fp2)) == 0)
return 0;
}
if(line == 0 && word == 1)
{
if(compare_word(case_insens(fp1, fp2)) == 0)
return 0;
}
else
{
if(compare_char(case_insens(fp1,fp2)) == 0)
return 0;
}
}
else
{
if(line == 1 && word == 1)
{
printf("That is invalid.");
return 2;
}
if(line == 1 && word == 0)
{
if(compare_line(fp1, fp2) == 0)
return 0;
}
if(line == 0 && word == 1)
{
if(compare_word(fp1, fp2) == 0)
return 0;
}
else
{
if(compare_char(fp1, fp2) == 0)
return 0;
}
}
}
return 1;
if(((fp1 = fclose(fp1)) == NULL) || (((fp2 = fclose(fp2)) == NULL)))
{
perror("fclose()");
return 3;
}
else
{
fp1 = fclose(fp1);
fp2 = fclose(fp2);
}
}
/*
* Function to compare two files line-by-line.
*/
int compare_line(FILE *fp1, FILE *fp2)
{
/* Buffer variables to store the lines in the file */
char buff1 [LINESIZE];
char buff2 [LINESIZE];
/* Check that neither is the end of file */
while((!feof(fp1)) && (!feof(fp2)))
{
/* Go through files line by line */
fgets(buff1, LINESIZE, fp1);
fgets(buff2, LINESIZE, fp2);
}
/* Compare files line by line */
if(strcmp(buff1, buff2) == 0)
{
printf("Files are equal.\n");
return 0;
}
printf("Files are not equal.\n");
return 1;
}
/*
* Function to compare two files word-by-word.
*/
int compare_word(FILE *fp1, FILE *fp2)
{
/* File pointers */
FILE *fp1, *fp2;
/* Arrays to store words */
char fp1words[LINESIZE];
char fp2words[LINESIZE];
if(strtok(fp1, " ") == NULL || strtok(fp2, " ") == NULL)
{
printf("File is empty. Cannot compare.\n");
return 0;
}
else
{
fp1words = strtok(fp1, " ");
fp2words = strtok(fp2, " ");
if(fp1words == fp2words)
{
fputs(fp1words);
fputs(fp2words);
printf("Files are equal.\n");
return 0;
}
}
return 1;
}
/*
* Function to compare two files character by character.
*/
int compare_char(FILE *fp1,FILE *fp2)
{
/* Variables to store the characters from both files */
int c;
int d;
/* Buffer variables to store chars */
char buff1 [LINESIZE];
char buff2 [LINESIZE];
while(((c = fgetc(fp1))!= EOF) && (((d = fgetc(fp2))!=EOF)))
{
if(c == d)
{
if((fscanf(fp1, "%c", buff1)) == (fscanf(fp2, "%c", buff2)))
{
printf("Files have equivalent characters.\n");
return 1;
break;
}
}
}
return 0;
}
/*
* Function to compare two files in a case-insensitive manner.
*/
int case_insens(FILE *fp1, FILE *fp2, size_t n)
{
/* Pointers for files. */
FILE *fp1, *fp2;
/* Variable to go through files. */
size_t i = 0;
/* Arrays to store file information. */
char fp1store[LINESIZE];
char fp2store[LINESIZE];
while(!feof(fp1) && !feof(fp2))
{
for(i = 0; i < n; i++)
{
fscanf(fp1, "%s", fp1store);
fscanf(fp2, "%s", fp2store);
fp1store = tolower(fp1store);
fp2store = tolower(fp2store);
return 1;
}
}
return 0;
}
To my knowledge, the three most popular ways how to parse command line arguments in C are:
Getopt (#include <unistd.h> from the POSIX C Library), which can solve simple argument parsing tasks. If you're a bit familiar with bash, the getopt built-in of bash is based on Getopt from the GNU libc.
Argp (#include <argp.h> from the GNU C Library), which can solve more complex tasks and takes care of stuff like, for example:
-?, --help for help message, including email address
-V, --version for version information
--usage for usage message
Doing it yourself, which I don't recommend for programs that would be given to somebody else, as there is too much that could go wrong or lower quality. The popular mistake of forgetting about '--' to stop option parsing is just one example.
The GNU C Library documentation has some nice examples for Getopt and Argp.
http://www.gnu.org/software/libc/manual/html_node/Getopt.html
http://www.gnu.org/software/libc/manual/html_node/Argp.html
Example for using Getopt
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
bool isCaseInsensitive = false;
int opt;
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;
while ((opt = getopt(argc, argv, "ilw")) != -1) {
switch (opt) {
case 'i': isCaseInsensitive = true; break;
case 'l': mode = LINE_MODE; break;
case 'w': mode = WORD_MODE; break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
// Now optind (declared extern int by <unistd.h>) is the index of the first non-option argument.
// If it is >= argc, there were no non-option arguments.
// ...
}
Example for using Argp
#include <argp.h>
#include <stdbool.h>
const char *argp_program_version = "programname programversion";
const char *argp_program_bug_address = "<your#email.address>";
static char doc[] = "Your program description.";
static char args_doc[] = "[FILENAME]...";
static struct argp_option options[] = {
{ "line", 'l', 0, 0, "Compare lines instead of characters."},
{ "word", 'w', 0, 0, "Compare words instead of characters."},
{ "nocase", 'i', 0, 0, "Compare case insensitive instead of case sensitive."},
{ 0 }
};
struct arguments {
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode;
bool isCaseInsensitive;
};
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct arguments *arguments = state->input;
switch (key) {
case 'l': arguments->mode = LINE_MODE; break;
case 'w': arguments->mode = WORD_MODE; break;
case 'i': arguments->isCaseInsensitive = true; break;
case ARGP_KEY_ARG: return 0;
default: return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };
int main(int argc, char *argv[])
{
struct arguments arguments;
arguments.mode = CHARACTER_MODE;
arguments.isCaseInsensitive = false;
argp_parse(&argp, argc, argv, 0, 0, &arguments);
// ...
}
Example for Doing it Yourself
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
bool isCaseInsensitive = false;
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;
size_t optind;
for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) {
switch (argv[optind][1]) {
case 'i': isCaseInsensitive = true; break;
case 'l': mode = LINE_MODE; break;
case 'w': mode = WORD_MODE; break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
argv += optind;
// *argv points to the remaining non-option arguments.
// If *argv is NULL, there were no non-option arguments.
// ...
}
Disclaimer: I am new to Argp, the example might contain errors.
Use getopt(), or perhaps getopt_long().
int iflag = 0;
enum { WORD_MODE, LINE_MODE } op_mode = WORD_MODE; // Default set
int opt;
while ((opt = getopt(argc, argv, "ilw") != -1)
{
switch (opt)
{
case 'i':
iflag = 1;
break;
case 'l':
op_mode = LINE_MODE;
break;
case 'w':
op_mode = WORD_MODE;
break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file ...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
/* Process file names or stdin */
if (optind >= argc)
process(stdin, "(standard input)", op_mode);
else
{
int i;
for (i = optind; i < argc; i++)
{
FILE *fp = fopen(argv[i], "r");
if (fp == 0)
fprintf(stderr, "%s: failed to open %s (%d %s)\n",
argv[0], argv[i], errno, strerror(errno));
else
{
process(fp, argv[i], op_mode);
fclose(fp);
}
}
}
Note that you need to determine which headers to include (I make it 4 that are required), and the way I wrote the op_mode type means you have a problem in the function process() - you can't access the enumeration down there. It's best to move the enumeration outside the function; you might even make op_mode a file-scope variable without external linkage (a fancy way of saying static) to avoid passing it to the function. This code does not handle - as a synonym for standard input, another exercise for the reader. Note that getopt() automatically takes care of -- to mark the end of options for you.
I've not run any version of the typing above past a compiler; there could be mistakes in it.
For extra credit, write a (library) function:
int filter(int argc, char **argv, int idx, int (*function)(FILE *fp, const char *fn));
which encapsulates the logic for processing file name options after the getopt() loop. It should handle - as standard input. Note that using this would indicate that op_mode should be a static file scope variable. The filter() function takes argc, argv, optind and a pointer to the processing function. It should return 0 (EXIT_SUCCESS) if it was able to open all the files and all invocations of the function reported 0, otherwise 1 (or EXIT_FAILURE). Having such a function simplifies writing Unix-style 'filter' programs that read files specified on the command line or standard input.
I've found Gengetopt to be quite useful - you specify the options you want with a simple configuration file, and it generates a .c/.h pair that you simply include and link with your application. The generated code makes use of getopt_long, appears to handle most common sorts of command line parameters, and it can save a lot of time.
A gengetopt input file might look something like this:
version "0.1"
package "myApp"
purpose "Does something useful."
# Options
option "filename" f "Input filename" string required
option "verbose" v "Increase program verbosity" flag off
option "id" i "Data ID" int required
option "value" r "Data value" multiple(1-) int optional
Generating the code is easy and spits out cmdline.h and cmdline.c:
$ gengetopt --input=myApp.cmdline --include-getopt
The generated code is easily integrated:
#include <stdio.h>
#include "cmdline.h"
int main(int argc, char ** argv) {
struct gengetopt_args_info ai;
if (cmdline_parser(argc, argv, &ai) != 0) {
exit(1);
}
printf("ai.filename_arg: %s\n", ai.filename_arg);
printf("ai.verbose_flag: %d\n", ai.verbose_flag);
printf("ai.id_arg: %d\n", ai.id_arg);
int i;
for (i = 0; i < ai.value_given; ++i) {
printf("ai.value_arg[%d]: %d\n", i, ai.value_arg[i]);
}
}
If you need to do any extra checking (such as ensuring flags are mutually exclusive), you can do this fairly easily with the data stored in the gengetopt_args_info struct.
You can use James Theiler's "opt" package.
And a flattering post with some examples of how it is so much simpler than other approaches is here:
Opt 3.19 review and upgrades
Docopt has a C implementation that I thought was quite
nice:
From a man-page standardized format describing command line options, docopt infers and creates an argument parser. This got started in Python; the Python version literally just parses the docstring and returns a dict. To do this in C takes a little more work, but it's clean to use and has no external dependencies.
There is a great general-purpose C library, libUCW, which includes neat command-line option parsing and configuration file loading.
The library also comes with good documentation and includes some other useful stuff (fast I/O, data structures, allocators, ...) but this can be used separately.
Example libUCW option parser (from the library docs)
#include <ucw/lib.h>
#include <ucw/opt.h>
int english;
int sugar;
int verbose;
char *tea_name;
static struct opt_section options = {
OPT_ITEMS {
OPT_HELP("A simple tea boiling console."),
OPT_HELP("Usage: teapot [options] name-of-the-tea"),
OPT_HELP(""),
OPT_HELP("Options:"),
OPT_HELP_OPTION,
OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
OPT_STRING(OPT_POSITIONAL(1), NULL, tea_name, OPT_REQUIRED, ""),
OPT_END
}
};
int main(int argc, char **argv)
{
opt_parse(&options, argv+1);
return 0;
}
Tooting my own horn if I may, I'd also like to suggest taking a look at an option parsing library that I've written: dropt.
It's a C library (with a C++ wrapper if desired).
It's lightweight.
It's extensible (custom argument types can be easily added and have equal footing with built-in argument types).
It should be very portable (it's written in standard C) with no dependencies (other than the C standard library).
It has a very unrestrictive license (zlib/libpng).
One feature that it offers that many others don't is the ability to override earlier options. For example, if you have a shell alias:
alias bar="foo --flag1 --flag2 --flag3"
and you want to use bar but with--flag1 disabled, it allows you to do:
bar --flag1=0
I wrote a tiny library that parses arguments similar to POpt, which I had several issues with, called XOpt. It uses GNU-style argument parsing and has a very similar interface to POpt.
I use it from time to time with great success, and it works pretty much anywhere.
I wrote a command-line parser library called cmdparser
https://github.com/XUJINKAI/cmdparser/
It's fully tested and support nested sub-commands.
An example for question:
static cmdp_action_t callback(cmdp_process_param_st *params);
static bool g_line_by_line = false;
static bool g_word_by_word = false;
static bool g_case_insensitive = false;
static cmdp_command_st cmdp = {
.options = {
{'l', NULL, "line by line", CMDP_TYPE_BOOL, &g_line_by_line },
{'w', NULL, "word by word", CMDP_TYPE_BOOL, &g_word_by_word },
{'i', NULL, "case insensitive", CMDP_TYPE_BOOL, &g_case_insensitive },
{0},
},
.fn_process = callback,
};
int main(int argc, char **argv) {
return cmdp_run(argc - 1, argv + 1, &cmdp);
}
static cmdp_action_t callback(cmdp_process_param_st *params) {
if (g_line_by_line && g_word_by_word) {
return CMDP_ACT_FAIL | CMDP_ACT_SHOW_HELP;
}
// your code here...
return CMDP_ACT_OVER;
}
#include <stdio.h>
int main(int argc, char **argv)
{
size_t i;
size_t filename_i = -1;
for (i = 0; i < argc; i++)
{
char const *option = argv[i];
if (option[0] == '-')
{
printf("I am a flagged option");
switch (option[1])
{
case 'a':
/*someting*/
break;
case 'b':
break;
case '-':
/* "--" -- the next argument will be a file.*/
filename_i = i;
i = i + 1;
break;
default:
printf("flag not recognised %s", option);
break;
}
}
else
{
printf("I am a positional argument");
}
/* At this point, if -- was specified, then filename_i contains the index
into argv that contains the filename. If -- was not specified, then filename_i will be -1*/
}
return 0;
}
Instructional template for parsing command line arguments in C.
C:>programName -w -- fileOne.txt fileTwo.txt
BOOL argLine = FALSE;
BOOL argWord = FALSE;
BOOL argChar = FALSE;
char * fileName1 = NULL;
char * fileName2 = NULL;
int main(int argc, char * argv[]) {
int i;
printf("Argument count=%d\n",argc);
for (i = 0; i < argc; i++) {
printf("Argument %s\n",argv[i]);
if (strcmp(argv[i],"-l")==0) {
argLine = TRUE;
printf(" argLine=TRUE\n");
}
else if (strcmp(argv[i],"-w")==0) {
argWord = TRUE;
printf(" argWord=TRUE\n");
}
else if (strcmp(argv[i],"-c")==0) {
argChar = TRUE;
printf(" argChar=TRUE\n");
}
else if (strcmp(argv[i],"--")==0) {
if (i+1 <= argc) {
fileName1 = argv[++i];
printf(" fileName1=%s\n",fileName1);
}
if (i+1 <= argc) {
fileName2 = argv[++i];
printf(" fileName2=%s\n",fileName2);
}
}
}
return 0;
}
/*
Here's a rough one not relying on any libraries.
Example:
-wi | -iw //word case insensitive
-li | -il //line case insensitive
-- file //specify the first filename (you could just get the files
as positional arguments in the else statement instead)
PS: don't mind the #define's, they're just pasting code :D
*/
#ifndef OPT_H
#define OPT_H
//specify option requires argument
#define require \
optarg = opt_pointer + 1; \
if (*optarg == '\0') \
{ \
if (++optind == argc) \
goto opt_err_arg; \
else \
optarg = argv[optind]; \
} \
opt_pointer = opt_null_terminator;
//start processing argv
#define opt \
int optind = 1; \
char *opt_pointer = argv[1]; \
char *optarg = NULL; \
char opt_null_terminator[2] = {'\0','\0'}; \
if (0) \
{ \
opt_err_arg: \
fprintf(stderr,"option %c requires argument.\n",*opt_pointer); \
return 1; \
opt_err_opt: \
fprintf(stderr,"option %c is invalid.\n",*opt_pointer); \
return 1; \
} \
for (; optind < argc; opt_pointer = argv[++optind]) \
if (*opt_pointer++ == '-') \
{ \
for (;;++opt_pointer) \
switch (*opt_pointer) \
{
//stop processing argv
#define done \
default: \
if (*opt_pointer != '\0') \
goto opt_err_opt; \
else \
goto opt_next; \
break; \
} \
opt_next:; \
}
#endif //opt.h
#include <stdio.h>
#include "opt.h"
int
main (int argc, char **argv)
{
#define by_character 0
#define by_word 1
#define by_line 2
int cmp = by_character;
int case_insensitive = 0;
opt
case 'h':
puts ("HELP!");
break;
case 'v':
puts ("fileCMP Version 1.0");
break;
case 'i':
case_insensitive = 1;
break;
case 'w':
cmp = by_word;
break;
case 'l':
cmp = by_line;
break;
case '-':required
printf("first filename: %s\n", optarg);
break;
done
else printf ("Positional Argument %s\n", argv[optind]);
return 0;
}
Okay, that's the start of long story - made short
abort parsing a command line in C ...
/**
* Helper function to parse the command line
* #param argc Argument Counter
* #param argv Argument Vector
* #param prog Program Instance Reference to fill with options
*/
bool parseCommandLine(int argc, char* argv[], DuplicateFileHardLinker* prog) {
bool pathAdded = false;
// Iterate over all arguments...
for (int i = 1; i<argc; i++) {
// Is argv a command line option?
if (argv[i][0] == '-' || argv[i][0] == '/') {
// ~~~~~~ Optionally Cut that part vvvvvvvvvvvvv for sake of simplicity ~~~~~~~
// Check for longer options
if (stricmp( &argv[i][1], "NoFileName") == 0 ||
strcmp( &argv[i][1], "q1" ) == 0 ) {
boNoFileNameLog = true;
} else if (strcmp( &argv[i][1], "HowAreYou?") == 0 ) {
logInfo( "SECRET FOUND: Well - wow I'm glad ya ask me.");
} else {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Now here comes the main thing:
//
// Check for one-character options
while (char option = *++argv[i]) {
switch (option) {
case '?':
// Show program usage
logInfo(L"Options:");
logInfo(L" /q\t>Quite mode");
logInfo(L" /v\t>Verbose mode");
logInfo(L" /d\t>Debug mode");
return false;
// Log options
case 'q':
setLogLevel(LOG_ERROR);
break;
case 'v':
setLogLevel(LOG_VERBOSE);
break;
case 'd':
setLogLevel(LOG_DEBUG);
break;
default:
logError(L"'%s' is an illegal command line option!"
" Use /? to see valid options!", option);
return false;
} // switch one-char-option
} // while one-char-options
} // else one vs longer options
} // if isArgAnOption
//
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ So that's it! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// What follows now is are some useful extras...
//
else {
// The command line options seems to be a path...
WCHAR tmpPath[MAX_PATH_LENGTH];
mbstowcs(tmpPath, argv[i], sizeof(tmpPath));
// Check if the path is existing!
//...
prog->addPath(tmpPath); // Comment or remove to get a working example
pathAdded = true;
}
}
// Check for parameters
if (!pathAdded) {
logError("You need to specify at least one folder to process!\n"
"Use /? to see valid options!");
return false;
}
return true;
}
int main(int argc, char* argv[]) {
try {
// Parse the command line
if ( !parseCommandLine(argc, argv, prog) ) {
return 1;
}
// I know that sample is just to show how the nicely parse command-line arguments
// So Please excuse more nice useful C-glatter that follows now...
}
catch ( LPCWSTR err ) {
DWORD dwError = GetLastError();
if ( wcslen(err) > 0 ) {
if ( dwError != 0 ) {
logError(dwError, err);
}
else {
logError(err);
}
}
return 2;
}
}
#define LOG_ERROR 1
#define LOG_INFO 0
#define LOG_VERBOSE -1
#define LOG_DEBUG -2
/** Logging level for the console output */
int logLevel = LOG_INFO;
void logError(LPCWSTR message, ...) {
va_list argp;
fwprintf(stderr, L"ERROR: ");
va_start(argp, message);
vfwprintf(stderr, message, argp);
va_end(argp);
fwprintf(stderr, L"\n");
}
void logInfo(LPCWSTR message, ...) {
if ( logLevel <= LOG_INFO ) {
va_list argp;
va_start(argp, message);
vwprintf(message, argp);
va_end(argp);
wprintf(L"\n");
}
}
Note that this version will also support combining arguments:
So instead of writing /h /s -> /hs will also work.
Sorry for being the n-th person posting here - however I wasn't really satisfied with all the stand-alone-versions I saw here.
Well, the library ones are quiet nice. So I would prefer the libUCW option parser, Arg or Getopt over a home-made ones.
Note you may change:
*++argv[i] -> (++argv*)[0]
It is longer and less cryptic, but still cryptic.
Okay, let's break it down:
argv[i]-> access i-th element in the argv-char pointer field
++*... -> will forward the argv-pointer by one char
... [0]-> will follow the pointer read the char
++(...) -> bracket are there so we'll increase the pointer and not the char value itself.
It is so nice that in C#, the pointers 'died' - long live the pointers!!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int is_arg(int ac, char **argv, char *arg) {
if (ac < 2) {
return 0;
}
for(int x=1; x < ac; x++) {
if (0 == strcmp(argv[x], arg)) {
return x; // return position of arg
}
}
return 0; // arg not present
}
int main (int argc, char *argv[]) {
int z = 0;
if (argc < 2) {
printf("no args present, aborting.\n");
exit(1);
}
(z=is_arg(argc, argv, "bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
(z=is_arg(argc, argv, "one bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
(z=is_arg(argc, argv, "foo")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
/* testing:
run: ./getopt two bar "one bar" foo
TRUE 2
TRUE 3
TRUE 4
run: ./getopt two bar one bar foo
TRUE 2
FALSE
TRUE 5
*/
return 0;
}

How to use command line arguments to change a bool value [duplicate]

I'm trying to write a program that can compare two files line by line, word by word, or character by character in C. It has to be able to read in command line options -l, -w, -i or --...
if the option is -l, it compares the files line by line.
if the option is -w, it compares the files word by word.
if the option is --, it automatically assumes that the next argument is the first filename.
if the option is -i, it compares them in a case insensitive manner.
defaults to comparing the files character by character.
It's not supposed to matter how many times the options are input as long as -w and -l aren't inputted at the same time and there are no more or less than two files.
I don't even know where to begin with parsing the command line arguments.
So this is the code that I came up with for everything. I haven't error checked it quite yet, but am I writing things in an overcomplicated manner?
/*
* Functions to compare files.
*/
int compare_line();
int compare_word();
int compare_char();
int case_insens();
/*
* Program to compare the information in two files and print message saying
* whether or not this was successful.
*/
int main(int argc, char* argv[])
{
/* Loop counter */
size_t i = 0;
/* Variables for functions */
int caseIns = 0;
int line = 0;
int word = 0;
/* File pointers */
FILE *fp1, *fp2;
/*
* Read through command-line arguments for options.
*/
for (i = 1; i < argc; i++)
{
printf("argv[%u] = %s\n", i, argv[i]);
if (argv[i][0] == '-')
{
if (argv[i][1] == 'i')
{
caseIns = 1;
}
if (argv[i][1] == 'l')
{
line = 1;
}
if (argv[i][1] == 'w')
{
word = 1;
}
if (argv[i][1] == '-')
{
fp1 = argv[i][2];
fp2 = argv[i][3];
}
else
{
printf("Invalid option.");
return 2;
}
}
else
{
fp1(argv[i]);
fp2(argv[i][1]);
}
}
/*
* Check that files can be opened.
*/
if(((fp1 = fopen(fp1, "rb")) == NULL) || ((fp2 = fopen(fp2, "rb")) == NULL))
{
perror("fopen()");
return 3;
}
else
{
if (caseIns == 1)
{
if(line == 1 && word == 1)
{
printf("That is invalid.");
return 2;
}
if(line == 1 && word == 0)
{
if(compare_line(case_insens(fp1, fp2)) == 0)
return 0;
}
if(line == 0 && word == 1)
{
if(compare_word(case_insens(fp1, fp2)) == 0)
return 0;
}
else
{
if(compare_char(case_insens(fp1,fp2)) == 0)
return 0;
}
}
else
{
if(line == 1 && word == 1)
{
printf("That is invalid.");
return 2;
}
if(line == 1 && word == 0)
{
if(compare_line(fp1, fp2) == 0)
return 0;
}
if(line == 0 && word == 1)
{
if(compare_word(fp1, fp2) == 0)
return 0;
}
else
{
if(compare_char(fp1, fp2) == 0)
return 0;
}
}
}
return 1;
if(((fp1 = fclose(fp1)) == NULL) || (((fp2 = fclose(fp2)) == NULL)))
{
perror("fclose()");
return 3;
}
else
{
fp1 = fclose(fp1);
fp2 = fclose(fp2);
}
}
/*
* Function to compare two files line-by-line.
*/
int compare_line(FILE *fp1, FILE *fp2)
{
/* Buffer variables to store the lines in the file */
char buff1 [LINESIZE];
char buff2 [LINESIZE];
/* Check that neither is the end of file */
while((!feof(fp1)) && (!feof(fp2)))
{
/* Go through files line by line */
fgets(buff1, LINESIZE, fp1);
fgets(buff2, LINESIZE, fp2);
}
/* Compare files line by line */
if(strcmp(buff1, buff2) == 0)
{
printf("Files are equal.\n");
return 0;
}
printf("Files are not equal.\n");
return 1;
}
/*
* Function to compare two files word-by-word.
*/
int compare_word(FILE *fp1, FILE *fp2)
{
/* File pointers */
FILE *fp1, *fp2;
/* Arrays to store words */
char fp1words[LINESIZE];
char fp2words[LINESIZE];
if(strtok(fp1, " ") == NULL || strtok(fp2, " ") == NULL)
{
printf("File is empty. Cannot compare.\n");
return 0;
}
else
{
fp1words = strtok(fp1, " ");
fp2words = strtok(fp2, " ");
if(fp1words == fp2words)
{
fputs(fp1words);
fputs(fp2words);
printf("Files are equal.\n");
return 0;
}
}
return 1;
}
/*
* Function to compare two files character by character.
*/
int compare_char(FILE *fp1,FILE *fp2)
{
/* Variables to store the characters from both files */
int c;
int d;
/* Buffer variables to store chars */
char buff1 [LINESIZE];
char buff2 [LINESIZE];
while(((c = fgetc(fp1))!= EOF) && (((d = fgetc(fp2))!=EOF)))
{
if(c == d)
{
if((fscanf(fp1, "%c", buff1)) == (fscanf(fp2, "%c", buff2)))
{
printf("Files have equivalent characters.\n");
return 1;
break;
}
}
}
return 0;
}
/*
* Function to compare two files in a case-insensitive manner.
*/
int case_insens(FILE *fp1, FILE *fp2, size_t n)
{
/* Pointers for files. */
FILE *fp1, *fp2;
/* Variable to go through files. */
size_t i = 0;
/* Arrays to store file information. */
char fp1store[LINESIZE];
char fp2store[LINESIZE];
while(!feof(fp1) && !feof(fp2))
{
for(i = 0; i < n; i++)
{
fscanf(fp1, "%s", fp1store);
fscanf(fp2, "%s", fp2store);
fp1store = tolower(fp1store);
fp2store = tolower(fp2store);
return 1;
}
}
return 0;
}
To my knowledge, the three most popular ways how to parse command line arguments in C are:
Getopt (#include <unistd.h> from the POSIX C Library), which can solve simple argument parsing tasks. If you're a bit familiar with bash, the getopt built-in of bash is based on Getopt from the GNU libc.
Argp (#include <argp.h> from the GNU C Library), which can solve more complex tasks and takes care of stuff like, for example:
-?, --help for help message, including email address
-V, --version for version information
--usage for usage message
Doing it yourself, which I don't recommend for programs that would be given to somebody else, as there is too much that could go wrong or lower quality. The popular mistake of forgetting about '--' to stop option parsing is just one example.
The GNU C Library documentation has some nice examples for Getopt and Argp.
http://www.gnu.org/software/libc/manual/html_node/Getopt.html
http://www.gnu.org/software/libc/manual/html_node/Argp.html
Example for using Getopt
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
bool isCaseInsensitive = false;
int opt;
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;
while ((opt = getopt(argc, argv, "ilw")) != -1) {
switch (opt) {
case 'i': isCaseInsensitive = true; break;
case 'l': mode = LINE_MODE; break;
case 'w': mode = WORD_MODE; break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
// Now optind (declared extern int by <unistd.h>) is the index of the first non-option argument.
// If it is >= argc, there were no non-option arguments.
// ...
}
Example for using Argp
#include <argp.h>
#include <stdbool.h>
const char *argp_program_version = "programname programversion";
const char *argp_program_bug_address = "<your#email.address>";
static char doc[] = "Your program description.";
static char args_doc[] = "[FILENAME]...";
static struct argp_option options[] = {
{ "line", 'l', 0, 0, "Compare lines instead of characters."},
{ "word", 'w', 0, 0, "Compare words instead of characters."},
{ "nocase", 'i', 0, 0, "Compare case insensitive instead of case sensitive."},
{ 0 }
};
struct arguments {
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode;
bool isCaseInsensitive;
};
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct arguments *arguments = state->input;
switch (key) {
case 'l': arguments->mode = LINE_MODE; break;
case 'w': arguments->mode = WORD_MODE; break;
case 'i': arguments->isCaseInsensitive = true; break;
case ARGP_KEY_ARG: return 0;
default: return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };
int main(int argc, char *argv[])
{
struct arguments arguments;
arguments.mode = CHARACTER_MODE;
arguments.isCaseInsensitive = false;
argp_parse(&argp, argc, argv, 0, 0, &arguments);
// ...
}
Example for Doing it Yourself
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
bool isCaseInsensitive = false;
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;
size_t optind;
for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) {
switch (argv[optind][1]) {
case 'i': isCaseInsensitive = true; break;
case 'l': mode = LINE_MODE; break;
case 'w': mode = WORD_MODE; break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
argv += optind;
// *argv points to the remaining non-option arguments.
// If *argv is NULL, there were no non-option arguments.
// ...
}
Disclaimer: I am new to Argp, the example might contain errors.
Use getopt(), or perhaps getopt_long().
int iflag = 0;
enum { WORD_MODE, LINE_MODE } op_mode = WORD_MODE; // Default set
int opt;
while ((opt = getopt(argc, argv, "ilw") != -1)
{
switch (opt)
{
case 'i':
iflag = 1;
break;
case 'l':
op_mode = LINE_MODE;
break;
case 'w':
op_mode = WORD_MODE;
break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file ...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
/* Process file names or stdin */
if (optind >= argc)
process(stdin, "(standard input)", op_mode);
else
{
int i;
for (i = optind; i < argc; i++)
{
FILE *fp = fopen(argv[i], "r");
if (fp == 0)
fprintf(stderr, "%s: failed to open %s (%d %s)\n",
argv[0], argv[i], errno, strerror(errno));
else
{
process(fp, argv[i], op_mode);
fclose(fp);
}
}
}
Note that you need to determine which headers to include (I make it 4 that are required), and the way I wrote the op_mode type means you have a problem in the function process() - you can't access the enumeration down there. It's best to move the enumeration outside the function; you might even make op_mode a file-scope variable without external linkage (a fancy way of saying static) to avoid passing it to the function. This code does not handle - as a synonym for standard input, another exercise for the reader. Note that getopt() automatically takes care of -- to mark the end of options for you.
I've not run any version of the typing above past a compiler; there could be mistakes in it.
For extra credit, write a (library) function:
int filter(int argc, char **argv, int idx, int (*function)(FILE *fp, const char *fn));
which encapsulates the logic for processing file name options after the getopt() loop. It should handle - as standard input. Note that using this would indicate that op_mode should be a static file scope variable. The filter() function takes argc, argv, optind and a pointer to the processing function. It should return 0 (EXIT_SUCCESS) if it was able to open all the files and all invocations of the function reported 0, otherwise 1 (or EXIT_FAILURE). Having such a function simplifies writing Unix-style 'filter' programs that read files specified on the command line or standard input.
I've found Gengetopt to be quite useful - you specify the options you want with a simple configuration file, and it generates a .c/.h pair that you simply include and link with your application. The generated code makes use of getopt_long, appears to handle most common sorts of command line parameters, and it can save a lot of time.
A gengetopt input file might look something like this:
version "0.1"
package "myApp"
purpose "Does something useful."
# Options
option "filename" f "Input filename" string required
option "verbose" v "Increase program verbosity" flag off
option "id" i "Data ID" int required
option "value" r "Data value" multiple(1-) int optional
Generating the code is easy and spits out cmdline.h and cmdline.c:
$ gengetopt --input=myApp.cmdline --include-getopt
The generated code is easily integrated:
#include <stdio.h>
#include "cmdline.h"
int main(int argc, char ** argv) {
struct gengetopt_args_info ai;
if (cmdline_parser(argc, argv, &ai) != 0) {
exit(1);
}
printf("ai.filename_arg: %s\n", ai.filename_arg);
printf("ai.verbose_flag: %d\n", ai.verbose_flag);
printf("ai.id_arg: %d\n", ai.id_arg);
int i;
for (i = 0; i < ai.value_given; ++i) {
printf("ai.value_arg[%d]: %d\n", i, ai.value_arg[i]);
}
}
If you need to do any extra checking (such as ensuring flags are mutually exclusive), you can do this fairly easily with the data stored in the gengetopt_args_info struct.
You can use James Theiler's "opt" package.
And a flattering post with some examples of how it is so much simpler than other approaches is here:
Opt 3.19 review and upgrades
Docopt has a C implementation that I thought was quite
nice:
From a man-page standardized format describing command line options, docopt infers and creates an argument parser. This got started in Python; the Python version literally just parses the docstring and returns a dict. To do this in C takes a little more work, but it's clean to use and has no external dependencies.
There is a great general-purpose C library, libUCW, which includes neat command-line option parsing and configuration file loading.
The library also comes with good documentation and includes some other useful stuff (fast I/O, data structures, allocators, ...) but this can be used separately.
Example libUCW option parser (from the library docs)
#include <ucw/lib.h>
#include <ucw/opt.h>
int english;
int sugar;
int verbose;
char *tea_name;
static struct opt_section options = {
OPT_ITEMS {
OPT_HELP("A simple tea boiling console."),
OPT_HELP("Usage: teapot [options] name-of-the-tea"),
OPT_HELP(""),
OPT_HELP("Options:"),
OPT_HELP_OPTION,
OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
OPT_STRING(OPT_POSITIONAL(1), NULL, tea_name, OPT_REQUIRED, ""),
OPT_END
}
};
int main(int argc, char **argv)
{
opt_parse(&options, argv+1);
return 0;
}
Tooting my own horn if I may, I'd also like to suggest taking a look at an option parsing library that I've written: dropt.
It's a C library (with a C++ wrapper if desired).
It's lightweight.
It's extensible (custom argument types can be easily added and have equal footing with built-in argument types).
It should be very portable (it's written in standard C) with no dependencies (other than the C standard library).
It has a very unrestrictive license (zlib/libpng).
One feature that it offers that many others don't is the ability to override earlier options. For example, if you have a shell alias:
alias bar="foo --flag1 --flag2 --flag3"
and you want to use bar but with--flag1 disabled, it allows you to do:
bar --flag1=0
I wrote a tiny library that parses arguments similar to POpt, which I had several issues with, called XOpt. It uses GNU-style argument parsing and has a very similar interface to POpt.
I use it from time to time with great success, and it works pretty much anywhere.
I wrote a command-line parser library called cmdparser
https://github.com/XUJINKAI/cmdparser/
It's fully tested and support nested sub-commands.
An example for question:
static cmdp_action_t callback(cmdp_process_param_st *params);
static bool g_line_by_line = false;
static bool g_word_by_word = false;
static bool g_case_insensitive = false;
static cmdp_command_st cmdp = {
.options = {
{'l', NULL, "line by line", CMDP_TYPE_BOOL, &g_line_by_line },
{'w', NULL, "word by word", CMDP_TYPE_BOOL, &g_word_by_word },
{'i', NULL, "case insensitive", CMDP_TYPE_BOOL, &g_case_insensitive },
{0},
},
.fn_process = callback,
};
int main(int argc, char **argv) {
return cmdp_run(argc - 1, argv + 1, &cmdp);
}
static cmdp_action_t callback(cmdp_process_param_st *params) {
if (g_line_by_line && g_word_by_word) {
return CMDP_ACT_FAIL | CMDP_ACT_SHOW_HELP;
}
// your code here...
return CMDP_ACT_OVER;
}
#include <stdio.h>
int main(int argc, char **argv)
{
size_t i;
size_t filename_i = -1;
for (i = 0; i < argc; i++)
{
char const *option = argv[i];
if (option[0] == '-')
{
printf("I am a flagged option");
switch (option[1])
{
case 'a':
/*someting*/
break;
case 'b':
break;
case '-':
/* "--" -- the next argument will be a file.*/
filename_i = i;
i = i + 1;
break;
default:
printf("flag not recognised %s", option);
break;
}
}
else
{
printf("I am a positional argument");
}
/* At this point, if -- was specified, then filename_i contains the index
into argv that contains the filename. If -- was not specified, then filename_i will be -1*/
}
return 0;
}
Instructional template for parsing command line arguments in C.
C:>programName -w -- fileOne.txt fileTwo.txt
BOOL argLine = FALSE;
BOOL argWord = FALSE;
BOOL argChar = FALSE;
char * fileName1 = NULL;
char * fileName2 = NULL;
int main(int argc, char * argv[]) {
int i;
printf("Argument count=%d\n",argc);
for (i = 0; i < argc; i++) {
printf("Argument %s\n",argv[i]);
if (strcmp(argv[i],"-l")==0) {
argLine = TRUE;
printf(" argLine=TRUE\n");
}
else if (strcmp(argv[i],"-w")==0) {
argWord = TRUE;
printf(" argWord=TRUE\n");
}
else if (strcmp(argv[i],"-c")==0) {
argChar = TRUE;
printf(" argChar=TRUE\n");
}
else if (strcmp(argv[i],"--")==0) {
if (i+1 <= argc) {
fileName1 = argv[++i];
printf(" fileName1=%s\n",fileName1);
}
if (i+1 <= argc) {
fileName2 = argv[++i];
printf(" fileName2=%s\n",fileName2);
}
}
}
return 0;
}
/*
Here's a rough one not relying on any libraries.
Example:
-wi | -iw //word case insensitive
-li | -il //line case insensitive
-- file //specify the first filename (you could just get the files
as positional arguments in the else statement instead)
PS: don't mind the #define's, they're just pasting code :D
*/
#ifndef OPT_H
#define OPT_H
//specify option requires argument
#define require \
optarg = opt_pointer + 1; \
if (*optarg == '\0') \
{ \
if (++optind == argc) \
goto opt_err_arg; \
else \
optarg = argv[optind]; \
} \
opt_pointer = opt_null_terminator;
//start processing argv
#define opt \
int optind = 1; \
char *opt_pointer = argv[1]; \
char *optarg = NULL; \
char opt_null_terminator[2] = {'\0','\0'}; \
if (0) \
{ \
opt_err_arg: \
fprintf(stderr,"option %c requires argument.\n",*opt_pointer); \
return 1; \
opt_err_opt: \
fprintf(stderr,"option %c is invalid.\n",*opt_pointer); \
return 1; \
} \
for (; optind < argc; opt_pointer = argv[++optind]) \
if (*opt_pointer++ == '-') \
{ \
for (;;++opt_pointer) \
switch (*opt_pointer) \
{
//stop processing argv
#define done \
default: \
if (*opt_pointer != '\0') \
goto opt_err_opt; \
else \
goto opt_next; \
break; \
} \
opt_next:; \
}
#endif //opt.h
#include <stdio.h>
#include "opt.h"
int
main (int argc, char **argv)
{
#define by_character 0
#define by_word 1
#define by_line 2
int cmp = by_character;
int case_insensitive = 0;
opt
case 'h':
puts ("HELP!");
break;
case 'v':
puts ("fileCMP Version 1.0");
break;
case 'i':
case_insensitive = 1;
break;
case 'w':
cmp = by_word;
break;
case 'l':
cmp = by_line;
break;
case '-':required
printf("first filename: %s\n", optarg);
break;
done
else printf ("Positional Argument %s\n", argv[optind]);
return 0;
}
Okay, that's the start of long story - made short
abort parsing a command line in C ...
/**
* Helper function to parse the command line
* #param argc Argument Counter
* #param argv Argument Vector
* #param prog Program Instance Reference to fill with options
*/
bool parseCommandLine(int argc, char* argv[], DuplicateFileHardLinker* prog) {
bool pathAdded = false;
// Iterate over all arguments...
for (int i = 1; i<argc; i++) {
// Is argv a command line option?
if (argv[i][0] == '-' || argv[i][0] == '/') {
// ~~~~~~ Optionally Cut that part vvvvvvvvvvvvv for sake of simplicity ~~~~~~~
// Check for longer options
if (stricmp( &argv[i][1], "NoFileName") == 0 ||
strcmp( &argv[i][1], "q1" ) == 0 ) {
boNoFileNameLog = true;
} else if (strcmp( &argv[i][1], "HowAreYou?") == 0 ) {
logInfo( "SECRET FOUND: Well - wow I'm glad ya ask me.");
} else {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Now here comes the main thing:
//
// Check for one-character options
while (char option = *++argv[i]) {
switch (option) {
case '?':
// Show program usage
logInfo(L"Options:");
logInfo(L" /q\t>Quite mode");
logInfo(L" /v\t>Verbose mode");
logInfo(L" /d\t>Debug mode");
return false;
// Log options
case 'q':
setLogLevel(LOG_ERROR);
break;
case 'v':
setLogLevel(LOG_VERBOSE);
break;
case 'd':
setLogLevel(LOG_DEBUG);
break;
default:
logError(L"'%s' is an illegal command line option!"
" Use /? to see valid options!", option);
return false;
} // switch one-char-option
} // while one-char-options
} // else one vs longer options
} // if isArgAnOption
//
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ So that's it! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// What follows now is are some useful extras...
//
else {
// The command line options seems to be a path...
WCHAR tmpPath[MAX_PATH_LENGTH];
mbstowcs(tmpPath, argv[i], sizeof(tmpPath));
// Check if the path is existing!
//...
prog->addPath(tmpPath); // Comment or remove to get a working example
pathAdded = true;
}
}
// Check for parameters
if (!pathAdded) {
logError("You need to specify at least one folder to process!\n"
"Use /? to see valid options!");
return false;
}
return true;
}
int main(int argc, char* argv[]) {
try {
// Parse the command line
if ( !parseCommandLine(argc, argv, prog) ) {
return 1;
}
// I know that sample is just to show how the nicely parse command-line arguments
// So Please excuse more nice useful C-glatter that follows now...
}
catch ( LPCWSTR err ) {
DWORD dwError = GetLastError();
if ( wcslen(err) > 0 ) {
if ( dwError != 0 ) {
logError(dwError, err);
}
else {
logError(err);
}
}
return 2;
}
}
#define LOG_ERROR 1
#define LOG_INFO 0
#define LOG_VERBOSE -1
#define LOG_DEBUG -2
/** Logging level for the console output */
int logLevel = LOG_INFO;
void logError(LPCWSTR message, ...) {
va_list argp;
fwprintf(stderr, L"ERROR: ");
va_start(argp, message);
vfwprintf(stderr, message, argp);
va_end(argp);
fwprintf(stderr, L"\n");
}
void logInfo(LPCWSTR message, ...) {
if ( logLevel <= LOG_INFO ) {
va_list argp;
va_start(argp, message);
vwprintf(message, argp);
va_end(argp);
wprintf(L"\n");
}
}
Note that this version will also support combining arguments:
So instead of writing /h /s -> /hs will also work.
Sorry for being the n-th person posting here - however I wasn't really satisfied with all the stand-alone-versions I saw here.
Well, the library ones are quiet nice. So I would prefer the libUCW option parser, Arg or Getopt over a home-made ones.
Note you may change:
*++argv[i] -> (++argv*)[0]
It is longer and less cryptic, but still cryptic.
Okay, let's break it down:
argv[i]-> access i-th element in the argv-char pointer field
++*... -> will forward the argv-pointer by one char
... [0]-> will follow the pointer read the char
++(...) -> bracket are there so we'll increase the pointer and not the char value itself.
It is so nice that in C#, the pointers 'died' - long live the pointers!!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int is_arg(int ac, char **argv, char *arg) {
if (ac < 2) {
return 0;
}
for(int x=1; x < ac; x++) {
if (0 == strcmp(argv[x], arg)) {
return x; // return position of arg
}
}
return 0; // arg not present
}
int main (int argc, char *argv[]) {
int z = 0;
if (argc < 2) {
printf("no args present, aborting.\n");
exit(1);
}
(z=is_arg(argc, argv, "bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
(z=is_arg(argc, argv, "one bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
(z=is_arg(argc, argv, "foo")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
/* testing:
run: ./getopt two bar "one bar" foo
TRUE 2
TRUE 3
TRUE 4
run: ./getopt two bar one bar foo
TRUE 2
FALSE
TRUE 5
*/
return 0;
}

Trouble assigning optarg when using getopt()

I am having trouble assigning optarg to a inFilename and outFilename. The error states that an incompatible type error has occurred. Please forgive me if this a trivial error, I've started learning C about a week ago.
EDIT: I've used strncpy but getting segmentation fault.
EDIT: Here is how I plan on using this :
./sortfile -i input.txt -o output.txt
int main(int argc, char *argv[]) {
char c;
const int MAX_FILENAME_LEN = 256;
const int MAX_NUMBERS = 100;
int xFlag = 0;
int yFlag = 0;
char inFilename[MAX_FILENAME_LEN];
char outFilename[MAX_FILENAME_LEN];
int *numbers; // number array: to be dynamically allocated
int count;
int exitValue = 1;
//printf("Enter the input file name: ");
//scanf("%s", inFilename);
while ((c = getopt(argc, argv, "ioxy")) != -1) {
switch (c) {
case 'i':
strncpy(inFilename, optarg, sizeof(inFilename) - 1);
break;
case 'o':
strncpy(outFilename, optarg, sizeof(outFilename) - 1);
break;
case 'x':
xFlag = 1;
break;
case 'y':
yFlag = 1;
break;
case '?':
fprintf(stderr, "Unrecognized option!\n");
break;
}
}
if (!inFilename || !outFilename) {
fprintf(stderr, "Must have -i and -o option!\n");
exit(0);
}
numbers = (int *) malloc(MAX_NUMBERS * sizeof(int));
count = readNumbers(numbers, inFilename);
if (count >= 0) {
//printf("Enter the output file name (will be created/overwitten): ");
//scanf("%s", outFilename);
printArray(numbers, count);
bubbleSort(numbers, count, true);
printArray(numbers, count);
writeNumbers(numbers, count, outFilename);
}
free(numbers);
return exitValue;
}
Here is the program before the changes. This program works.
int main(void) {
const int MAX_FILENAME_LEN = 256;
const int MAX_NUMBERS = 100;
char inFilename[MAX_FILENAME_LEN];
char outFilename[MAX_FILENAME_LEN];
int *numbers; // number array: to be dynamically allocated
int count;
int exitValue = 1;
printf("Enter the input file name: ");
scanf("%s", inFilename);
numbers = (int *) malloc(MAX_NUMBERS * sizeof(int));
count = readNumbers(numbers, inFilename);
if (count >= 0) {
printf("Enter the output file name (will be created/overwitten): ");
scanf("%s", outFilename);
printArray(numbers, count);
bubbleSort(numbers, count, true);
printArray(numbers, count);
writeNumbers(numbers, count, outFilename);
exitValue = 0;
}
free(numbers);
return exitValue;
}
This:
getopt(argc, argv, "ioxy")
tells getopt() about four options, i, o, x and y, none of which take arguments. This is why any attempts to get those arguments from optarg are failing, because they aren't there.
What you need is:
getopt(argc, argv, "i:o:xy")
to tell getopt() that your i and o options should have arguments

Resources