C: Strings, segmentation error - c

The following piece of code is scanning for command line arguments for valid sentences that contains the words 'Int' or 'Float'.
I have debugged it for hours trying to locate where the error is occurring to no avail.
Any help would be appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
bool process_input(int, char *[]);
bool first_word_correct(char *);
void print_string(char *);
int main(int argc, char *argv[]) {
if (!process_input(argc, argv))
printf("Incorrect input.\n");
else
printf("CORRECT input.\n");
return EXIT_SUCCESS;
}
bool process_input(int argc, char *argv[]) {
char *word;
int i, j;
bool is_new_sentence = true;
i = 0;
j = 0;
while (++i < argc) {
// check the first word - must be "A" or "An"
if (is_new_sentence) {
if (!first_word_correct(argv[i])) {
return false;
}
is_new_sentence = false;
}
// we're checking subsequent words of the same sentence
else if (!is_new_sentence) {
// is this the last word?
strcpy(word, argv[i]);
if (word[strlen(word) - 1] == '.') {
is_new_sentence = true;
}
switch(is_variable_name(word)) {
case 1:
printf("int found!\n");
break;
case 2:
printf("float found!\n");
break;
}
}
}
return true;
}
bool first_word_correct(char *word) {
if (strcmp(word, "A") == 0 || strcmp(word, "An") == 0) {
return true;
}
else {
return false;
}
}
/*
*int 1
*float 2
*/
int is_variable_name(char *word) {
if ((strcmp(word, "int") == 0) || (strcmp(word, "int.") == 0))
return 1;
if ((strcmp(word, "float") == 0) || (strcmp(word, "float.") == 0))
return 2;
return -1;
}

You are not allocated memory for word before using it. Try like
else if (!is_new_sentence) {
// is this the last word?
word=malloc(100);// size is based on your argv[i]
strcpy(word, argv[i]);
if (word[strlen(word) - 1] == '.') {
is_new_sentence = true;
}

Related

C program in CLion runs perfectly in Debug mode but returns exit code -1073741819 (0xC0000005) when executed normally

I am doing the Advent of Code, and I am trying to do it all in C. I am currently on day three, and I kind of solved it, but as the title says it behaves very strangely in my IDE CLion. Here is the objective.
I would very much like to know why it is not running properly, and finding out why appears to be beyond my capability.
This is my code:
//
// Created by gusta on 2022-12-06.
//
#include "aocio.h"
#include <string.h>
int cexists(char* cp, char c, int length);
char getDuplicate(char* line);
int main() {
FILE* fptr = openFile("../Inputs/day3.txt");
char* line;
while (readLine(fptr, &line)) {
char c = getDuplicate(line);
putchar(c);
}
return 0;
}
char getDuplicate(char* line) { // Returnerar STRING_END om ingen fanns
unsigned int length = strlen(line);
char letters[length/2];
char* cp = &letters[0];
for (int i = 0; i < length/2; i++) {
letters[i] = ' ';
}
for (int index = 0; line[index] != STRING_END; index++) {
if (index < length/2) {
int i_char = cexists(letters, line[index], length/2);
if (i_char == -1) {
*cp = line[index];
cp++;
}
}
else {
if (cexists(letters, line[index], length/2) != -1) {
return line[index];
}
}
}
}
int cexists(char* cp, char c, int length) {
for (int i = 0; i < length; i++) {
if (cp[i] == c) {
return i;
}
}
return -1;
}
Here is aocoi.h (advent of code input output.h):
//
// Created by gusta on 2022-12-01.
//
#ifndef ADVENTOFCODE_AOCIO_H
#define ADVENTOFCODE_AOCIO_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define SIGN_ASCII 45
#define TRUE 1
#define FALSE 0
#endif //ADVENTOFCODE_AOCIO_H
#define STRING_END '\0'
#define NUMBERS_ASCII 48
char* prompt(const char* question) {
printf(question);
char* string = malloc(1);
int curChar = 0, index = 0;
while (curChar != '\n') {
curChar = getchar();
if (curChar == '\n'){
string[index] = STRING_END;
}
else{
if (index > 0) {
string = (char*) realloc(string, index+1);
}
string[index] = curChar;
}
index++;
}
return string;
}
FILE* openFile(const char* fileName) {
FILE *fptr;
fptr = fopen(fileName, "r");
if (fptr == NULL) {
printf("Big fat file error!!\n");
fclose(fptr);
getchar();
exit(-1);
}
return fptr;
}
char readLine(FILE* fptr, char** line) {
int index = 0, end = 0;
char* string = (char *) malloc(1);
char curChar;
do {
end = fscanf(fptr, "%c", &curChar);
if (end == EOF) {
string[index] = STRING_END;
fclose(fptr);
*line = string;
return FALSE;
}
if (curChar == '\n') {
string[index] = STRING_END;
*line = string;
return TRUE;
}
else {
if (index > 0) {
string = (char *) realloc(string, index + 1);
}
string[index] = curChar;
index++;
}
} while (end != EOF);
}
int parseToInt(char* string) {
int numberLength = 0, number = 0;
int sign = FALSE;
for (int index = 0; string[index] != STRING_END; index++) {
numberLength++;
}
for (int index = numberLength-1; index >= 0; index--) {
char curChar = string[index];
if (index == 0 && curChar - SIGN_ASCII == 0) {
sign = TRUE;
continue;
}
if (curChar - NUMBERS_ASCII >= 0 && curChar - NUMBERS_ASCII <= 9) {
int num = (int) (curChar - NUMBERS_ASCII);
num *= (int)(pow(10, numberLength-index-1));
number += num;
}
else {
printf("Felaktig inmatning. parseInt kan bara ta in tal"); // Invalid input. parseInt can only take in numbers
getchar();
exit(-1);
}
}
if (sign == TRUE) {
number *= -1;
}
return number;
}
Through searching the web I found that the error code should mean stack overflow (ha!) or something like that, but I cannot for the life of me find any place in my code where that could occur. All pointers and stuff should be automatically freed - is there something I have missed?
In readLine():
if (index > 0) {
string = (char *) realloc(string, index + 1);
}
string[index] = curChar;
After this section, the buffer has size index+1, therefore string[index] is the last element you can write to. Afterwards, you do
index++;
Now, writing to string[index] is out of bounds, resulting in a buffer overflow. This is what happens when an EOF or EOL is detected.

C getting unknown input length

My question was answered in another previous old question here but it was answered with code only & no explanation link.
I would love an answer why the code there works & mine not (what I'm missing?), this is mine:
#include <stdio.h>
#include <stdlib.h>
void get_sentence(char* sentence) {
char end_of_input = 'a', * temp_pointer = NULL;
for (unsigned short int input_index = 0; end_of_input != '\n'; input_index -= -1) {
temp_pointer = sentence;
sentence[input_index] = end_of_input = getchar();
printf("%d: %c\n", (1 + input_index), end_of_input);
if (end_of_input == '\n') {
printf("end of input\n");
sentence[input_index] = '\0';
return;
}
sentence = (char*)realloc(sentence, ((int)(input_index + 2)) * sizeof(char));
if (sentence == NULL) {
free(temp_pointer);
return;
}
}
}
void main(int argc, char const* argv[]) {
char* sentence = malloc(sizeof(char));
if (sentence == NULL) {
printf("blyat");
exit(1);
}
get_sentence(sentence);
printf("Answer = ");
for (unsigned short int run = 0; sentence[run] != '\0'; run -= -1)
printf("%c", sentence[run]);
printf("\n");
free(sentence);
exit(0);
}
In the answer code he also does +=16 which is a waste of memory isn't it?.
IT IS WORKING THANK YOU "#Johnny Mopp" ♥.
#include <stdio.h>
#include <stdlib.h>
char* get_sentence() {
char end_of_input = 'a', *sentence = malloc(sizeof(char)), *temp_pointer = NULL;
for (unsigned short int input_index = 0; end_of_input != '\n'; input_index -= -1) {
temp_pointer = sentence;
sentence[input_index] = end_of_input = getchar();
if (end_of_input == '\n') {
sentence[input_index] = '\0';
return sentence;
}
sentence = (char*)realloc(sentence, ((int)(input_index + 2)) * sizeof(char));
if (sentence == NULL) {
free(temp_pointer);
return NULL;
}
}
}
int main(int argc, char const* argv[]) {
char* sentence = get_sentence(&sentence);
if (!sentence)
exit(1);
printf("Answer: %s\n", sentence);
free(sentence);
exit(0);
}

How is "++argv" correct in this program from "The C programming Language by Dennis Ritchie"?

The main function has second parameter as char * argv[] , that is an array of pointers.In the same book it has been mentioned that we can't use operations like "++" on array names. But, here we can see ++argv has been used.
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char* line, int max);
/* find: print lines that match pattern from 1st arg */
int main(int argc, char* argv[])
{
char line[MAXLINE];
long lineno = 0;
int c, except = 0, number = 0, found = 0;
while (--argc > 0 && (*++argv)[0] == '-')
{
while (c = *++argv[0])
{
switch (c)
{
case 'x': {
except = 1;
}
break;
case 'n': {
number = 1;
}
break;
default: {
printf("find: illegal option %c\n", c);
argc = 0;
found = -1;
}
break;
}
}
}
if (argc != 1)
{
printf("Usage: find -x -n pattern\n");
}
else
{
while (getline(line, MAXLINE) > 0)
{
lineno++;
if ((strstr(line, *argv) != NULL) != except)
{
if (number)
{
printf("%ld:", lineno);
}
printf("%s", line);
found++;
}
}
}
return found;
}
An array as a parameter to a function is automatically converted to a pointer. So this:
char *argv[]
Is exactly the same as:
char **argv
That's why you're allowed to do argv++.

Trying to create a program to check palindromes and semordinlap

I'm trying to create a program to check if a given string is a palindrome or an emordinlap (or reverse pair), however when running my program it's outputting that a string is a palindrome but not a reverse pair (such as racecar should be both).
I've been debugging and starring at this compute screen for 3 hours, and I have no idea what's going on. My first instinct was that it's coming from the checkPali function, so I assigned it to pointers, no luck, same issue.
My second guess was to put printf statements everywhere (I cleaned them up), no luck there either.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
static void *helpPage(void)
{
puts("usage: epi -h -t -s [string] [file]\n");
puts("-h Print this help and exit\n");
puts("-t Run the test strings.\n");
puts("-s Input a string to check if it is either a palindrome or");
puts(" a emordinlap, followed by the path to a file.");;
}
static char *reverse(char *str)
{
// reverse a given string
char tmp, *src, *dst;
size_t len;
if (str != NULL)
{
len = strlen(str);
if (len > 1)
{
src = str;
dst = src + len - 1;
while (src < dst)
{
tmp = *src;
*src++ = *dst;
*dst-- = tmp;
}
}
}
return str;
}
static char *strip(char *s)
{
// strip a string of a new line
return strtok(s, "\n");
}
static bool checkEpi(char *reversed, char *filePath)
{
// check if the word matches in a given file
// or if the word is an emordnilap
FILE *wordList;
char *line = NULL;
size_t len = 0;
ssize_t read;
wordList = fopen(filePath, "r");
if (wordList == NULL)
{
perror("Failed to open file: "); // file probably doesn't exit
}
while ((read = getline(&line, &len, wordList)) != -1) // read the file line by line
{
if (strip(line) == reversed)
{
return true; // return true if the word matches
}
}
fclose(wordList);
}
static bool checkPali(char *origin, char *reversed)
{
// check if a given word is a palindrome or not
if (*origin == *reversed)
return true;
}
static void checkAll(char *origin, char* reverse, char *filePath)
{
// basically a main function to check if it's a palindrome or a emordnilap
bool paliRes = checkPali(origin, reverse);
bool epiRes = checkEpi(reverse, filePath);
if (paliRes == true)
{
printf("\n%s is a palindrome, it is the same forward and backwards\n", origin);
}
else
{
printf("\n%s is not a palindrome, it is not the same forward and backwards\n", origin);
}
if (epiRes == true)
{
printf("Reverse of %s is a emordinlap, it spells a word backwards.\n\n", origin);
}
else
{
printf("Reverse of %s is not a emordinlap, it does not spell a word backwards\n\n", origin);
}
}
int main(int argc, char *argv[])
{
if (argv[1] == NULL)
{
puts("\nYou failed to pass a valid flag...\n");
helpPage();
return 1;
}
else
{
char *testStrings[] = {"a", "ab", "abc", "another", "cbc", "|0|", "palindrome"};
int i;
char s[10000];
char *defaultWordList = "/usr/share/dict/american-english";
size_t optInt;
for (optInt = 1; optInt < argc && argv[optInt][0] == '-'; optInt++)
{
switch(argv[optInt][1])
{
case 't':
{
for (i = 0; i < sizeof(testStrings) / sizeof(testStrings[0]); i++)
{
strcpy(s, testStrings[i]);
char *origin = testStrings[i];
char *revStr = reverse(s);
checkAll(origin, revStr, defaultWordList);
}
return 0;
}
case 's':
{
if (argv[2] == NULL)
{
puts("\nYou must provide a string to test.\n");
helpPage();
return 1;
}
else if (argv[3] == NULL)
{
puts("\nYou must pass a valid file path to use as a wordlist.\n");
helpPage();
return 1;
}
else
{
//strcpy(s, argv[2]);
char *origin = argv[2];
char *revStr = reverse(argv[2]);
checkAll(origin, revStr, argv[3]);
return 0;
}
}
case 'h': helpPage(); return 0;
}
}
return 0;
}
}
What am I doing wrong to where my statements are not comparing correctly?
You can't meaningfully compare strings using == in C.
if (*origin == *reversed)
This just compares the first char of each of the two parameters. Try strcmp instead.
static bool checkPali(char *origin, char *reversed)
{
/* match if result of strcmp is 0 */
return strcmp(origin, reversed) == 0;
}
You will need a similar change for
if (strip(line) == reversed)

Why doen't the name of the file increment like the data of the file's array does?

So the title I know is a little misleading with the contents, but my question is: with the code provided here, when I run ./tstats input1.txt input2.txt the output is as follows
5 1 21 input1.txt
2 1 11 input1.txt
So it's obvious that the information from each file is going through but the names of the files aren't. Any idea as to why this is happening? Some small mistake I'm looking over?
Secondly, how would I tally all of the wcount results so that I have:
7 2 32 total
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
enum state
{
START,
WORD,
DELIM,
};
FILE*
input_from_args(int argc, char *argv[])
{
if (argc == 1)
{
return stdin;
}
else
{
return fopen(argv[1], "r");
}
}
char* get_filename_from_args(int argc, char* argv[])
{
int i;
static char stdin_name[] = "-";
if (argc == 1)
{
return stdin_name;
}
else
{
for (i = 1; i < argc; i++)
{
return argv[i];
}
return argv[i];
}
}
void
wcount(FILE *src, FILE *dest, char* src_filename)
{
int ch, wc, lc, cc;
enum state cstate;
wc = lc = cc = 0;
cstate = START;
while ((ch = fgetc(src)) != EOF){
cc++;
switch (cstate) {
case START:
if (isspace(ch)) {
cstate = DELIM;
if (ch == '\n') {
lc++;
}
}
else {
cstate = WORD;
wc++;
}
break;
case DELIM:
if (ch == '\n') {
lc++;
}
else if (!isspace(ch)) {
cstate = WORD;
wc++;
}
break;
case WORD:
if (isspace(ch)) {
cstate = DELIM;
if (ch == '\n') {
lc++;
}
}
break;
}
}
fprintf(dest, "%4d\t%4d\t%4d\t%10s\n", wc, lc, cc, src_filename);
}
int
main(int argc, char* argv[])
{
int i;
FILE *src = input_from_args(argc, argv);
FILE *dest = stdout;
for (i = 1; i < argc; i++)
{
if ((src = fopen(argv[i], "r")) == NULL)
{
fprintf(stderr, "%s: unable to open %s\n", argv[0], argv[i]);
}
wcount(src, dest, get_filename_from_args(argc, argv));
fclose(src);
}
return EXIT_SUCCESS;
}
get_filename_from_args doesn't do what you think it does. Get rid of it entirely and just use argv[i] again at the call site.
regarding this sub question:
Secondly, how would I tally all of the wcount results so that I have:
7 2 32 total
add three global int variables
Then when each value is available, add it to the appropriate global variables.
Then, in the end, printf the contents of those global variables
This line:
'wcount(src, dest, get_filename_from_args(argc, argv));'
is not stepping through the arguments contained in argv.
Suggest:
'wcount(src, dest, argv[i]);'
and eliminating the function:
'char* get_filename_from_args(int argc, char* argv[])'

Resources