Circuit Simulator in C input - c

I have to create a circuit based off the inputs of a text file i read.
The first text file I use has the circuit description and then the next has the actual binary values of the circuit you end up creating.
Example:
INPUTVAR 3 A B C
OUTPUTVAR 1 Q
AND A B w
AND A C x
OR w x Q
the format of this example means that there are 3 input variables called A, B , C. There is also 1 output variable called Q. The And creates an expression called w= A.B. along with x=A.C. and the or is Q = w.x. My problem is that I do not know how to read in this input since it isnt the same format every time. The amount of variables after the INPUTVAR depends on what that first number says. I am confused on how I can interpret this in code, I know how to read in data that is formatted. Any hints or help provided will be appreciated.
I believe I need to use fgets() to do it line by line.
with help I have came up with the following code
FILE *circuit;
circuit = fopen(argv[1],"r");
char line[50];
char *str;
while (fgets(line,sizeof(line),circuit) != NULL)
{
str = strtok(line," "); //space is DELIM
printf("str is: %s\n",str);
}
I am geting the following outputs from the text file that I included as an example:
str is: INPUTVAR
str is: OUTPUTVAR
str is: AND
str is: AND
str is: OR
How do I go about getting the characters after the first word of each line?

I adjusted your code [please pardon any gratuitous style cleanup]:
FILE *circuit;
circuit = fopen(argv[1], "r");
char line[50];
char *str;
char *cp;
while (1) {
cp = fgets(line, sizeof(line), circuit);
if (cp == NULL)
break;
cp = strchr(line,'\n');
if (cp != NULL)
*cp = 0;
cp = line;
while (1) {
str = strtok(cp, " ");
cp = NULL;
if (str == NULL)
break;
printf("str is: %s\n", str);
}
}
UPDATE: Per your request, here is a more generalized solution that gets you closer. This compiles but is untested. But, it should give you the idea.
// circuit -- circuit simulator
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// command parsing control
// NOTE: this can be build up if desired
struct cmd {
const char *cmd_str; // command string
const char *(*cmd_parse)(struct cmd *); // command parse function
};
// forward declarations
const char *parse_inputvar(struct cmd *cmd);
const char *parse_outputvar(struct cmd *cmd);
const char *parse_and(struct cmd *cmd);
const char *parse_or(struct cmd *cmd);
// list of supported commands
struct cmd cmdlist[] = {
{ .cmd_str = "INPUTVAR", .cmd_parse = parse_inputvar },
{ .cmd_str = "OUTPUTVAR", .cmd_parse = parse_outputvar },
{ .cmd_str = "AND", .cmd_parse = parse_and },
{ .cmd_str = "OR", .cmd_parse = parse_or },
{ .cmd_str = NULL }
};
int
main(int argc,char **argv)
{
FILE *circuit;
char line[5000];
char *cp;
struct cmd *cmd;
const char *err;
--argc;
++argv;
circuit = fopen(*argv, "r");
while (1) {
cp = fgets(line, sizeof(line), circuit);
if (cp == NULL)
break;
cp = strchr(line,'\n');
if (cp != NULL)
*cp = 0;
cp = strtok(line," ");
err = NULL;
for (cmd = cmdlist; cmd->cmd_str != NULL; ++cmd) {
if (strcmp(cp,cmd->cmd_str) == 0) {
err = cmd->cmd_parse(cmd);
break;
}
}
if (cmd->cmd_str == NULL)
printf("unknown command -- '%s'\n",cp);
if (err != NULL)
printf("error in %s command -- %s\n",cmd->cmd_str,err);
}
fclose(circuit);
}
const char *
parse_inputvar(struct cmd *cmd)
{
char *cp;
char *sym;
int cnt;
int idx;
const char *err = NULL;
do {
// get the count string
cp = strtok(NULL," ");
if (cp == NULL) {
err = "missing count";
break;
}
// decode count string into number
cnt = atoi(cp);
if (cnt <= 0) {
err = "bad count";
break;
}
for (idx = 0; idx < cnt; ++idx) {
sym = strtok(NULL," ");
if (sym == NULL) {
err = "missing symbol";
break;
}
// NOTE: sym will be invalid after we return so we need to preserve
// it now
sym = strdup(sym);
// do whatever ...
}
} while (0);
return err;
}
const char *
parse_outputvar(struct cmd *cmd)
{
const char *err = NULL;
return err;
}
const char *
parse_and(struct cmd *cmd)
{
const char *err = NULL;
return err;
}
const char *
parse_or(struct cmd *cmd)
{
const char *err = NULL;
return err;
}

You can read the line of input, using fgets(), as a string and them do a split() function on it to parse it into numbers. The split creates an array of strings using spaces as delimiters. The first string will be INPUTVAR in string format, then you can do an atoi() on that to convert to a number.

Related

Split string into two variables in C

I have been givin an assignment which uses C to read a given file and input data into a binary tree. My current problem is splitting the line read from the file into two different variables.
The file that has been given contains two bits of data, an ID and some information. 2409, blah, blah, blah
Currently, the program is reading the file correctly and storing each line and then displaying it. I have tried to use token's, memmove and trying to simply select the characters manually however this needs to be dynamic. The ID is not a fixed amount of numbers so manually selecting it will not work. As mentioned, I have tried to use strtok using ", " as a delimited however it just doesn't change anything.
This is currently what I am using to display the information, I intent to split the string within the while loop for each line:
int main() {
struct node* root = NULL;
FILE *file;
char filename[15];
char buff[255];
char line[128];
strcpy(filename, "file.txt");
file = fopen(filename, "r");
if (file == NULL) {
printf("File could not be openned.\n");
exit(0);
}
while (line != NULL)
{
strcpy(line, fgets(buff, 255, file));
printf("%s", line);
}
fclose(file);
}
Is there any way that I am able to simply select the first characters up to the first occurance of "," and convert them into an integer. Then select the rest of the data removing the first "ID, " and insert that into a char variable.
Your help is greatly appreciated.
Like #LPs suggested, and assuming each line is like "2019, blah, blah, blah", you can get the ID for each line by calling:
int id = atoi(strtok(line, ","));
If one wants to parse files like,
2409, blah, blah, blah
0x10,foo, bar, baz, qux
# This is more difficult.
010 , a\
a, b b\#\\\,still b,c
one is probably better off just using a parser generator like lex and yacc or my favourite, re2c.
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <assert.h>
/* Tokens. */
#define PARAM(A) A
#define STRINGISE(A) #A
#define TOKENS(X) X(ERROR), X(END), X(COMMA), X(NEWLINE), \
X(ESCAPE), X(WSP), X(NUMBER), X(WORD)
enum Token { TOKENS(PARAM) };
static const char *const tokens[] = { TOKENS(STRINGISE) };
struct Lexer { size_t line; char *marker, *from, *cursor; };
static enum Token lex(struct Lexer *lexer) {
assert(lexer);
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = char;
re2c:define:YYCURSOR = lexer->cursor;
re2c:define:YYMARKER = lexer->marker; // Rules overlap.
newline = "\n" | ("\r" "\n"?);
oct = "0" [0-7]*;
dec = [1-9][0-9]*;
hex = '0x' [0-9a-fA-F]+;
num = oct | dec | hex;
word = [^\x00\\\n\r \t\v\f,0-9]+;
comment = "#" [^\x00\n\r]* newline;
*/
scan:
lexer->from = lexer->cursor;
/*!re2c
* { return ERROR; }
"\x00" { return END; }
[ \t\v\f]+ { return WSP; }
newline { lexer->line++; return NEWLINE; }
"\\\n" | comment { lexer->line++; goto scan; }
"\\\\" | "\\," | "\\ " | "\\n" | "\\#" { return ESCAPE; }
"," { return COMMA; }
word { return WORD; }
num { return NUMBER; }
*/
}
struct Buffer {
char *data;
size_t size, capacity;
};
static char *buffer_reserve(struct Buffer *const buf, const size_t reserve) {
const size_t min = buf->size + reserve;
size_t c = buf->capacity;
char *data;
assert(buf);
if(reserve > (size_t)-1 - buf->size || min > ((size_t)-1 >> 1) + 1)
{ errno = ERANGE; return 0; }
if(min > c) {
if(!c) c = 1;
while(min <= c) c <<= 1;
if(!(data = realloc(buf->data, c))) return 0;
buf->data = data;
buf->capacity = c;
}
return buf->data + buf->size;
}
struct Word { char *start, *end; };
struct Parser {
int id, id_set, first_comma;
size_t num_words;
struct Word words[64]; /* Lazy. */
char *start_words, *end_words;
};
static size_t parser_max_words = sizeof ((struct Parser *)0)->words
/ sizeof *((struct Parser *)0)->words;
static void clear_parser(struct Parser *const parser) {
assert(parser);
parser->id_set = 0;
parser->first_comma = 1;
parser->num_words = 0;
parser->start_words = parser->end_words = 0;
}
static void print_parser(const struct Parser *const parser) {
const struct Word *word = parser->words,
*word_end = parser->words + parser->num_words;
assert(parser && parser->id_set && parser->num_words <= parser_max_words);
printf("#%d: ", parser->id);
for( ; word < word_end; word++) {
if(word != parser->words) printf(", ");
if(!word->start) { printf("<null>"); continue; }
assert(word->start <= word->end);
if(word->start == word->end) { printf("<empty>"); continue; }
printf("<%.*s>", (int)(word->end - word->start), word->start);
}
fputc('\n', stdout);
}
static void expand_word(struct Parser *const parser,
const struct Lexer *const lexer) {
assert(parser && lexer && lexer->from < lexer->cursor);
if(!parser->start_words) {
assert(!parser->end_words);
parser->start_words = lexer->from;
}
parser->end_words = (lexer->from + INT_MAX >= lexer->cursor) ?
lexer->cursor : lexer->from + INT_MAX;
}
static int store_word(struct Parser *const parser) {
struct Word *word;
assert(parser);
if(parser->num_words >= parser_max_words) return errno = EILSEQ, 0;
word = parser->words + parser->num_words++;
word->start = parser->start_words;
word->end = parser->end_words;
parser->start_words = parser->end_words = 0;
return 1;
}
int main(int argc, char **argv) {
const size_t granularity = 1024;
struct Lexer lexer = { 1, 0, 0, 0 };
struct Parser parser;
size_t nread;
struct Buffer buf = { 0, 0, 0 };
char *b;
FILE *fp = 0;
int success = 0, end_of_buffer = 0;
/* Open. */
if(argc != 2) return fprintf(stderr, "Needs filename.\n"), EXIT_FAILURE;
if(!(fp = fopen(argv[1], "r"))) goto catch;
/* Read. */
do {
if(!(b = buffer_reserve(&buf, granularity))) goto catch;
nread = fread(b, 1, granularity, fp);
buf.size += nread;
} while(nread == granularity);
if(ferror(fp)) goto catch;
fclose(fp), fp = 0;
if(!(b = buffer_reserve(&buf, 1))) goto catch;
*b = '\0'; /* Make sure it's a string. */
/* Parse. */
lexer.cursor = buf.data;
clear_parser(&parser);
do {
enum Token tok;
switch((tok = lex(&lexer))) {
case ERROR: goto catch;
case END: end_of_buffer = 1; break;
case COMMA:
if(!parser.id_set) { errno = EILSEQ; goto catch; }
if(parser.first_comma) { parser.first_comma = 0; break; }
if(!store_word(&parser)) goto catch;
break;
case NEWLINE:
if(parser.id_set) {
/* We require at least key, data. */
if(!store_word(&parser)) goto catch;
print_parser(&parser);
clear_parser(&parser);
} else if(parser.start_words) {
errno = EILSEQ; goto catch;
}
break;
case ESCAPE:
if(!parser.id_set) { errno = EILSEQ; goto catch; }
expand_word(&parser, &lexer);
break;
case WSP: break;
case NUMBER:
if(parser.id_set) {
expand_word(&parser, &lexer);
} else {
char *end;
long i = strtol(lexer.from, &end, 0);
if(end != lexer.cursor || i < INT_MIN || i > INT_MAX)
{ errno = EDOM; goto catch; }
parser.id = (int)i;
parser.id_set = 1;
}
break;
case WORD:
expand_word(&parser, &lexer);
break;
}
} while(!end_of_buffer);
success = EXIT_SUCCESS;
goto finally;
catch:
fprintf(stderr, "While on line %lu.\n", (unsigned long)lexer.line);
perror("parsing");
assert(!lexer.from || (lexer.from < lexer.cursor
&& lexer.from + INT_MAX >= lexer.cursor));
if(lexer.from) fprintf(stderr, "While on %.*s.\n",
(int)(lexer.cursor - lexer.from), lexer.from);
finally:
free(buf.data);
if(fp) fclose(fp);
return success;
}
Prints,
#2409: <blah>, <blah>, <blah>
#16: <foo>, <bar>, <baz>, <qux>
#8: <a\
a>, <b b\#\\\,still b>, <c>
but that's probably overkill.
As #HAL9000 mentioned, I was able to complete this by using sscanf. Simply extracting the integer and string from the line using sscanf(line, "%d %[^\n]s", &ID, details);
I did try using strtok however, couldn't get my head around it as it wasn't working. sscanf was the easiest to do so this is what I am going to use, thanks.
Using sscanf
e.g
int main(int argc, char *argv[]) {
const char *str = "123, this, is, a test ;##";
char buff[128] = {0};
int num = 0;
if (2 == sscanf(str, "%d,%[^\r\n]s", &num, buff))
printf("== num: %d, string: '%s'\n", num, buff);
else
printf("== Wrong!\n");
return 0;
}
result: == num: 123, string: ' this, is, a test ;##'

Program doesn't segfault only when stepping through debugger (gdb)

This program I wrote to automatically turn my network's auto configuration logic only segfaults if I run through the program without breaking. When running through a debugger with breakpoints, it runs perfectly fine. When run through a debugger without breakpoints, it segfaults. Is there a bug in this program that I'm not noticing?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define COMLEN 64
#define BUFSIZE 128
const char *program_name;
typedef enum errors
{
SUCCESS = 0,
MEM_ERROR,
FILE_ERROR,
USAGE_ERROR,
} Error;
void terminal_error(Error e)
{
switch (e)
{
case MEM_ERROR:
fputs("Memory error!\n", stderr);
break;
case FILE_ERROR:
fputs("File error!\n", stderr);
break;
case USAGE_ERROR:
fprintf(stderr, "Incorrect usage. Try %s [y|n]\n", program_name);
break;
default:
fputs("Unknown error!\n", stderr);
break;
}
exit(e);
}
Error read_file(FILE *fp, char **buffer)
{
long len;
fseek(fp, 0, SEEK_END);
len = ftell(fp);
fseek(fp, 0, SEEK_SET);
if ((*buffer = malloc(len+1)) == NULL)
return MEM_ERROR;
if (fread(*buffer, 1, len, fp) != len)
return FILE_ERROR;
buffer[len+1] = '\0';
return SUCCESS;
}
Error get_interface(char *name)
{
const char *cmd = "netsh wlan show networks";
FILE *fp;
// open pipe to parse output from command and run command
if ((fp = popen(cmd, "rb")) == NULL)
return FILE_ERROR;
// read the piped output into buffer
char *buffer = NULL;
Error res;
res = read_file(fp, &buffer);
if (res != SUCCESS)
return res;
fclose(fp);
// parse the buffer
// needle delimits the start of the interface name in the command
// end_needle delimits the end of the interface name
const char *needle = "Interface name : ";
const char *end_needle = " \r\nThere";
// end will point to end_needle (end of interface name)
char *end = NULL;
// name points to one-past the end of the needle (start of interface
// name)
name = strstr(buffer, needle);
name += strlen(needle);
end = strstr(buffer, end_needle);
*end = '\0'; // terminates the interface name
return SUCCESS;
}
int main(int argc, const char *argv[])
{
const char *template = "netsh wlan set autoconfig enabled=%s interface=\"%s\"";
char interface_name[BUFSIZE] = {0};
program_name = argv[0];
Error res = SUCCESS;
// get name of wireless interfacew
res = get_interface(interface_name);
if (res != SUCCESS)
terminal_error(res);
char *op;
if (argc > 1)
// arguments were provided
{
if (strcmp(argv[1], "y") == 0)
op = "yes";
else if (strcmp(argv[1], "n") == 0)
op = "no";
else
terminal_error(USAGE_ERROR);
char command[COMLEN];
sprintf(command, template, op, interface_name);
system(command);
}
else
// no arguments were provided
{
system("netsh wlan show settings");
}
return 0;
}

Problems of History for my own Shell in C

As a project, I have to make my own shell. I did it but I have some problems with the history feature.
Here's piece of my code:
int main(int argc, char* argv[])
{
char saisie[300], cwd[1024];
char* nom = getenv("USER");
char* backup[MAXCMD];
int boucle = 1, n = 0, i, u = 0, b = 0;
for(i = 0; i < MAXCMD; i++)
{
backup[i] = NULL;
}
for(i = 0; i < MAX_INPUT_SZ; i++)
{
saisie[i] = 0;
}
char* cmd[MAXPARAMS]; //MAXPARAMS is 20
while( boucle == 1)
{
printf("%s#sam:~ %s> ", nom, (getcwd(cwd, sizeof(cwd))));
fgets(saisie,MAX_INPUT_SZ,stdin);
printf("\n");
split_input(saisie, cmd);
free(backup[u]);
backup[u] = strdup(saisie);
u = (u + 1) % MAXCMD;
b = switchcmd(cmd,backup,b,u);
start(cmd,b);
b = 0; //débloquage fonction start
}
return 0;
}
I print the history with this fonction:
int historique(char* backup[], int u)
{
int i = u;
int place = 1;
do
{
if (backup[i])
{
printf("%4d: %s\n", place, backup[i]);
place++;
}
i = (i + 1) % MAXCMD;
} while (i != u);
return 0;
}
B is used to block the execution fonction (start) when user enter "cd" or "history", because it will generate an error.
Here's the fonction triggered when user enters "cd", "history", or "exit":
int switchcmd(char** cmd,char** backup, int b,int u)
{
int i, n = 3, switch_value = 0;
char* error;
char* listcmd[n];
listcmd[0] = "cd";
listcmd[1] = "exit";
listcmd[2] = "history";
for (i = 0; i < n; ++i)
{
if(strcmp(cmd[0], listcmd[i]) == 0)
{
switch_value = i + 1;
break;
}
}
switch (switch_value)
{
case 1:
chdir(cmd[1]);
b = 1;
error = strerror(errno);
if (*error != 0)
{
printf("sam: %s: %s\n", cmd[0], error);
}
break;
case 2:
printf("Bye bye\n");
exit(0);
case 3:
historique((char**)backup,u);
b = 1;
break;
}
return b;
}
When I execute my shell, and enter these commands successively, they work. °i1
> clear
> ls -a -l
> ls -a
> cd ..
> man chdir
Then "history" for printing the history, I have this : °i2
1: clear
2: ls
3: ls
4: cd
5: man
6: history
and I want this output, with all parameters: °i3
1: clear
2: ls -a -l
3: ls -a
4: cd ..
5: man chdir
6: history`
I dont know why, and I don't understand why strdup does not duplicate my cmd in backup at it should.
Any help please?
When the user command is store in ' saisie ', this command is duplicate and split in array of parameters. And I use ' cmd ' in the execution fonction, with execvp.
Then there is your big problem, cmd has a fixed length of 1, if you use that to stored the command arguments for execvp, then you can only store one thing: NULL.
You have two options:
Use a large fixed size, for example char *cmd[100] where you can store up
to 99 arguments and no more. This is the easiest solution but it is not flexible
enough. Although some systems have a limit on the number of arguemnts you can
pass to a new process, I don't know if there is a limit for all systems,
this and this might help you there.
Dynamically create an array of char pointers depending on the command line.
This is more work but this is also the more flexible solution. Assuming that
your command line does not have support for pipes (|) and redirections (<,
<<, >, >>), then split_input could look like this:
char **split_input(const char *cmd)
{
if(cmd == NULL)
return NULL;
char **argv = NULL, **tmp;
char *line = strdup(cmd);
if(line == NULL)
return NULL;
const char *delim = " \t\n";
char *token = strtok(line, delim);
if(token == NULL)
{
free(line);
return NULL;
}
size_t len = 0;
do {
char *arg = strdup(token);
if(arg == NULL)
{
free_argv(argv);
free(line);
return NULL;
}
tmp = realloc(argv, (len + 2) * sizeof *argv);
if(tmp == NULL)
{
free_argv(argv);
free(line);
return NULL;
}
argv = tmp;
argv[len++] = arg;
argv[len] = NULL; // argv must be NULL terminated
} while(token = strtok(NULL, delim));
free(line);
return argv;
}
void free_argv(char **argv)
{
if(argv == NULL)
return;
for(size_t i = 0; argv[i]; ++i)
free(argv[i]);
free(argv);
}
Now you can use it like this:
while( boucle == 1)
{
printf("%s#sam:~ %s> ", nom, (getcwd(cwd, sizeof(cwd))));
fgets(saisie,MAX_INPUT_SZ,stdin);
printf("\n");
char **argv = split_input(saisie);
if(argv == NULL)
{
fprintf(stderr, "Cannot split command line, not enough memory\n");
continue;
}
free(backup[u]);
backup[u] = strdup(argv[0]); // <-- passing argv[0], not argv
// but perhaps what you really want
// is strdup(saisie)
u = (u + 1) % MAXCMD;
b = switchcmd(argv,backup,b,u);
start(argv,b);
b = 0; //débloquage fonction start
free_argv(argv);
}
You are also doing
backup[u] = strdup(cmd);
but the problem is that cmd is an array of char pointers, strdup expects a
const char*, you are passing the wrong type. It should be strdup(cmd[0]) or
strdup(saisie) if you want to store the whole command.

How to split a file in rows or columns in c

I am trying to split a line from a file in different parts separated by a single space, but it doesn`t work... So my question is: How can i split my line in different parts and do that for every single line of the file and then put those parts in dynamically allocated vectors/matrix? Or by columns as well. Tell me as you see fit.
The file looks like this:
BERLIN CAR 1 U
BERLIN CAR 1 R
BUCHAREST JACKET 2 D
NEW_YORK DOLL 7 U
BERLIN ROBOT 5 L
BUCHAREST BALL 4 L
I want to do this.
Example:
Locations[i]={"BERLIN","BERLIN","BUCHAREST","NEW_YORK"."BERLIN","BUCHAREST"}
TOYS[j]={"CAR","CAR","JACKET","DOLL","ROBOT","BALL"}
NUMBER[k]={1,1,2,7,5,4}
LETTER[l]={'U','R','D','U','L','L'}
My code so far (MAX_STRING_LENGTH is defined to 30 ) :
int i;
char *p,**a,delim[]=" ";
a=malloc(100 * sizeof(char));
for(i = 0; i < 100; i++)
{
a[i]=calloc(MAX_STRING_LENGTH,sizeof(char));
}
while(!feof(stdin))
{
fgets(*a,500,stdin);
p=strtok(*a,delim);
}
strtok is the correct function, however you are using it wrong.
man strtok
The strtok() function breaks a string into a sequence of zero or more nonempty tokens.
On the first call to strtok(), the string to be parsed should be specified
in str. In each subsequent call that should parse the same string, str
must be NULL.
I made the most important part of the quote bold.
Also bear in mind that strtok modifies the source, if you need the source
afterwards, you have to make a copy.
// assuming that line is either a char[] or char*
char *token = strtok(line, " ");
if(token == NULL)
{
// error detection
}
while(token = strtok(NULL, " "))
{
// do the work
}
Also I recommend not to use sizeof(<data type>) in
malloc/calloc/realloc calls. It's easy to overlook a * and make
mistakes. Better:
int *myarray = malloc(size * sizeof *myarray);
// or
int *mayarray = calloc(size, sizeof *myarray);
Using sizeof *var is better because it will always returns the correct size.
One last thing:
while(!feof(stdin))
See Why is “while ( !feof (file) )” always wrong?
better
char buffer[1024];
while(fgets(buffer, sizeof buffer, stdin))
{
// do the work here
}
EDIT
Here you have a sample implementation using strtok. My implementation uses
an array of MAPs. Look at the way I construct/destruct the the MAP objects and how the memory is allocated. Obviously this can be done with less code and less strdups, but I think this shows more precisely how to use these functions. You can use this code as your base or just use it as a basic idea.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct{
char *destination;
char *type_present;
int no_available_presents;
char *direction;
} MAP;
MAP *create_map(const char *dest, const char *type, int present, const char *dir);
void free_map(MAP *map);
void print_map(MAP *map);
MAP *create_map(const char *dest, const char *type, int present, const char *dir)
{
MAP *map = calloc(1, sizeof *map);
if(map == NULL)
return NULL;
int errors = 0;
if(!(map->destination = strdup(dest)))
errors++;
if(!(map->type_present = strdup(type)))
errors++;
if(!(map->direction = strdup(dir)))
errors++;
map->no_available_presents = present;
if(!errors)
return map;
free_map(map);
return NULL;
}
void free_map(MAP *map)
{
if(map == NULL)
return;
free(map->destination);
free(map->type_present);
free(map->direction);
free(map);
}
void print_map(MAP *map)
{
if(map == NULL)
{
puts("(null)");
return;
}
printf("destination: %s\n", map->destination);
printf("type: %s\n", map->type_present);
printf("present: %d\n", map->no_available_presents);
printf("direction: %s\n", map->direction);
}
int main(char argc, char **argv)
{
FILE *fp;
if(argc != 1 && argc != 2)
{
fprintf(stderr, "usage: %s [database]\n", argv[0]);
return 1;
}
if(argc == 1)
fp = stdin;
else
fp = fopen(argv[1], "r");
if(fp == NULL)
{
fprintf(stderr, "Could not open '%s': %s\n", argv[1], strerror(errno));
return 1;
}
MAP **maps = NULL;
size_t map_len = 0;
char line[1024];
const char *delim = " \r\n";
while(fgets(line, sizeof line, fp))
{
int pres;
char *dest = NULL, *type = NULL, *dir = NULL, *token;
token = strtok(line, delim);
dest = strdup(token);
token = strtok(NULL, delim);
type = strdup(token);
token = strtok(NULL, delim);
pres = atoi(token);
token = strtok(NULL, delim);
dir = strdup(token);
if(dest == NULL || type == NULL || dir == NULL)
{
// ignore line
free(dest);free(type);free(dir);
continue;
}
MAP *new_map = create_map(dest, type, pres, dir);
if(new_map == NULL)
{
// ignore line
free(dest);free(type);free(dir);
continue;
}
MAP **tmp_map = realloc(maps, (map_len + 1) * sizeof *tmp_map);
if(tmp_map == NULL)
{
// ignore line
free_map(new_map);
free(dest);free(type);free(dir);
continue;
}
maps = tmp_map;
maps[map_len++] = new_map;
free(dest);free(type);free(dir);
}
for(int i = 0; i < map_len; ++i)
{
print_map(maps[i]);
puts("---");
free_map(maps[i]);
}
free(maps);
if(fp != stdin)
fclose(fp);
return 0;
}
The output:
destination: BERLIN
type: CAR
present: 1
direction: U
---
destination: BERLIN
type: CAR
present: 1
direction: R
---
destination: BUCHAREST
type: JACKET
present: 2
direction: D
---
destination: NEW_YORK
type: DOLL
present: 7
direction: U
---
destination: BERLIN
type: ROBOT
present: 5
direction: L
---
destination: BUCHAREST
type: BALL
present: 4
direction: L
---

Segmentation faulting in assignment

I'm currently learning C and I came from java. Our assignment asked us to count strings from either a file that could be added or it asks for the user to input a string. We just started using pointers and i looked up the different reasons why segmentation faults happened but I have no idea how to check for which issue it is. I initialized all my pointers to NULL but it still didn't work and from what i read that was the most common reason why a segmentation fault happens.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int debug = 0;
int
main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
FILE* infile = NULL;
int c, err = 0;
int lflag = 0, sflag = 0, count = 0; //flags and count
char *shortWord = NULL, *longWord = NULL; //variable for shortest and longest word
int shortest = 100, longest = 0; //longest char 100, shortest 0
char *string = NULL;
char *pch = NULL;
static char usage[] = "usage: %s [-l] [-s] [filename]\n";
while ((c = getopt(argc, argv, "ls")) != -1)
switch (c)
{
case 'l':
lflag = 1;
break;
case 's':
sflag = 1;
break;
case '?':
err = 1;
break;
}
if (err)
{
fprintf(stderr, usage, argv[0]);
exit(1);
}
//checks for file and then runs loop for word count
infile = fopen("myfile.txt","r");
if (infile != NULL)
{
fgets(string, 100, infile);
pch = strtok (string, " ,.-");
while(pch != NULL)
{
count++;
if (strlen(pch) > longest)
longWord = pch;
if (strlen(pch) < shortest)
shortWord = pch;
pch = strtok (NULL, " ,.");
}
}
//else, asks for string
else
{
printf("Enter your string: \n");
fgets(string, 100, stdin);
int len = strlen(string);
count = len;
pch = strtok ( string, " ,.-");
while(pch != NULL)
{
count++;
if (strlen(pch) > longest)
longWord = pch;
if (strlen(pch) < shortest)
shortWord = pch;
pch = strtok (NULL, " ,.");
}
}
//following lines compute value based on arguments
if(lflag == 1)
{
printf("Longest word is %s", longWord);
}
if(sflag == 1)
{
printf("Shortest word is %s", shortWord);
}
printf("Word count = %.2d\n", count);
exit(0);
}
Their are some issues in your code:
You initialized string to NULL, then used it as an input buffer for fgets(). fgets() reqiures a pointer to an array of chars, either declared on the stack or dynamically allocated with malloc(3). You can set an input buffer such as char string[100].
fgets() must be checked, as it returns NULL when unable to read a line.
Your delimiter for strtok() is not accounting for the \n character appended by fgets(). You can either remove this newline, or include it in the delimter. If you want to include it in the delimeter, make sure your delimiter is " ,.-\n".
You could create function which parses your input with strtok(), as this would allow your main() to be shorter and reduce the repetitiveness in the code. An example function prototype could be void longest_shortest_words(char line[], char **longest, char **shortest, size_t *word_count);, whereby you pass the longest, shortest words along with the number of words back to main() via pointers. You could also just store the longest and shortest words in a 2D array or array of pointers.
You should also explicitly check that your file was opened correctly. Something like this should be included:
infile = fopen("myfile.txt", "r");
if (infile == NULL) {
fprintf(stderr, "Failed to open file\n");
exit(EXIT_FAILURE);
}
When checking opt, checking ? as a character in your switch statement is not right. Instead of:
case '?':
err = 1;
break;
Use default, which covers any other invalid option entered. Here is how you can use it:
default:
fprintf(stderr, "usage: %s [-l] [-s] [filename]\n", argv[0]);
exit(EXIT_FAILURE);
Checking sflag and lflag at the end is not enough. You should check if longWord and shortWord are not NULL.
Here is some example code which demonstrates these points:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define LINESIZE 100
void longest_shortest_words(char line[], char **longest, char **shortest, size_t *wordcount);
void print_output(int lflag, int sflag, char *longword, char *shortword, size_t wordcount);
void remove_newline(char line[]);
int main(int argc, char * const argv[]) {
FILE *infile;
char line[LINESIZE] = {'\0'};
int opt, sflag = 0, lflag = 0;
size_t wordcount = 0;
const char *optstr = "ls";
char *longword = NULL, *shortword = NULL;
while ((opt = getopt(argc, argv, optstr)) != -1) {
switch(opt) {
case 'l':
lflag = 1;
break;
case 's':
sflag = 1;
break;
default:
fprintf(stderr, "usage: %s [-l] [-s] [filename]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
/* Checking if file is in directory */
infile = fopen("myfile.txt", "r");
if (infile == NULL) {
fprintf(stderr, "Failed to open file\n");
exit(EXIT_FAILURE);
}
/* checking if line exists in file */
if (fgets(line, LINESIZE, infile) == NULL) {
fprintf(stderr, "No line found in file.\n");
printf("\nEnter string instead:\n");
if (fgets(line, LINESIZE, stdin) != NULL) {
remove_newline(line);
longest_shortest_words(line, &longword, &shortword, &wordcount);
/* checking that longWord, shortWord and word_count are valid */
if (longword != NULL && shortword != NULL && wordcount > 0) {
print_output(lflag, sflag, longword, shortword, wordcount);
}
}
/* file has line, do stuff with it */
} else {
remove_newline(line);
longest_shortest_words(line, &longword, &shortword, &wordcount);
print_output(lflag, sflag, longword, shortword, wordcount);
}
exit(EXIT_SUCCESS);
}
/* function for printing output, can be improved */
void print_output(int lflag, int sflag, char *longword, char *shortword, size_t wordcount) {
if (lflag) {
printf("Longest word: %s\n", longword);
}
if (sflag) {
printf("Shortest word: %s\n", shortword);
}
if (wordcount > 0) {
printf("Word count = %zu\n", wordcount);
}
}
/* function for removing newline, and checking that input hasnt exceeded limit */
void remove_newline(char line[]) {
size_t slen;
slen = strlen(line);
if (slen > 0 && line[slen-1] == '\n') {
line[slen-1] = '\0';
} else {
fprintf(stderr, "\nToo many characters in input.\n");
exit(EXIT_FAILURE);
}
}
/* function which parses line, and saves longWord and shortWord in pointers */
void longest_shortest_words(char line[], char **longword, char **shortword, size_t *wordcount) {
char *word = NULL;
const char *delim = " ,.";
word = strtok(line, delim);
if (word != NULL) {
*longword = word;
*shortword = word;
*wordcount = 1;
}
while ((word = strtok(NULL, delim)) != NULL) {
(*wordcount)++;
if (strlen(word) > strlen(*longword)) {
*longword = word;
} else if (strlen(word) < strlen(*shortword)) {
*shortword = word;
}
}
}
Note: The code shown above can be improved, it is just to show you another approach to your problem.

Resources