The following program is supposed to read from the command line( using argv[]) and execute either one of three functions:
right(decides whether or not a triangle is a right triangle based on the side length: by typing mu -r [sidea] [sideb] [sidec]
findtext:(finds the sequence of a string inside of a given file: (ie,"hello") and lists the line number where it found it.
count: (counts the number of tabs, words and backspaces).
Findtext.c is functioning as it should:
for example if i type ./mu -f [string] [file.txt]
it successfully list the file and the line number in the file in which it was found.
but when I run the same code with the -r(right) option it gives me the following segmentation fault:
Program name: ./mu
3
Segmentation fault (core dumped)
where am i going wrong in this code?
#include <stdio.h>
#include <stdlib.h>
//#include "count.h"
//#include "right.h"
//#include "findtext.h"
#define STAND_ALONE 1
void right(int, char **);
void count(int, char **);
void findtext(int, char **);
#ifdef STAND_ALONE
int main(int argc, char *argv[])
{
printf("\n");
printf("Can only use one option(-f, -c, -r) at once. sorry!\n");
printf("\n");
printf("Program name: %s\n", argv[0]);
while ((argc > 1) && (argv[1][0] == '-'))
{
switch (argv[1][1])
{
case 'f': // findtext.c
printf("%s\n",&argv[1][2]);
findtext(argc, argv);
break;
case 'r': // right.c
printf("%s\n",&argv[1][2]);
right(argc, argv);
break;
case 'c': // count.c
printf("%s\n",&argv[1][2]);
count(argc,argv);
break;
default:
printf("Wrong Argument: %s\n", argv[1]);
}
++argv;
--argc;
}
return (0);
}
#endif
void right(int argc, char *argv[]){
int a;
int b;
int c;
int largest;
int a2;
int b2;
int c2;
/*if(argc != 4){
printf("please enter 3 sides, only \n");
} */
a = atoi(argv[2]);
b = atoi(argv[3]);
c = atoi(argv[4]);
//printf("argv2:%s ",argv[2]);
if((a <= 0 )|| (b <= 0) || (c <= 0))
printf("Only positive values allowed\n"); exit(0);
a2 = (a*a);
b2 = (b*b);
c2 = (c*c);
if((c > a) && (c > b))
largest = c;
if((b > a) && (b > c))
largest = b;
if((a > b) && (a > c))
largest = a;
if(largest == a){
printf("HEy hey hey!");
if((b2 + c2) == a2){ printf("%s %s %s is a right triangle\n",argv[2],argv[3],argv[4]); }
else{printf("%s %s %s is not a right triangle\n",argv[3],argv[2],argv[4]);}
}
if(largest == b){
printf("HEy");
if((a2 + c2) == b2){ printf("%s %s %s is a right triangle\n",argv[2],argv[3],argv[4]); }
else{printf("%s %s %s is not a right triangle\n",argv[2],argv[3],argv[4]);}
}
if(largest == c){
printf("yo");
if((a2 + b2) == c2){ printf("%s %s %s is a right triangle\n",argv[2],argv[3],argv[4]); }
else{printf("%s %s %s is not a right triangle\n",argv[2],argv[3],argv[4]);}
}
} /* end method right() */
void findtext(int argc, char *argv[]){
FILE *fin;
char buffer[100];
int counter;
char *ptr = buffer;
char *result;
//if(argc != 3) {printf("Usage: %s filename argc:%d\n", argv[0], argc); exit(1);}
fin = fopen(argv[3], "r");
if(!fin) {printf("Unable to open %s\n", argv[2]); exit(1); }
counter = 0;
while (fgets(buffer, 99, fin)){
counter = counter + 1;
if(strstr(ptr,argv[2])){
printf("%d. %s", counter, ptr);
printf("\n");
}
}
fclose (fin);
}
void count(int argc, char **argv){
FILE *fin;
int lcounter = 0;
int count = 0;
char name[100];
char ch;
int word = 0;
int nchar = 0;
fin = fopen(argv[1],"r"); // open file
if(fin==0){
printf("Could not find specified file.\n");
exit(0);
}
while((ch = getc(fin)) != EOF)
{
nchar++;
if(ch == '\n')
lcounter++;
if(isspace(ch) || ch == '\t' || ch == '\n')
word++;
}
printf("number of characters: %d\n",nchar);
printf("Lines: %d\n",lcounter);
printf("words: %d\n",word);
printf("\n");
fclose(fin);
}
if((a <= 0 )|| (b <= 0) || (c <= 0))
printf("Only positive values allowed\n"); exit(0);
Beware to the if-block. You are not using braces. At this point of the right function the program will end, in any case.
Of course this is not the bug you are looking for but it offers a starting point: the bug must necessarily be before this point.
You may need to debug what goes on here:
a = atoi(argv[2]);
b = atoi(argv[3]);
c = atoi(argv[4]);
See #Domenico De Felice answer.
Add #include <string.h>, #include <ctype.h>
You are accessing chars in argv[1] without knowing it is long enough to have those characters. while ((argc > 1) && (argv[1][0] == '-')). printf("%s\n",&argv[1][2]); should print only a line feed as argv[1] is "-r".
in right(), you do c = atoi(argv[4]); without knowing 5 args exist.
please review the need for your interesting code ++argv; --argc;. I really don't think these are going to do what you may want.
I think you are crashing because you do not always put the numbers of arguments (command parameters) that your code hopes to read. Increase your validation of program parameters before using them.
5.
You are crashing because you are trying to increment argv which is an array variable, not a pointer. So you cannot do argv++;
as a good programming principle, (although it is optional) argc, and argv should NOT be touched; you should use point to them using other variables if you want to run through them.
There are many other uglinesses in the code that try to access pointers and addresses without actually confirming whether they are there.
However, to begin with argv is where you are seg-faulting.
Related
I have this little piece of C code, which takes a set of command-line arguments --help, -h, -d and -o (each correspondingly representing "Help", "Hexadecimal", "Decimal", "Octal"), and I am calling certain functions depending in which argument is passed, -h will call hexaFlag(), -dh will call hexaFlag() and decFlag(). However, in order to do this, I am employing a block of if else that is messy. Is there any less convoluted way to achieve this? I was told to use a switch statement, but I do not know how I could use it here considering I am checking for different conditions each time.
main() function of the code I am referring to:
int main(int argc, char* argv[]){
if(argc == 1){
printf("%s",usage());
printf("Use --help for more options.\n");
}
else if(strcmp(argv[1], "--help") == 0){
printf("%s", usage());
printf("Options are:\n -h = Hexadecimal values\n -d = Decimal values\n -o = Octal values\n --help = Shows this message\n");
}
else if(strchr(argv[1], '-') != NULL){
if(strchr(argv[1], 'h') != NULL){
hexaFlag(argc, argv);
}
if(strchr(argv[1], 'd') != NULL){
decFlag(2, argc, argv);
}
if(strchr(argv[1], 'o') != NULL){
octaFlag(argc, argv);
}
}
else {
decFlag(1, argc, argv);
}
return 0;
}
I think you should modify the function signature a bit to make them consistent. That would allow you to do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
void hexaFlag(int x, int argc, char **argv) { printf("%d:%s\n", x, __func__); }
void decFlag(int x, int argc, char **argv) { printf("%d:%s\n", x, __func__); }
void octaFlag(int x, int argc, char **argv) { printf("%d:%s\n", x, __func__); }
struct args {
void (*f)(int, int, char **);
int x;
};
void
parse_args(int argc, char *argv[], struct args *A)
{
int c;
if( argc == 1 || !strcmp(argv[1], "--help") ){
printf("usage: %s [-hdo] [--help] arg [arg...]\n",
basename(argv[0]));
exit(EXIT_SUCCESS);
}
A->f = decFlag;
A->x = 1;
while( (c = getopt(argc, argv, "hdo")) != -1 ) {
switch( c ){
case 'h': A->f = hexaFlag; break;
case 'd': A->f = decFlag; A->x = 2; break;
case 'o': A->f = octaFlag; break;
default: exit(EXIT_FAILURE);
}
}
}
int
main(int argc, char* argv[])
{
struct args A;
parse_args(argc, argv, &A);
A.f(A.x, argc, argv);
return 0;
}
You should probably not pass argc/argv directly to the worker functions, but should extract what you need from them into the struct args in parse_args().
My basic switch processing (example), very functional but maybe not beautiful, is:
int main (int argc, char *argv[])
{
int i=0, number, files=0;
/* process switches; other prms are considered files (2)
that are opened for input/output. Files not specified
are taken as stdin and stdout. Prms can be in any order.
*/
while (++i < argc)
switch (argv[i][0]) {
case '-': while (*++argv[i])
switch (*argv[i]) {
case 'N':
++argv[i]; number= 0;
while (isdigit(*argv[i]))
number = number *10 + *argv[i]++ - '0';
argv[i]--;
break;
case 'P' : printf ("Prm: P\n"); break;
case 'O' : printf ("Prm: O\n"); break;
case 'o' : printf ("Prm: o\n"); break;
default :
printf ("Bad switch %c, ignored.\n",*argv[i]);
}
break;
default :
switch (files) {
case 0: if ((inf=fopen(argv[i],"r")) == 0)
pexit("Error opening input file %s.", argv[i]);
files++; break;
case 1: if ((outf=fopen(argv[i],"w")) == 0)
pexit ("Error creating output file %s.", argv[i]);
files++; break;
case 2: fprintf (stderr,"Too many file arguments: %s ignored.\n",argv[i]);
break;
} /* end switch files */
} /* end switch argc */
if (files <1) inf = stdin;
if (files <2) outf = stdout;
I am trying to read inputs from a file that contain strings such as "Jane 30", "Chris 40", and so on, line by line. I then need to store each name with it's corresponding number in the same index of different arrays, so "Jane" in one array with index 0, and 30 in an integer array with index 0.
This is the code I have so far, but I am struggling to figure out how to extract the integers into a separate array, and the characters into another. Please help.
#include stdio.h
#include stdlib.h
#include DarrensInfo.
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char names[150];
int change[50];
int x, y;
while(!feof(coinfile)){
fgets(names, 150, coinfile);
y = 0;
for(x=0; names[x]; x++){
if(names[x] <= '0' && names[x] <= '9'){
change[y] = names[x];
y++;
}
}
}
fclose(coinfile);
return 0;
}
#define COINS_MAX_LINES 150
#define MAX_LINE_LENGTH 100
#define MAX_NAME_LENGTH 50
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char line[MAX_LINE_LENGTH];
char names[COINS_MAX_LINES][MAX_NAME_LENGTH];
int change[COINS_MAX_LINES];
int lineno = 0;
int i = 0;
while(fgets(line, MAX_LINE_LENGTH, coinfile))
{
sscanf(line, "%s %d", names[lineno], &change[lineno]);
++lineno;
}
fclose(coinfile);
for (i = 0; i<lineno;++i)
printf("Name = %s Change = %d\n", names[i], change[i]);
return 0;
}
After the end of the while loop, names array & change arrays will contain what you want. I have printed it out in the second loop
You need a array of character arrays. Define the variable names as names[150][30], assuming the length of each name will not exceed more than 30 characters.
Use a combination of fgets and fscanf to parse the line of entry in the file into separate variables as you desired
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char names[150][30];
int change[150];
int x = 0, y, i;
char buf[100];
while(!feof(coinfile)){
if(fgets(buf, 100, coinfile) != NULL) {
sscanf(buf, "%s %d", names[x], &change[x]);
x++;
}
}
fclose(coinfile);
puts("-------");
for (int i = 0; i < x; i++)
printf("%s %d\n", names[i], change[i]);
return 0;
}
I am trying to read inputs from a file that contain strings such as "Jane 30", "Chris 40", and so on, line by line
You're reading a file that might contain strings such as "Jane 30", "Chris 40", and so on; but might also contain millions of typing mistakes and/or other errors; and therefore you need to detect errors and clearly inform the user what the error is so that they can easily understand the problem, then find the mistake, then fix it.
For this reason none of the C library functions are ever useful.
Instead build a parser as a finite state machine. For example (untested):
// State
int state = 0;
int column = 0;
int line = 1;
char current_name[MAX_NAME_LENGTH];
int name_length;
int number;
// Main loop
for(;;) {
int c = fgetc(file);
column++;
switch(state) {
case 0: /* At start of new line */
if(c == FEOF) {
return OK;
} else if(isdigit(c)) {
printf("ERROR: Number found at start of line (missing name), on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isalpha(c)) {
name_length = 0;
current_name[name_length++] = c;
state = 1;
} else if(c == '\n') {
line++
} else if(isspace(c)) {
} else {
printf("ERROR: Bad character at start of line, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 1: /* In middle of name */
if(c == FEOF) {
printf("ERROR: File ends in the middle of a name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
printf("ERROR: No whitespace between name and number, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isalpha(c)) {
if(name_length >= MAX_NAME_LENGTH) {
printf("ERROR: Name too long (max length is %d), on line %d at column %d\n", MAX_NAME_LENGTH, line, column);
return NOT_OK;
}
current_name[name_length++] = c;
} else if(c == '\n') {
printf("ERROR: No number after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isspace(c)) {
state = 2;
} else {
printf("ERROR: Bad character in middle of name, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 2: /* Between name and number */
if(c == FEOF) {
printf("ERROR: File ends after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
number = c - '0';
state = 3;
} else if(c == '\n') {
printf("ERROR: No number after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isspace(c)) {
} else {
printf("ERROR: Bad character after name, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 4: /* In middle of number */
if(c == FEOF) {
printf("ERROR: File ends in middle of number, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
if(number > INT_MAX / 10) {
printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
return NOT_OK;
}
number *= 10;
if(number > INT_MAX - (c - '0') ) {
printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
return NOT_OK;
}
number += c - '0';
} else if(c == '\n') {
create_new_entry(current_name, name_length, number);
line++
state = 0;
} else if(isspace(c)) {
state = 5;
} else {
printf("ERROR: Bad character after number, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 5: /* Trailing white space before end of line */
if(c == FEOF) {
printf("ERROR: File ends between number and end of line, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(c == '\n') {
line++
create_new_entry(current_name, name_length, number);
state = 0;
} else if(isspace(c)) {
} else {
printf("ERROR: Unknown characters between number and end of line, on line %d at column %d\n", line, column);
return NOT_OK;
}
}
}
If you're trying to parse names that might look like "King Charles 3", it will be difficult to use scanf. It's not actually that difficult to do this sort of thing without using static sized buffers, and that's a good practice to get used to. You'll want to avoid fgets, since that is difficult to use without a fixed size. Note that growing arrays is notoriously difficult, so I make no claims as to the correctness of the following. One has to do this sort of exercise every few months to be reminded why we don't do this sort of thing in C:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE * Fopen(const char *, const char *);
void * Realloc(void *, size_t);
/* Remove trailing whitespace */
void trim_white(char *b, char *end)
{
while( end > b && isspace(*--end)) {
*end = '\0';
}
}
int
main(int argc, char **argv)
{
char *path = argc > 1 ? argv[1] : "stdin";
FILE *coinfile = argc > 1 ? Fopen(path, "r") : stdin;
size_t asize = 16;
size_t bsize = 0;
char *buf = NULL;
char **names = Realloc(NULL, asize * sizeof *names);
long *change = Realloc(NULL, asize * sizeof *change);
unsigned line_number = 0;
ssize_t char_read;
while( buf = NULL, (char_read = getline(&buf, &bsize, coinfile)) != -1) {
char *space;
char *end;
trim_white(buf, buf + char_read);
space = strrchr(buf, ' ');
if(space == NULL) {
fprintf(stderr, "Invalid input on line %d (no space)\n", line_number + 1);
exit(EXIT_FAILURE);
}
change[line_number] = strtol(space + 1, &end, 10);
if(*end != '\0') {
fprintf(stderr, "Invalid input on line %d at '%s'\n", line_number + 1, space + 1);
exit(EXIT_FAILURE);
}
*space = '\0';
names[line_number] = buf;
if(++line_number == asize) {
asize *= 2;
names = Realloc(names, asize * sizeof *names);
change = Realloc(change, asize * sizeof *change);
}
}
return EXIT_SUCCESS;
}
FILE *
Fopen(const char *path, const char *mode) {
FILE *fp = fopen(path, mode);
if( fp == NULL ) { perror(path); exit(EXIT_FAILURE); }
return fp;
}
void *
Realloc(void *buf, size_t s)
{
buf = realloc( buf, s );
if( buf == NULL) { perror("realloc"); exit(EXIT_FAILURE); }
return buf;
}
I want to replicate the -s option of the cat linux command. It basically removes every empty line adjacent to another making the output equally spaced. How can i approach this without making a temporary file?
Here's my cat command:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <getopt.h>
#define BUF_SIZE 1024
void formattedLineout(int *index, char buffer[]){
printf (" %d\t%s", *index, buffer);
(*index)++;
}
void bprint(int *index, char buffer[]){
if (strcmp(buffer,"\n") == 0){
printf (" %s", buffer);
}
else {
formattedLineout(index, buffer);
}
}
void outputLine(int *index, char buffer[], int bflag, int nflag){
if (nflag){
formattedLineout(index, buffer);
}
else if (bflag){
bprint(index, buffer);
}
else{
printf("%s", buffer);
}
}
int readStdin(int index, int bflag, int nflag){
char buffer[BUF_SIZE];
while(fgets(buffer, BUF_SIZE, stdin)){ //reads from the standard input and prints the input
outputLine(&index, buffer, bflag, nflag);
}
return index; //returns the incremented index to perpetuate its use
}
int readFile(char* filename, FILE* fp, int index, int bflag, int nflag){
char s[BUF_SIZE];
if (fp==NULL){ //in case the file doesn't exist
printf("%s: No such file or directory\n", filename);
exit(1);
}
while ((fgets(s, BUF_SIZE, fp))){ //printing loop
outputLine(&index, s, bflag, nflag);
}
return index;
}
void readArgs(int argc, char* argv[], int bflag, int nflag){
FILE* fp;
int index = 1; //line index. to be used in case -b or -n is passed as an argument
if (bflag == 1 && nflag == 1){ //if -b and -n are passed as argument, b overrides n
nflag = 0;
}
for (int i=optind; i<argc; i++){
if (*argv[i] == '-'){ //in case of '-' in argv[i], reads from stdin and prints
index = readStdin(index, bflag, nflag);
clearerr(stdin);
}
else { //prints the contents of the file in *argv[i]
fp = fopen(argv[i], "r");
index = readFile(argv[i], fp, index, bflag, nflag);
fclose(fp);
}
}
}
int main(int argc, char* argv[]){
int option; //option passed as argument
int bflag = 0; //-b option deactivated by default
int nflag = 0; //-n option deactivated by default
opterr = 0; //deactivates getopt's default error messages
//checks if there are options passed as argument and updates their flags
while ((option = getopt(argc, argv, "bn")) != -1){
switch (option){
case 'b':
bflag = 1;
break;
case 'n':
nflag = 1;
break;
case '?': //in case there was some problem
exit(1);
}
}
if (argc<2 || optind == argc){ //if there are no arguments or if there are only options
readStdin(1,0,0);
return 0;
}
readArgs(argc, argv, bflag, nflag); //otherwise
return 0;
}
I want to be able to mix this functionality with the other options i implemented (like -n and -b).
Any suggestion?
Parameters:
linux LF not linux CRLF
skip whitespace to skip whitepsace in the empty lines
void skip(int skipw, int linux)
{
int count = 0;
int ch;
while((ch = fgetc(stdin)) != EOF)
{
if((ch == '\r' && !linux) || ch == '\n')
{
if(count <= (!linux * 2))
{
count++;
printf("%c", ch);
}
}
else
{
if(skipw && isspace(ch)) continue;
count = 0;
printf("%c", ch);
}
}
}
I am trying to read only the first character of each line using scanf.
With this input:
c FILE: myciel3.col
c SOURCE: Michael Trick (trick#cmu.edu)
c DESCRIPTION: Graph based on Mycielski transformation.
c Triangle free (clique number 2) but increasing
c coloring number
p edge 11 20
Sorry for my bad english.
int main(int argc, char *argv[]) {
char option;
int countC = 0;
int countP = 0;
while(scanf("%c",&option) != EOF) {
if(option == 'c') countC++;
else if (option == 'p') countP++;
}
printf("c: %d\tp: %d\n",countC, countP);
return (0);
}
I expect the output C:5 and P:1, but the actual output is c:15 p:2
Your code reads every character in your input, not the first character of each line.
Use fgets or any other function that gets a line.
#include <stdio.h>
int main(int argc, char *argv[]) {
char option[255];
int countC = 0;
int countP = 0;
while(fgets(option, 255, stdin) != NULL) {
if(option[0] == 'c') countC++;
else if (option[0] == 'p') countP++;
}
printf("c: %d\tp: %d\n",countC, countP);
return (0);
}
I have this small program in C that reads through a file a compares word by word,
how can I assure that words like "this," won't be read as a word? I would like it to read as "this"
int main(int argc, char *argv[]) {
if(argc != 3)
{
printf("Usage: ./sw <word> <filename> \n");
exit(1);
}
char* word = argv[1];
const char* filename = argv[2];
FILE* file = fopen(filename, "r");
if(file == NULL)
{
printf("Could not open file\n");
exit(1);
}
//Assuming one word can not have more than 250 chars
char w[250], check_eof;
do
{
check_eof = fscanf(file, "%s", w);
if(strcmp(word, w) == 0)
{
printf("W : %s \n", w);
}
} while(check_eof != EOF);
fclose(file);
return 0;
}
You can check if a char belongs to a word like this
int c = fgetc(file);
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
// c belongs to a word
word[n++] = c;
} else {
// end of word
if (strncmp(word, w, n) == 0) {
// word and w match!
}
}
If you #include <ctype.h>, then you can call isalpha(c) instead to test it.
In the code below, I use isalpha() and I copy the result string in a new buffer named res. However, this procedure can be done in-place, but I'll leave now for the sake of simplicity.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> // for isalpha()
int main(int argc, char *argv[]) {
char* word = "this";
const char* filename = "test.txt";
FILE* file = fopen(filename, "r");
if(file == NULL)
{
printf("Could not open file\n");
exit(1);
}
//Assuming one word can not have more than 250 chars
// ATTENTION, it is 249 chars, do NOT forget of the null terminator
char w[250], res[250];
int check_eof; // should be of type int, for EOF checking
do
{
check_eof = fscanf(file, "%s", w);
// what if 'word' appears as the last word
// in the file? You should check for eof
// right after fscanf()
if(check_eof == EOF)
break;
int i = 0, j = 0;
while (w[i]) // parse what we read
{
if (isalpha(w[i]))
res[j++] = w[i]; // keep only the alphabetic chars
i++;
}
res[j] = '\0'; // it should be a null terminated string
if(strcmp(word, res) == 0) // compare to 'res' now
{
printf("W : %s \n", res);
}
} while(1); // the terminating step is inside the body now
fclose(file);
return 0;
}