I'm working on an implementation of the tail Unix command, and this is my code so far:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
char *resize(char *data, int size)
{
char *newData = (char*) malloc((size + 1) * sizeof(char));
int counter;
for(counter = 0; counter < size; counter++)
newData[counter] = data[counter];
free(data);
return newData;
}
int printLines(char *data, int size)
{
int lines = 0, position, counter;
for(position = size - 1; position > -1; position--)
{
if (data[position] == '\n') lines++;
if (lines == 10) break;
}
if (lines == 10)
for(counter = position; counter < size; counter++)
{
write(STDOUT_FILENO, &data[counter], 1);
}
else write(STDOUT_FILENO, data, size);
return 0;
}
int stdIn(char *data, int size)
{
char buff, end = '\n';
int rState = 0;
while ((rState = read(STDIN_FILENO, &buff, 1)) > 0)
{
if(rState < 0)
{
if(errno == EINTR) rState = 0;
else
{
perror("read()");
return 1;
}
}
data = resize(data, size);
data[size - 1] = buff;
size++;
}
if(rState == 0) write(STDOUT_FILENO, &end, 1);
return 0;
}
int tailRead(char *data, char *fileName)
{
int size = 1;
data = (char*)malloc(size * sizeof(char));
if(fileName == 0 || fileName == "-")
{
if(stdIn(data, size) > 0) return 1;
}
else
{
}
printLines(data, size);
return 0;
}
int main(int argc, char *argv[])
{
char *data = 0;
int counter;
if(argc == 1)
{
tailRead(data, 0);
if(data > 0) return 1;
}
else for (counter = 1; counter < argc; counter++)
{
tailRead(data, argv[counter]);
if(data > 0) return 1;
}
return 0;
}
The problem is that somewhere in the resize() function i get a Segmentation Fault, and when i ran the program in GDB, i got Program received signal SIGSEGV Segmentation fault. 0x00000000004006f7 in resize (). This tells me that I have some kind of memory allocation problem in resize(), but so far I have been unable to find the bug. What should I do?
int tailRead(char *data, char *fileName)
/* ... */
int main(int argc, char *argv[])
{
char *data = 0;
/* ... */
tailRead(data, 0);
}
You seem to expect, that in main() data will point to the memory allocated in tailRead(). That is not the case. In tailRead() data is a copy of the pointer data from main() you only change the copy, not the original pointer. The original pointer still points to 0.
Then you call resize() with a null pointer, which of course will lead to a segmentation violation.
Solution
use a pointer to a pointer instead, to modify the original pointer.
int tailRead(char **data, char *fileName)
{
int size = 1;
*data = (char*)malloc(size * sizeof(char));
/* ... */
}
int main(int argc, char *argv[])
{
char *data = 0;
/* ... */
tailRead(&data, 0);
/* ... */
}
You have the same issue with stdIn(). It changes data without reflecting the change to the data-pointer at the call site in tailRead(). You leave stdIn() leaking memory and continue to work with a dangling pointer.
I think your problem is here...
for(counter = 0; counter < size; counter++)
newData[counter] = data[counter];
You are trying to access data[counter] when counter is greater than what you allocated for the previous malloc(). By this I mean, you are reading beyond the (current) legitimate end of data. Make sense?
EDIT: Now that I think about it, this may not cause the segfault, but it is a problem.
Related
I have a working example of copy lines from a file into an array of strings. I want to move the code to copy the lines into a function to which I simply pass a pointer to the array of strings, where the lines will be stored, and a pointer to the file. However, I have tried to move the code into a function and keep getting seg faults. I have tried debugging using GDB and it seems like the problem is with the memory allocation to rows. But I can't work out what the problem is. realloc seems to be working correctly since I find the size of row increases on the 3rd iteration (using malloc_usable_size(*rows)), but then seg faults. I'm compiling with gcc -Wall -Wextra -pedantic -std=c99 -g c_programs/read_file_function.c on Linux.
Working example
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Please supply a file path:\n%s <file path>\n", argv[0]);
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (!fp)
{
perror("ERROR");
return EXIT_FAILURE;
}
char **rows = (char **)malloc(sizeof(char *));
char *lineBuf = NULL;
size_t n = 0;
size_t nLines = 0;
ssize_t lineLength = 0;
size_t i = 0;
while ((lineLength = getline(&lineBuf, &n, fp)) != -1)
{
lineBuf[strcspn(lineBuf, "\n")] = 0;
lineBuf[strcspn(lineBuf, "\r")] = 0;
rows[i] = (char *)malloc(lineLength + 1);
strcpy(rows[i], lineBuf);
i++;
nLines = i;
rows = (char **)realloc(rows, (nLines + 1) * sizeof(char *));
}
printf("nLines: %lu\n", nLines);
printf("row 1: %s\n", rows[0]);
printf("row 2: %s\n", rows[1]);
printf("row 2: %s\n", rows[10]);
return 0;
}
Non working function version
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t readFile(FILE **fp, char ***rows)
{
char *lineBuf = NULL;
size_t n = 0;
size_t nLines = 0;
ssize_t lineLength = 0;
size_t i = 0;
while ((lineLength = getline(&lineBuf, &n, *fp)) != -1)
{
lineBuf[strcspn(lineBuf, "\n")] = 0;
lineBuf[strcspn(lineBuf, "\r")] = 0;
*rows[i] = (char *)malloc(lineLength + 1);
strcpy(*rows[i], lineBuf);
i++;
nLines = i;
*rows = (char **)realloc(*rows, (nLines + 1) * sizeof(char *));
}
return nLines;
}
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Please supply a file path:\n%s <file path>\n", argv[0]);
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (!fp)
{
perror("ERROR");
return EXIT_FAILURE;
}
char **rows = (char **)malloc(sizeof(char *));
size_t nLines = readFile(&fp, &rows);
printf("nLines: %lu", nLines);
printf("row 1: %s", rows[0]);
printf("row 2: %s", rows[1]);
return 0;
}
*rows[i] is doing *(rows[i]) - accessing ith element in the array of rows, and then dereferencing it. You want to do (*rows)[i] - dereference rows and then access ith element.
I advise to:
readFile(..., char ***rows0) {
char **rows = NULL; // temporary internal variable
...
// use rows normally
rows = stuff();
...
// when finished, assign once
*rows0 = rows;
return nLines;
}
But do not be a 3-star programmer. At best, use a structure, -> is easy to use. Like:
struct string {
char *str;
};
struct lines {
struct string *strs;
size_t cnt;
};
// #return 0 on success, otherwise error
int readFile(...., struct lines *p) {
// initialization
p->cnt = 0;
p->strs = NULL;
...
void *pnt = realloc(p->strs, (p->cnt + 1) * ....);
if (!pnt) { /* handle error */ return -1; }
p->strs = pnt;
p->strs[p->cnt]->str = malloc(lineLenght + 1);
if (!p->strs[p->cnt]->str) { /* handle error */ return -2; }
strcpy(p->strs[p->cnt]->str, lineBuf);
p->cnt++;
...
return 0; /* success */
}
int main(int argc, char **argv) {
struct lines p = {0};
if (readFile(..., &p)) {
/* handle error */
}
printf("nLines: %zu\n", p.cnt);
Do not pre-allocate memory. Initialize memory with NULL and call realloc before using memory. realloc(NULL is the same as malloc().
Check for allocation errors.
I currently have functioning code, that when I attempted to make a function out of the conversion of a file to array, I get a segmentation fault. I know that the objects inside of fileToArray are correct (as far as the myData objects are concerned) because when inside of the function, the myData.length, and myData.array all return correctly. However, after the pointer is referenced in the main, I get a seg fault. I'm new to c, but all of this is working without that specific pointer to the struct.
So, if I call this program with an argument with a file with multiple rows of text, the set fault happens.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
typedef struct {
int length;
char** array;
} FileStruct;
void fileToArray(FileStruct* fileDataPtr, int argc, char *argv[]){
int fd, i, n, count;
struct stat statbuf;
char *buf, *inbuf, *str, *saveptr;
char **array;
if ((fd = open(argv[1], O_RDONLY)) == -1) {
printf("Error opening file %s\n", argv[1]);
exit (-1);
}
if (lstat(argv[1], &statbuf) < 0) {
printf("Unable to lstat file %s\n", argv[1]);
exit (-1);
}
off_t filesize = statbuf.st_size;
buf = malloc(sizeof(char)*filesize);
array = malloc(sizeof(char *)*filesize);
count = 0;
if ((n = read(fd, buf, filesize)) > 0){
inbuf = buf;
for (i = 1; ; inbuf = NULL, i++) {
str = strtok_r(inbuf, "\n", &saveptr);
if (str == NULL)
break;
array[count] = malloc(sizeof(char)*(strlen(str)+1));
strcpy(array[count++], str);
}
} else {
printf("Error reading input file\n");
exit (-1);
}
close(fd);
// I know array works because it prints correctly here.
for (i = 0; i < count; i++) {
printf("%s\n", array[i]);
free(array[i]);
}
fileDataPtr->length = count;
fileDataPtr->array = array;
free(array);
free(buf);
}
int main(int argc, char *argv[]) {
int i;
FileStruct myData;
FileStruct* fileDataPtr = &myData;
fileToArray(fileDataPtr, argc, argv);
printf("length: %i", myData.length);
// I know this doesn't work because anything related to myData causes Seg fault.
// for (i = 0; i < 1; i++) {
// printf("%s\n", myData.array[i]);
// free(myData.array[i]);
// }
return 0;
}
Near the end of fileToArray, you assign array to fileDataPtr->array, then on the next line you free array. This will leave fileDataPtr->array pointing to freed memory (a dangling pointer). When you dereference it later, you're into Undefined Behavior and anything can happen.
Since the assignment transfers ownership of the allocated memory to fileDataPtr, you do not need to free array before returning from fileToArray.
Remove the free(array); line.
I wrote the next function that tries to read and enter each line from text file into a string array in c :
int main(int argc,char* argv[])
{
char ** lines;
readFile(argv[1],lines);
}
int readFile(char* filePath,char** lines)
{
char file_char;
int letter_in_line=0;
int line=1;
char* line_string=malloc(1024);
int j=1;
int fd=open(filePath,O_RDONLY);
if (fd < 0)
{
return 0;
}
while (read(fd,&file_char,1) >0)
{
if(file_char != '\n' && file_char != '0x0')
{
line_string[letter_in_line] = file_char;
letter_in_line++;
}
else
{
if(lines != NULL)
{
lines=(char**)realloc(lines,sizeof(char*)*line);
}
else
{
lines=(char**)malloc(sizeof(char*));
}
char* line_s_copy=strdup(line_string);
lines[line-1]=line_s_copy;
line++;
letter_in_line=0;
memset(line_string,0,strlen(line_string));
}
j++;
}
printf("cell 0 : %s",lines[0]);
return 1;
}
I have 2 questions :
1)Whenever the code reaches the print of cell 0, I'm getting
Segmentation fault (core dumped) error. What is wrong ?
2)In case I
want to see the changes in the lines array in my main, I should pass
&lines to the func and get char*** lines as an argument ? In
addition, I will need to replace every 'line' keyword with '*line' ?
*I know that I can use fopen,fget, etc... I decided to implement it in this way for a reason.
There is many issues that make your code core dump.
Here a version very similar to your code. I hope it will help you to understand this.
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
int read_file(const char *filename, char ***result)
{
/* open the file */
const int fd = open(filename, O_RDONLY);
if (fd < 0) {
*result = NULL;
return -1;
}
/* read the file characters by characters */
char *buffer = (char *)malloc(sizeof(char) * 1024);
char c;
int column = 0;
int line = 0;
*result = NULL;
/* for each characters in the file */
while (read(fd, &c, 1) > 0) {
/* check for end of line */
if (c != '\n' && c != 0 && column < 1024 - 1)
buffer[column++] = c;
else {
/* string are null terminated in C */
buffer[column] = 0;
column = 0;
/* alloc memory for this line in result */
*result = (char **)realloc(*result, sizeof(char *) *
(line + 1));
/* duplicate buffer and store it in result */
(*result)[line++] = strdup(buffer);
}
}
free(buffer);
return line;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: %s [filename]", argv[0]);
return 1;
}
char **lines;
int line_count = read_file(argv[1], &lines);
if (line_count < 0) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
for(int i=0; i < line_count; i++)
printf("%s\n", lines[i]);
return 0;
}
Here an other version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int read_file(const char *filename, char ***result)
{
/* init result */
*result = NULL;
/* open the file */
FILE *file = fopen(filename, "r");
if (file == NULL)
return -1;
/* read the file line by line */
char *buffer = (char *)malloc(sizeof(char) * 1024);
int line = 0;
while (fgets(buffer, 1024, file)) {
*result = (char **)realloc(*result, sizeof(char *) *
(line + 1));
(*result)[line++] = strdup(buffer);
}
free(buffer);
return line;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: %s [filename]", argv[0]);
return 1;
}
char **lines;
int line_count = read_file(argv[1], &lines);
if (line_count < 0) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
for(int i=0; i < line_count; i++)
printf("%s\n", lines[i]);
return 0;
}
I've got this homework, which is to make an implementation of the "tail" command in Linux and this is what i have so far:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
char *resize(char *data, int size)
{
char *newData = malloc((size + 1) * sizeof(char));
int counter;
for(counter = 0; counter < size; counter++)
newData[counter] = data[counter];
return newData;
}
char *readStdIn(char *data)
{
char buff, end = '\n';
int rState, size = 1;
data = malloc(size * sizeof(char));
printf("%ld\n", sizeof(data));
while ((rState = read(STDIN_FILENO, &buff, 1)) > 0)
{
if(rState < 0)
{
if(errno == EINTR) rState = 0;
else
{
perror("read()");
return 0;
}
}
data = resize(data, size);
data[size - 1] = buff;
size++;
}
printf("%ld\n", sizeof(data));
if(rState == 0) write(STDOUT_FILENO, &end, 1);
return data;
}
int printLines(char *data)
{
int lines = 0, position;// counter;
for(position = sizeof(data) - 1; position > -1; position--);
if (data[position] == '\n') lines++;
return 0;
}
int main(int argc, char *argv[])
{
char *data = 0;
if(argc == 1)
{
readStdIn(data);
if(data == 0) return 1;
if(printLines(data) != 0) return 1;
}
return 0;
}
In readStdIn() I'm supposed to save what i have read in the standard input and put it in char* data, so that i can find the last 10 lines(or find that there are less than 10 lines) of the input. The problem is, that when i call resize(), data doesn't resize, and I can't find out why. It's most likely a problem in the resize() itself, but i can't figure it out. The idea is that resize() increases the size of data by 1 char.
I hope I was thorough enough in my explanation.
Your printf is incorrect. You are printing the size of a pointer, which will be a fixed number depending on your architecture. You should instead print out the size directly.
printf("%d\n", size);
Your resize is correct except that you failed to free the previous memory, so you have a memory leak. You can fix this by adding a free() before you return.
char *resize(char *data, int size)
{
char *newData = malloc((size + 1) * sizeof(char));
int counter;
for(counter = 0; counter < size; counter++)
newData[counter] = data[counter];
free(data);
return newData;
}
I'm writing a program that takes a file with the 3 lines of encrypted passwords and compares them to all 4 lower case letters from aaaa - zzzz. The only issue I am having is that I can't figure out how to go line by line of the file I input and how to compare it to the 4 letter words individually. It should then print the 3 decrypted 4 letter words that correlate to the passwords from the file. Also if there are any types on how to improve my code, please let me know. I'm a beginner at this so I need clear explanations and examples if possible. Thank you.
EDIT*****
The main problem is in the all function and main. I'm not wanting to print the aaaa, aaab, aaac, etc to the screen but I want to put it in an char array so that i can compare each individually to each line from the file using crypt. So I need advice on how to put all 456976 combinations into an array, compare it to each line of code, and print the solutions to the screen.
file looks like this:
$1$6gMKIopE$I.zkP2EvrXHDmApzYoV.B.
$1$pkMKIcvE$WQfqzTNmcQr7fqsNq7K2p0
$1$0lMKIuvE$7mOnlu6RZ/cUFRBidK7PK.
My code looks like this:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFFER_SIZE 1024
int my_fgets(char* buf, int len, int f)
{
for (int i = 0; i < len; i++,buf++)
{
int count = read(f, buf, 1);
if (!count || (buf[0] == '\n'))
{
buf[0] = 0;
return i;
}
}
return 0;
}
int inc(char *c,char begin, char end){
if(c[0]==0)
return 0;
if(c[0] == end){ // This make the algorithm to stop at char 'f'
c[0]=begin; // but you can put any other char
return inc(c+sizeof(char), begin, end);
}
c[0]++;
return 1;
}
char all(int a, int n,char begin, char end){
int i, j;
char *c = malloc((n+1)*sizeof(char));
char result[] = "";
for(i = a; i <= n; i++)
{
for(j=0;j<i;j++) c[j]=begin;
c[i]=0;
do {
int k = sizeof(result);
for (int g = 0; g < k -1; g++)
{
result[g] = c;
}
} while(inc(c,begin,end));
}
free(c);
}
int main(int argc, char* argv[])
{
char *result;
char let[456976];
int f = open("pass.txt", O_RDONLY);
if (f < 0)
return 0;
char buf[1024];
while (my_fgets(buf, sizeof(buf), f))
{
let = all(4,4,'a','z');
int i = 0;
result = crypt((let[i+1]), buf);
int ok = strcmp (result, pass) == 0;
return ok ? 0 : 1;
all(4, 4, 'a', 'z');
}
close(f);
}
I think you need to reword the question. Maybe the code below is what you want. Let's say you have a password, and you have a file which contains a list of encrypted passwords (or hash usually), you want to see if password is right or wrong. You compare the hash of the password with hashes in the file. I haven't tested this code.
ps, let me know if I am way off, I'll delete the answer.
void crypt(char* hash, const char* password_test) {
//create hash from password, or encrypt it or something?
}
int test_password(const char *filename, const char *password){
FILE *f;
f = fopen(filename, "r");
if (!f) return 0;
char password_hash[256];
crypt(password_hash, password);
char hash[256];
char buf[1024];
while (fgets(buf, sizeof(buf), f) != NULL)
{
crypt(hash, buf);
if (strcmp(password_hash, hash) == 0)
return 1;
}
fclose(f);
return 0;
}
void main() {
int result = test_password("test.txt", "password");
if (result == 1)
printf("password is good\n");
}
Reading line by line using open/read/close
int my_fgets(char* buf, int len, int f)
{
for (int i = 0; i < len; i++,buf++)
{
int count = read(f, buf, 1);
if (!count || (buf[0] == '\n'))
{
buf[0] = 0;
return i;
}
}
return 0;
}
int main(){
int f = open("test.txt", O_RDONLY);
if (f < 0) return 0;
char buf[1024];
while (my_fgets(buf, sizeof(buf), f))
printf("%s\n", buf);
close(f);
}