Quick Edit: This is a homework assignment. My goal is to take in a few cl arguments for my program (either -s, -w with a width length, and the file) and word wrap the file that according the default length of 40 characters or a new number if the user chooses the '-w' option.
I'm trying to write a C program that takes in the arguments via command prompt (the executable is named "wrapfile.exe"). The program isn't done and more is to be added, this is just a part of it that is causing me mayhem.
Here would be an example of valid command prompt entries:
C:\"within wrapfile.exe's directory"> wrapfile -s filename.txt
C:\"within wrapfile.exe's directory"> wrapfile -w 5 filename.txt
C:\"within wrapfile.exe's directory"> wrapfile -s -w 50 filename.txt
etc.
Example of invalid entries:
C:\"within wrapfile.exe's directory"> wrapfile
C:\"within wrapfile.exe's directory"> wrapfile -w
C:\"within wrapfile.exe's directory"> wrapfile qwer
etc.
My issue is it cannot detect the number after I enter "-w" ..
Here is the code:
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "string.h"
int main(int argc, char *argv[])
{
int output = 0;
int commands = 1;
int wraplength= 41;
int i=0;
int counter=0;
int wordwrap = 0;
int ExitStatus = 1;
int input = 1;
int w = 0;
int s = 0;
FILE *f = NULL;
for (i=0; i < argc; i++)
{
if ( (*argv[input] + i-1) == '-') // check for option
{
printf(" - detected first");
if (*(argv[input] + i ) == 's') // check for wordwrap
{
printf(" s detected");
i++;
i++;
s = 1; // set s to true to that option can be added later
wordwrap = 1; // set wordwrap on or true
}
if (*(argv[input] + i) == 'w')//if the second option is a w
{
i++;
printf(" w detected ");
sscanf ((argv[input] + i), "%d", &wraplength);
printf ("%d", wraplength);
if ( wraplength < 1) // check what the number is
{
printf("Usage: wrapfile [-s] [-w width] file ...");
return 2; // command line options incorrect
}
}
if (*(argv[input] + i) == '-')
{
printf(" second - detected");
i++;
if (*(argv[input]+ i) == 'w')//if the second option is a w
{
i++;
if (sscanf ((argv[(input)+1]), "%d", &wraplength) != 1) // check what the number is
{
printf("Usage: wrapfile [-s] [-w width] file ...");
return 2; // command line options incorrect
}
}
}
}
}
return 0;
}
BIG EDIT:
I took Dietrich Epp suggestion and here is something I did with it. It seems my program crashes every time I try to check an argument after "-s". How can I check the next arguments (if there is none?) without crashing my program. I know that this line has something to do with the crashing:
arg = argv[i++];
Here's the code:
while (i < argc)
{
arg = argv[i++];
if (!strcmp(arg, "-s"))
{
arg = argv[i++];
son = 1;
printf("Have -s\n");
if (!strcmp(arg, "-w"))
{
if (i >= argc)
{
printf("Usage: wrapfile [-s] [-w width] file ...");
}
param = argv[i++];
wraplength = *param;
printf("Have -w %s\n", param);
}
}
I think you're mixing up your loop variables here.
This makes i loop over all arguments, including argv[0] which you usually don't want.
for (i=0; i < argc; i++)
This uses i as an index into one of the argument strings, but with funny syntax.
if (*(argv[input] + i ) == 's')
On other systems you'd just use getopt(), but that's not always an option on Windows.
Suggestion
You'll want a loop more like this:
// Note: C99, so you will need to translate to C89 if you use Microsoft's
// C compiler
int i = 1;
while (i < argc) {
char *arg = argv[i++];
if (!strcmp(arg, "-s")) {
printf("Have -s\n");
} else if (!strcmp(arg, "-w")) {
if (i >= argc)
error();
char *param = argv[i++];
printf("Have -w %s\n", param);
} else {
error();
}
}
Command option parsing is so incredibly not relevant to your program's performance that the above chain of if/else blocks and strcmp() are fine.
Warning!
You will not be able to specify arbitrary filenames with this! If you get the arguments from main(), they will be converted to whichever code page you are currently using, which is horribly broken for almost any purpose. (It might be fine if you are the only one using the program.)
In order to specify arbitrary filenames, you will need to call GetCommandLineW() to get the command line in UTF-16, and then CommandLineToArgvW() to parse it to int argc and wchar_t **argv.
Related
I'm working on a program in C that will open, read, and close a file with Linux system calls, and print the contents of the file(s) to the screen. The command format is
$ mycat [-bens] f1 [f2 ...].
The switches are as follows:
-b displays the line number for each non-blank line, starting at 1
-e displays a '$' at the end of each line
-n displays the line number for every line
-s removes all empty lines from the output (effectively single-spacing the output)
The problem is that when I use the -b or -n switch, printf appears to be "overlapping" the line number with what the buffer is trying to print from the text file itself.
Here is the code I have written for the program:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#define BUFFERSIZE 4096
void oops(char *, char *);
int main(int ac, char *av[])
{
int fd, numRead, curr, i, c;
char buf[BUFFERSIZE] = {0};
extern char *optarg;
extern int optind;
int tmpS = 0;
int tmpB = 0;
int bFlag = 0;
int eFlag = 0;
int nFlag = 0;
int sFlag = 0;
int bLineNum = 1;
int nLineNum = 1;
/* Flag processing in argument list */
while( (c = getopt(ac, av, "bens")) != -1)
{
switch(c)
{
case 'b':
bFlag = 1;
break;
case 'e':
eFlag = 1;
break;
case 'n':
nFlag = 1;
break;
case 's':
sFlag = 1;
break;
default:
exit(EXIT_FAILURE);
}
}
/* Scan through each argument after flag */
for(i = optind; i < ac; i++)
{
/* Error handling when opening each file */
if((fd = open(av[i], O_RDONLY)) == -1)
oops("Cannot open ", av[i]);
/* Read from file to buffer, until end is reached */
while( (numRead = read(fd, buf, BUFFERSIZE)) > 0)
{
/* Once buffer is filled, process each address in buffer */
for(curr = 0; curr < BUFFERSIZE; curr++)
{
/* sFlag squeezes output, eliminating blank lines */
if(sFlag && buf[curr] == '\n')
{
tmpS = curr + 1;
while(buf[tmpS] != '\r')
{
if(isspace(buf[tmpS]))
tmpS++;
else
break;
}
curr = tmpS + 1;
}
/* nFlag numbers each line, starting from 1 */
if(nFlag && buf[curr] == '\n')
printf("%d ", nLineNum++);
/* eFlag puts a '$' at the end of every line */
if(eFlag && buf[curr] == '\r')
printf(" $");
/* bFlag numbers every non-blank line, starting from 1 */
if(bFlag && buf[curr] == '\n')
{
tmpB = curr + 1;
if(isEmptyLine(buf, tmpB))
printf("%d ", bLineNum++);
}
/* Print the current character in the buffer address */
printf("%c", buf[curr]);
}
}
if(numRead == -1)
oops("Read error from ", av[i]);
}
return 0;
}
void oops(char *s1, char *s2)
{
fprintf(stderr, "Error: %s ", s1);
perror(s2);
exit(1);
}
int isEmptyLine(char *buf, int tmp)
{
while(buf[tmp] != '\n')
{
if(!isspace(buf[tmp]))
return 0;
tmp++;
}
return 1;
}
Sample input (file1.txt):
An excerpt from LEARNING DOS FOR THE COMPLETE NOVICE, by Steven Woas, copyright 1993.
1. Change to the compressed drive and then issue a CHKDSK
command like so:
c: <ENTER>
chkdsk /f <ENTER>
The /F tells DOS to fix errors.
Another option is to do it like so:
dblspace /chkdsk /f <ENTER>
A shortcut for the DBLSPACE /CHKDSK /F command is:
dblspace /chk /f <ENTER>
Output with -n flag on and running:
sh-4.2$ ./main -n file1.txt
1 excerpt from LEARNING DOS FOR THE COMPLETE NOVICE, by Steven Woas, copyright 1993.
2
3 Change to the compressed drive and then issue a CHKDSK
4 command like so:
5
6 c: <ENTER>
7
8 chkdsk /f <ENTER>
9
10 The /F tells DOS to fix errors.
11
12 Another option is to do it like so:
13
14 dblspace /chkdsk /f <ENTER>
15
16 A shortcut for the DBLSPACE /CHKDSK /F command is:
17
18 dblspace /chk /f <ENTER>
I'm having the same problem with the -b flag and I don't know why. Does it have to do with \r and \n not being read properly?
Your program exhibits the misbehavior you describe for files with Windows- (DOS-)style line endings (\r\n), but different misbehavior for files with UNIX-style line endings (\n alone) and yet different misbehavior for files with MacOS class-style line endings (\r alone). Inasmuch as you seem to be assuming Windows-style line endings overall, I'll focus on that.
Consider what happens when your program reaches the end of a line. It first processes the \r character, ultimately printing it. This causes the output position to return to the beginning of the line (which is possible because the standard output is line-buffered by default). You then print the line number, overwriting whatever may have been there before, and finally print the \n character, causing the buffer to be flushed and the output to move to the next line.
You probably ought to recognize the \r\n sequence as a line ending, instead of trying to handle these characters individually. That may prove to be a bit challenging, as you need to account for the possibility that the pair is split across two read()s, but that's shouldn't be too hard. This will also give you the opportunity to consider what to do if you encounter a lone \n and / or a lone \r, which your program could handle more gracefully than it now does.
Consider a file ArgumentFile.txt
int a=100;
int b[3] = { 5, 2, 5 };
double c = 0.0014;
And the main code code.c
int main(int argc, char *argv[])
{
if (argc > 1) FILE *f = fopen(argv[1], "r");
ParseFile(f); // Set the parameters based on file
DoStuff(a,b,c); // Run the process based on the parsed arguments
}
A user could then pass arguments by doing
./CodeExecutable ArgumentFile.txt
Is there a standard solution to parse arguments from file? It would be an equivalent of getopt which parse arguments from command line?
You do not need an equivalent to getopt() you can use exactly getopt(). The getopt() function does not specifically process command line arguments; it will process any array of pointers to strings in the style of command line arguments.
#define MAX_ARGS 256
#define MAX_FILE_LEN 4096
int main(int argc, char *argv[])
{
if( argc > 1 )
{
FILE *f = fopen(argv[1], "r");
if( f != 0 )
{
char fargs[MAX_FILE_LEN] = "" ;
fread( fargs, 1, MAX_FILE_LEN, f ) ;
// Build fargv from file content
char* fargv[MAX_ARGS] ;
int fargc = 0 ;
fargv[fargc] = strtok( fargs, " \n\r" ) ;
while( fargc < MAX_ARGS && fargv[fargc] != 0 )
{
fargc++ ;
fargv[fargc] = strtok( 0, "\n\r" ) ;
}
// Process fargv using getopt()
while( (char c = getopt( fargc, fargv, "a:b:c:")) != -1 )
{
switch( c )
{
...
}
}
}
}
...
return 0 ;
}
It is probably better to dynamically allocate fargs using teh actual file length, but the above is illustrative only.
Your input file might then look like:
-a 100
-b 5,2,5
-c 0.0014
The getopt() loop will then have to process the arguments as necessary - using sscanf() for example.
switch( c )
{
case 'a' : sscanf( optarg, "%i", a ) ; break ;
case 'b' : sscanf( optarg, "%i,%i,%i", b[0], b[1], b[2] ) ; break ;
case 'c' : sscanf( optarg, "%f", c ) ; break ;
}
DoStuff( a, b, c ) ;
I use getopt(). Here is an example that allows for some more flexibility. This example demonstrates how th handle for optional optarg and multiple optargs.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
void usage(void)
{
printf("usage: \n"
"This example demonstrates how to add flexibility to the traditional linux getopt()\n"
"This help text is printed if the program is executed without arguments\n"
"or with an invalid argument configuration.\n"
"to view the help file run without arguments or with -h\n"
"Oterwise the program accepts two options: -d, -u\n"
"-d: can come with 0 or one option argument\n"
"-u: can come with one or more option arguments\n"
"try this to see the output:\n"
"./test -d aaa -u ccc 4 \"quoted multi token string\" -d -u\n");
}
int main(int argc, char **argv)
{
char data[101];
int opt;
memset(data, 0, 101);
while ((opt = getopt(argc, argv, "hd:u:t:")) != -1) {
switch (opt) {
case 'h':
usage();
return 0;
case 'd': // can accept 0 or 1 parameters
if (optarg[0] == '-') { //not an optarg of ours...
optind--;
printf("option: -d. no tokens (another option follows)\n");
break;
}
strncpy(data, optarg, 100);
printf("option: -d. tokens: %s\n", data);
break;
case 'u': //can accept one or more parameters ...
strncpy(data, optarg, 100);
printf("option: -u. tokens: %s", data);
//do we have more arguments for 'u'?
while( optind <= argc && argv[optind][0] != '-') {
strncpy(data, argv[optind], 100);
printf(", %s", data);
optind++;
}
printf(".\n");
break;
case ':': //this happens if we got an option which expects an arg without any optarg.
if(optopt == 'd') {//lets allow a '-d' without its optarg
printf("option: -d. no tokens\n");
break;
}
//otherwise fall through to the default handler
default: //covers ':' '?' for missing value, '-h' for help, etc.
printf("on error you get: opt=%c. optopt=%c opterr=%d\n", opt, optopt, opterr);
return 0;
}
}
return 0;
}
You can't do that using plain C code. You'll have to write platform specific assembly language code to handle that.
Your best option is to use the C proprocessor.
int main(int argc, char *argv[])
{
#include "myfile.txt"
// Do Stuff
}
Having said that, I don't know what you would gain by that instead of putting the contents of myfile.txt in main() directly.
I am trying to deal with the command line arguments but the command line args include whitespaces which I need to make into consideration.
Here is my code and I am trying to deal with the following command line args:
if I type in "-upper" as the command args then it changes the text to upper cases/
if I type in "-lower" then it changes the text to lower cases/
if I type in "-upper -lower" then it only changes the text to lower cases/
if I type in "-lower -upper" then it only changes the text to upper cases/
the snippet of my program is:
int main(int argc, char *argv[])
{
int ch;
int arg;
char buf[100];
int upper = 0;
int lower = 0;
for( arg = 1; arg < argc; arg++ )
{
if( strcmp( argv[arg] , "-upper") == 0 )
{
upper = 1;
}
if( strcmp( argv[arg] , "-lower") == 0)
{
lower = 1;
}
if( strcmp( argv[arg] , "-lower -upper") == 0 )
{
upper = 1;
}
else if( strcmp( argv[arg] , "-upper -lower") == 0)
{
lower = 1;
}
else
{
fprintf(stderr,"Invalid command line option\n" );
return 0;
}
}
while ((ch = getchar()) != EOF) //deal with cmd args
However, strcmp doesn't handle the whitespaces. So the issue arised is that C program will not treat "-lower -upper" as "to uppercase". How can I compare the cmd args including the whitespaces so that it will handle "-lower -upper" the same as "-upper"?
Thanks for helping!
You can simply clear lower when upper is set or the opposite, ie. the last argument clears the old ones:
int upper = 0;
int lower = 0;
for( arg = 1; arg < argc; arg++ )
{
printf("parsing %s\n", argv[arg]);
if( strcmp( argv[arg] , "-upper") == 0 )
{
printf("Now in upper mode\n");
lower = 0;
upper = 1;
continue;
}
if( strcmp( argv[arg] , "-lower") == 0)
{
printf("Now in lower mode\n");
upper = 0;
lower = 1;
continue;
}
fprintf(stderr,"Invalid command line option\n" );
return 0;
}
For command line utilities on Unix, it's generally the last flag on the command line that take precedence over the previous flags. Thus, ls -C -1 will list the files in the current directory in a single column, not in multiple columns, while ls -1 -C has the opposite effect.
You should not need to compare pairs or other combinations of command line flags.
If flag -upper is present, set a variable do_upper=1. If next you find -lower set do_upper=0.
Since -upper and -lower are mutually exclusive, it doesn't make sense to keep track of to variables in your code. If you do, you end up with silly things like
if (do_upper && !do_lower) { ... }
In fact, you could have your code default to one of these things, -lower for example, and only have a single flag, -upper, that changes the default behaviour. I don't know if that makes sense in your particular application though.
Personally, on Unix, I would stick with single-letter flags and use getopt() for command line parsing. It makes my life so much easier than trying to do string manipulation of user-provided data.
What I want to do is to get more arguments from the command line and get them output each one on a new line. How could I do that by keeping the same structure? I want also to get the -f output.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
int
main(int argc, char *argv[])
{
int nod1, opt;
int nsecs, nod2;
nsecs = 0;
nod2 = 0;
nod1 = 0;
while ((opt = getopt(argc, argv, "nf:")) != -1) {
switch (opt) {
case 'n':
nod1 = 1;
break;
case 'f':
nsecs = atoi(optarg);
nod2 = 1;
break;
default: /* '?' */
fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n",
argv[0]);
exit(EXIT_FAILURE);
}
}
printf("nod1=%d; nod2=%d; optind=%d\n", nod1, nod2, optind);
if (optind >= argc) {
fprintf(stderr, "Expected argument after options\n");
exit(EXIT_FAILURE);
}
printf("Output = %s\n", argv[optind]);
/* Other code omitted */
exit(EXIT_SUCCESS);
}
From a comment:
The arguments after the -f should be optional and I want to list every single one that has been passed under one another...
$ ./partitioner -n 4 -f Test1 Test2 Test3 Test4
Number:4
File names:
Output = Test1
Output = Test2
Output = Test3
Output = Test4
$
Having in account the comment made by Jonathan Leffler I edit my answer:
POSIX getopt() doesn't really handle optional arguments sensibly. GNU getopt() is a bit better, but not by much. Avoid them whenever you can.
I come out with a simple idea, but one that could solve your problem so here it is:
In argv you have a list of arguments ordered as they was in the commend line right? So, if you can find any -f in argv that means all arguments following -f until another option or the end of the argument list are the options you want to print.
From -f to another option(-g in this case):
./command -a A -b B -f one two tree -g G
From -f to the end.
./command -a A -b B -f one two tree
Here you have a helper function for doing that:
bool get_f_args(int argc, char *argv[], int &count, int* indexes)
{
bool f_found = false, parsing_f_args = false;
int collect_count = 0;
if (argc < 3) return false; // "./command -f" are just two values for argv
// we need at least 3.
// Check for every argument in the argument list.
for (int i = 1; i < argc; i++)
{
// If you found another option
// stop collecting args.
if (argv[i][0] == '-' && parsing_f_args) // options starts with '-' character.
parsing_f_args = false;
if (parsing_f_args)
indexes[count++] = i;
// If some is "-f" then the following are the
// ones you're looking for. We check for -f after
//
// indexes[count++] = i;
//
// in roder to avoid adding -f index to indexes.
if (strcmp("-f", argv[i]) == 0) {
parsing_f_args = true;
f_found = true;
}
}
return f_found;
}
And here is an example of use:
int main(int argc, char* argv[])
{
int count = 0;
int indexes[10];
bool f_found = get_f_args(argc, argv, count, indexes);
if (f_found){
for (int i = 0; i < count; i++)
printf("Ouput = %s\n", argv[indexes[i]]);
}
return 0;
}
My goal is to have the user view the history of entered commands (historyArray - done) and allow him to re-run any command in history, by entering history 1, history 2 where 1 and 2 is the number of the list of commands as printed-out from historyArray.
I have managed to obtain the index from the second parameter (history 1) of the user input. My question is now, how to execute that specific command obtained from history N?
So, for example:
hshell> test [Enter]
Command not found
hshell> history 1
Command not found
Here is my progress:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[])
{
int i=0; int j=0; int k=0;
int elementCounter = 0;
char inputString[100];
char *result=NULL;
char delims[] = " ";
char historyArray[30][20] = {0};
char tokenArray[20][20] ;
int tempIndex = 0;
char hCommand[2][20]={0};
do
{
j = 0;
printf("hshell>");
gets(inputString);
strcpy (historyArray[k], inputString);
k = (k+1) % 20;
if (elementCounter <= 20)
{
elementCounter++;
}
if (elementCounter == 21)
{
k = 20;
for (i=0; i<20; i++)
{
strcpy(historyArray[i], historyArray[i+1]);
}
strcpy (historyArray[19], inputString);
}
// Break the string into parts
result = strtok(inputString, delims);
while (result!=NULL)
{
strcpy(tokenArray[j], result);
j++;
result= strtok(NULL, delims);
}
if (strcmp(tokenArray[0], "exit") == 0)
{
return 0;
}
else if (strcmp(tokenArray[0], "history") == 0)
{
if (j>1)
{
tempIndex = atoi(tokenArray[1]);
strcpy(hCommand,historyArray[tempIndex-1]);
puts(hCommand);
// tempIndex = atoi(tokenArray[j]);
//puts(tempIndex);
}
else
{
//print history array
for (i=0; i<elementCounter-1;i++)
printf("%i. %s\n", i+1, historyArray[i]);
}
}
else
{
printf("Command not found\n");
}
}while (1);
}
hCommand is where I store the command as obtained from historyArray.
I am using a Windows machine.
After getting the name of the command you wanna execute I would suggest going through the system call exec. Take into account the exec replaces the current process image with the one you are going to execute. Otherwise you might be interested in fork.
EDIT#1 Then I believe you need this API. Note that I am not familiar which of those functions are equivalent to the ones I have provided in first place. With a bit time you could figure it out, right? :)
You can use the 'system' function in stdlib.h.
#include <stdlib.h>
int system(const char *command);
This function is included in both windows and *nix. You do not need to worry about calling fork or CreateProcess separately, this will take care of it for you. See the MSDN documentation for details.
In your code, you would write:
system(hCommand);
It will return when the command finishes (it is a synchronous call).