Command Line Arguments and Chars - c

What is the proper way to receive a command line argument from the user and compare it to a single char? For example if the user typed "Y" for yes run some function and "N" for no would run another function.
My main error is "comparison between ptr and integer" & "too few arguments to function call, expected 3 have 1"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void calculate();
void verify();
int main (int argc, char *argv[]) {
if(argc < 2){
printf("Please enter a mode of operation.\n");
return 0;
}
else if(argc > 2){
printf("Too many arguments supplied. Please try again.\n");
return 0;
}
else if(strncmp(argv[1] == 'c') == 0)
calculate();
else if(strncmp(argv[1] == 'v') == 0)
verify();
return 0;
}
void calculate(){
}
void verify(){
}

You're not calling strncmp() correctly. It should be:
strncmp(argv[1], "c", 1)
It takes 3 arguments: two strings and a limit. 'c' is a char, not a string, string literals are put in double quotes.
You can also write simply:
if (argv[1][0] == 'c')
This just checks the first character, so the user can type y or yes and they'll match y. If you want to match the whole argument and require it be just a single character, you should use strcmp() rather than strncmp():
if (strcmp(argv[1], "c") == 0)

This
strncmp(argv[1] == 'c') == 0
just is incorrect syntax. For starters the function strncmp requires three arguments and it deals with strings while in the avove call of the function there is supplied only one argument of type int. because the wrong expression argv[1] == 'c' has type int and is equal to 0.
I can suggest the following solution
else if ( argc > 2 || strlen( argv[1] ) != 1 ){
printf("Too many arguments supplied. Please try again.\n");
return 0;
}
else if ( argv[1][0] == 'c' )
calculateCRC();
else if ( argv[1][0] == 'v' )
verifyCRC();

Related

Why does my program return "Segmentation fault (core dumped)" when I remove a conditional in "else if"?

I've written a program that allows me take a command argument of 26 alpha chars to be used as a key at a latter stage of my program to encipher text.
To do this, I had to (a) verify user input alpha chars (b) chars are 26 and (c) none of them repeat.
I decided to put this all in one function(as you'll see below).
This has been successful.
How ever, when I was trying to check whether the key is indeed 26 chars (in my main function) using an else if condition else if (argc != 2 || (is_valid_key(argv[1]) != 1 || is_valid_key(argv[2]) == 2)) I thought that (is_valid_key(argv[1]) != 1 may be useless here sinced I used it in if.
When I did this(regardless of my command argument) my program returned Segmentation fault (core dumped) and I am trying to understand why.
The line causing the problem is else if in my main function.
for context:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int is_valid_key(string verify);
int main(int argc, string argv[])
{
// check whether the user passed a valid command line argument
if (argc != 2 || is_valid_key(argv[1]) == 1)
{
printf("Usage: ./substitution key\n"); //re-prompt user if they didn't enter a digit
return 1;
}
else if (argc != 2 || is_valid_key(argv[2]) == 2))
{
printf("Key must only contain alphabetic characters.\n");
return 1;
}
string plain_text = get_string("plaintext: ");
printf("%s\n", plain_text);
}
int is_valid_key(string verify)
{
int i;
for (i = 0; i < verify[i]; i++)
{
char ch = verify[i];
if (!isalpha(ch))
{
return 1; //return for alpha
}
}
if (i != 26)
{
return 2; //return for 26 chars
}
for (int a = 0; verify[a] != '\0'; a++)
{
for (int b = 0; b < a; b++)
{
// if 2 characters match, the key is not valid
if (verify[a] == verify[b])
return 3; // return for no repeat
}
}
return 4; //return all correct
}
The code is not checking the correct argument in main().
It's testing is_valid_key(argv[2]) but in your test case of only a single command-line parameter:
# ./main qwertyuiopasdfghjklzxcvbnm
There is only:
argv[0] which points to the string "./main" (or whatever your executable is called)
argv[1] which points to the string "qwertyuiopasdfghjklzxcvbnm"
The referenced argv[2] does not exist, and references "off the end" of argv, hence the segfault.
Changing this line to:
else if (argc >= 2 || is_valid_key(argv[1]) == 2) // <<-- HERE
{
printf("Key must only contain alphabetic characters.\n");
return 1;
}
will fix it.
Segmentation fault means that you are trying to access a memory location which you shouldn't have. The indexing of arguments starts from 0. And there should be only 2 command-line arguments in this program. So, you are trying to access the index 2 which is out of bounds currently. So, your program exits saying 'Segmentation Fault'.

Is there a better way to check if a pid input is all numbers?

So I have this program that takes in PID input and a character.
$ ./transmit 1111 a
My question is. If it were
$ ./transmit 111a x
since PID are all numbers I'd need to catch that.
given:
char *a[] = {"./transmit", "111a", "x"};
how would I check if "111a" is numeric only? isdigit only works if it's a character. Do I have to loop through the entire input?
char *err;
unsigned long pid = strtoul(argv[1], &err, 10);
if (*err || err == argv[1])
error();
if ((pid_t)pid != pid || (pid_t)pid <= 0)
error();
When you are really pedantic, you can check for ULONG_MAX and errno == ERANGE too, but because pid_t is smaller than unsigned long, this will be catched by the second check already.
You could maybe use the strspn() function:
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
if (argc > 1) {
if (argv[1][strspn(argv[1], "0123456789")] == '\0') {
puts("Yes, the first command line argument is all numeric.");
}
else {
puts("No, the first command line argument is not all numeric.");
}
}
else {
puts("Please provide an argument on the command line");
}
return 0;
}

Evaluation of expressions like if( argv[1][0] != ~'t')

I have to input a string to make the second if condition false and print "Correct!". Can it be done?
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if(argc != 2) { // argc is how long argv is, including that path-to-self
puts("You need to give me exactly one argument!");
return -1;
}
// argv[1] represents the command line input you have given to the program. Which is a string or a char array.
if(argv[1][0] != ~'t' || argv[1][1] != ~'h' || argv[1][2] != ~'e') {
puts("noooo!");
return 0;
}
else printf("Correct!\n");
return 0;
}
If an argument is passed to the program (i.e. the first argc != 2 check doesn't fail) then the expression argv[1][0] != ~'t' will always be true (as well as the other checks but those won't be made because of short-circuit evaluation).
The expression ~'t' will first be converted to an int (see this reference for why, the same thing will happen to argv[1][0]). That means you will have ~0x00000074 (if using the ASCII alphabet and 32-bit int). That will evaluate as 0xffffff8b.
It doesn't matter what argument you give your program, argv[1][0] will never be equal to 0xffffff8b and so the condition is true.

C - How to scan an int only entered after symbol

I am having difficulty scanning from user input an integer (and storing it) only if printed directly after a !:
char cmd[MAX_LINE/2 + 1];
if (strcmp(cmd, "history") == 0)
history(hist, current);
else if (strcmp(cmd, "!!") == 0)
execMostRecHist(hist, current-1);
else if (strcmp(cmd, "!%d") == 0)
num = %d;
else
{//do stuff}
I understand this is completely wrong syntax for strcmp(), but just as an example of how I am gathering user input.
strcmp doesn't know about format specifiers, it just compares two strings. sscanf does what you want: It tests whether a string has a certain format and converts parts of the string to other types.
For example:
int n = 0;
if (sscanf(cmd, " !%d", &num) == 1) {
// Do stuff; num has already been assigned
}
The format specifier %d tells sscanf to look for a valid decimal integer. The exclamation mark has no special meaning and matches only if there is an exclamation mark. The space at the front means that the command may have leading white space. Nothe that there may be white space after the exclam and before the number and that the number may well be negative.
The format specifier is special to the scanf family and related to, but different from the ´%dformat ofprintf`. Is usually has no meaning in other strings and certainly not when it is found unquoted in the code.
Don't you like writing a checker by yourself?
#include <ctype.h>
#include <stdio.h>
int check(const char *code) {
if (code == NULL || code[0] != '!') return 0;
while(*(++code) != '\0') {
if (!isdigit(*code)) return 0;
}
return 1;
}
/* ... */
if (check(cmd))
sscanf(cmd + 1, "%d", &num);
Use sscanf() and check its results.
char cmd[MAX_LINE/2 + 1];
num = 0; // Insure `num` has a known value
if (strcmp(cmd, "history") == 0)
history(hist, current);
else if (strcmp(cmd, "!!") == 0)
execMostRecHist(hist, current-1);
else if (sscanf(cmd, "!%d", &num) == 1)
;
else
{//do stuff}

do - while loop with multiple conditions in C

I am trying to make a while - do loop to exit when the user types "exit" or "quit". For some reason that I can not understand, I simply can not make it happen. If I apply one of these conditions separately it works just fine, but I need to have both conditions simultaneously.
I searched online and I found several examples even on different programming languages, for example, (PHP) Do-While Loop with Multiple Conditions, (Python) How to do while loops with multiple conditions, (C++) Using multiple conditions in a do…while loop, etc. But no matter what procedure I am following I can make it work with both conditions simultaneously.
The ideal solution would be do add uppercase conditions also, but I can work with that later on as soon as I solve this problem.
Maybe a fresh pair of eyes see something that I am missing.
Sample of code is given below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_CHARACTERS 250
#define MAX_USERNAME 12
#define MAX_USERS 1024
#define MIN_REQUIRED 3
#define MAX_PORT 65536
#define MIN_PORT 1
typedef struct rec {
char username[MAX_USERNAME];
char msg[MAX_CHARACTERS];
}RECORD;
/* Usage Instructions void because we do not have any return */
int help(void) {
printf("Usage: test.c [-s <arg0>] [-s <arg1>]\n");
printf("\t-s: a string program name <arg0>\n");
printf("\t-s: a string with a Nickname maximum characters %i <arg2>\n",MAX_USERNAME);
return (1);
}
int main(int argc, char **argv) {
if ( argc < MIN_REQUIRED ) {
printf ("Please follow the instructions: not less than %i argument inputs\n", MIN_REQUIRED);
return help();
}
else if ( argc > MIN_REQUIRED ) {
printf ("Please follow the instructions: not more than %i argument inputs\n", MIN_REQUIRED);
return help();
}
else {
RECORD *ptr_record;
ptr_record = (RECORD *) malloc (sizeof(RECORD));
if (ptr_record == NULL) {
printf("Out of memory!\nExit!\n");
exit(0);
}
char username_argv[MAX_USERNAME];
memset( username_argv, '\0', sizeof(username_argv) );
if (strlen(argv[2]) > 12 ) {
printf("Maximum characters for Nickname: %i\nPlease try again!\n", MAX_USERNAME);
exit(0);
}
strcpy( username_argv, argv[1] );
strncpy( (*ptr_record).username, username_argv, sizeof(username_argv) );
printf("Username pointer: %s\n", (*ptr_record).username);
do {
printf("Please enter your MSG: \n");
scanf ("%s", (*ptr_record).msg);
printf("User MSG: %s\n", (*ptr_record).msg);
//} while ((strcmp((*ptr_record).msg,"exit") != 0) || (strcmp((*ptr_record).msg, "quit") != 0));
//} while (strcmp((*ptr_record).msg, "exit") || strcmp((*ptr_record).msg, "quit") != 0);
//} while ((strcmp((*ptr_record).msg,"exit")) || (strcmp((*ptr_record).msg, "quit")) != 0);
//} while ((*ptr_record).msg != "quit" || (*ptr_record).msg != "exit");
} while (((*ptr_record).msg != exit) || ((*ptr_record).msg != ));
free(ptr_record);
return 0;
} /* End of else */
} /* End of main() */
Try:
} while ((strcmp((*ptr_record).msg,"exit") != 0) &&
(strcmp((*ptr_record).msg,"quit") != 0));
You want NOT (A OR B), not NOT A OR NOT B. Remember De Morgan's laws that say that NOT (A OR B) is the same as NOT A AND NOT B.
Using || is wrong as you will always have at least one condition (operand of ||) or another to be true.
Imagine you string is "exit" then:
(strcmp((*ptr_record).msg,"exit") != 0)
is false
but
(strcmp((*ptr_record).msg,"quit") != 0)
is true.
It means 0 || 1 which yields 1. Same (in the opposite) for the "quit" string (1 || 0 which yields 1). For other strings, both operand of || will be 1 and 1 || 1 yields 1.
It doesn't work, because you want to do the loop while the string is different from "exit" and the string is different from "quit".
If you use an OR, the condition will always be true, because the string can't have both values at the same time.
do {
printf("Please enter your MSG:\n");
scanf ("%s", (*ptr_record).msg);
printf("User MSG: %s\n", (*ptr_record).msg);
} while ((strcmp((*ptr_record).msg, "exit") != 0) && (strcmp((*ptr_record).msg, "quit") != 0));
All of the lines in your example use the logical OR operator || to test both conditions. Try to use the logical AND operator, &&, instead.
The problem with OR is that the way you've got it set up, the condition will always pass, so the application will never quit. If the user typed "quit", then the condition passes because he/she didn't type "exit". Likewise, if the user typed "exit", then he/she didn't type "quit", so the condition still passes. And, of course, if he/she didn't type either one, then the condition passes. That doesn't leave you with any way out of the loop.
Try out this. You should use the && operator because both the conditions need to be true to continue.
do {
printf("Please enter your MSG: \n");
scanf ("%s", (*ptr_record).msg);
printf("User MSG: %s\n", (*ptr_record).msg);
} while (((*ptr_record).msg != "exit") && ((*ptr_record).msg != "quit"));

Resources