C: System calls and pointers to reverse a file - c

Solved. Thanks lutogniew.....just over complicating it...
So I am having some trouble with a homework assignment. The assignment is to take in a file (using only system calls), reverse it and write an output file with that reversed data on it (ASCII only). A catch is that the reverse section has to be done with pointers. I did the following below, which does work. However, it does not use pointers to reverse.
My question I guess, is how can I access something like data[] using pointers? Or, how can I read from the file into it. Everything I tried (mostly char**) only reads in null.
Any help would be much appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int i = 0;
int fileOut = open("output.txt", O_WRONLY | O_APPEND);
int fileIn = open("input.txt", O_RDONLY);
int start = lseek(fileIn, 0 , SEEK_CUR);
int end = lseek(fileIn, 0 , SEEK_END);
int restart = lseek(fileIn, 0-end , SEEK_CUR);
char data[end];
char reverseData[end];
read(fileIn, data, end);
for(i = 0; i< end; i++){
reverseData[i] = data[end-(i+1)];
}
write(fileOut, reverseData, end);
return 0;
}

After Accept Answer.
Something for OP to consider as another approach:
For fun, a not-so-serious recursive approach to reverse the file.
void reverse(int fileIn, int fileOut) {
char data;
if (read(fileIn, &data, 1) == 1) {
reverse(fileIn, fileOut);
write(fileOut, &data, 1);
}
}
int main(void) {
int fileOut = open("output.txt", O_WRONLY | O_APPEND);
int fileIn = open("input.txt", O_RDONLY);
reverse(fileIn, fileOut);
close(fileIn);
close(fileOut);
return 0;
}

You must understand that what is presented to you as an array in C, in reality is just a pointer, pointing to the beginning of the memory belonging to that array. This snippet should make everything quite clear:
int test[] = { 1, 2, 3 };
int* ptr = test + 2; // ptr now points to the third element
EDIT
As for loading data into an array: again, remember that array itself is just a pool of memory (a buffer) - it begins where the pointer mentioned above points to, and has the size equal to numberOfElements * sizeOfSingleElement. Now look at the signature of the read function:
size_t read(int fildes, void* buf, size_t nbytes);
It reads nbytes into buffer pointed to by buf. Rings a bell?

Related

How to read from pipe into a file?

I would like to read from the pipe straight into a file with the code below. base_fd is a pipe.
FILE* fp = fopen("dec_data", "wb+");
int r_result;
int len = msg_length-part-3; //set to 75933
while ((r_result = read(base_fd[0], fp, len))) {
printf("r_result: %d \n", r_result);
len -= r_result;
}
The read seems to happen fine, with r_result showing 65536 and then 10397 as required. However, when I inspect the file I created, it has a size of 0 bytes...
You have a semantic error in your code.
Take a look at the read(2) system call signature:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
The second parameter to the function is a void pointer (void *buf), which is where read will store the count bytes it reads from fd descriptor.
However, a FILE * is an abstraction of the C library. In this answer you can see more of it. The struct FILE in MinGW32 5.1.4 is:
typedef struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
What read will do is similar to how we copy strings. Consider this function:
void strcpy(char *dst, char *src)
{
while(*src) *dst++ = *src++;
}
This function will copy the contents from src into dst until it finds a NULL terminating byte. This is obviously a very flawed function and should never be used, but illustrates why your example doesn't work.
Under the hood, what read is doing is very similar to this strcpy function: it is overwriting a lot of bytes in memory starting at the address pointed to by the fp pointer. You are effectively losing your reference to the FILE * pointer and the resources associated to it.
I'll bet that if you try to close(fp) after that loop you'll get a segmentation fault (it's Undefined Behavior, but I'll bet anyway).
The right way to do what you want is:
FILE* fp = fopen("dec_data", "wb+");
char *buf;
int r_result;
int len = msg_length - part - 3; //set to 75933
buf = malloc(len);
if(!buf) {
perror("malloc");
exit(EXIT_FAILURE);
}
while ((r_result = read(base_fd[0], buf, len))) {
fprintf(fp, buf);
len -= r_result;
}
free(buf);
close(fp); // now it closes the file pointer

Segmentation fault when initializing pointer to char array

I am trying to write a program that reads text from a file, converts the characters to uppercase and then writes the output on a new file. The code works just fine for the reading and converting to uppercase parts, but for the writing the output part when I create a char* for the name of the output file, I get a segmentation fault.
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
void lowerToUpper(char* temp)
{
char* name;
name = strtok(temp,":");
// Convert to upper case
char* s = name;
while (*s)
{
*s = toupper((unsigned char) *s);
s++;
}
}
int main()
{
int fd;
char* file_name_read = "./test.txt";
fd = open(file_name_read, O_RDONLY);
char* buf_rd;
ssize_t nr;
size_t byte_count = 1000;
off_t offset = 0;
nr = pread(fd, buf_rd, byte_count, offset);
close(fd);
lowerToUpper(buf_rd);
char* file_name_write = "./test_uppercase.txt";
/* CODE FOR WRITING TO THE FILE */
return 0;
}
When I remove the char* file_name_write line, the code works fine. When I include it in the code, I get a segmentation fault.
I have tried
removing the call to the lowerToUpper() inside main
using char file_name_write[] instead of char* file_name_write
using malloc() to allocate space and then assign its value
using different byte_count and offset values
Edit:
The problem was an uninitialized pointer with buf_rd. When I added
char* buf_rd = (char*) malloc(1000 * sizeof(char));
it solved the problem.
Thank you Mr Lister and lurker!
char* buf_rd;
...
nr = pread(fd, buf_rd, byte_count, offset);
You did not allocate memory for buf_rd. It is just a pointer.
You use buf_rd as buffer, but that variable is only declared and never initialized.
The documentation says:
ssize_t pread(int fd , void * buf , size_t count , off_t offset );
pread() reads up to count bytes from file descriptor fd at offset
offset (from the start of the file) into the buffer starting at buf.
The file offset is not changed.
It is expected of you to initialize the a buffer that can be used by the pread function.

C Pointer of array of strings garbled when retreived later

I have read a lot of the answers on the theoretical issues with memory allocation to pointer to arrays, but have not been able to fix my code...so turning to you.
I have an array of strings in a STRUCT, which I need to write to and read from. Declared as:
typedef struct client_mod
{
/* Client ad_file */
char *ad_filenames[10];
/* Client's current ad array index*/
unsigned int ad_index;
} client;
Then , inside a function , I assign values to pointer:
static int get_spots (client_mod *client)
{
char buf[512];
FILE *ptr;
if ((ptr = popen("php /media/cdn/getspot.php", "r")) != NULL) {
/* Read one byte at a time, up to BUFSIZ - 1 bytes, the last byte will be used for null termination. */
size_t byte_count = fread(buf, 1, 512 - 1, ptr);
/* Apply null termination so that the read bytes can be treated as a string. */
buf[byte_count] = 0;
}
(void) pclose(ptr);
// parse extracted string here...
int i = 0;
client->ad_filenames[i] = strdup(strtok(buf,"|"));
while(client->ad_filenames[i]!= NULL && i<5)
{
client->ad_filenames[++i] = strdup(strtok(NULL,"|"));
if (client->ad_filenames[i] != NULL && strlen(client->ad_filenames[i]) > 5) {
LOG("TESTING FOR CORRECT FILE NAMES %s\n", client->ad_filenames[i]);
}
}
}
The problem comes when I retreive the values later:
/* in looping code block */
LOG("Checking file under index = %d, file is %s", client->ad_index, client->ad_filenames[client->ad_index]);
The first two members of the array are retreived normally, everything after that is garbled.
I would appreciate any guidance. Thanks!
I understand this probablby comes from undefined behaviour of assigning directly to the pointer, but I can't figure out how to solve it.
I think the problem is with assigning to this struct element.
char *ad_filenames[10];
ad_filenames is an array of 10 of pointer to characters.
What that means is that memory allocation is needed for each index.
Something like
client->ad_filenames[0] = strdup(var1);
strdup() does both malloc() and strcpy() within this function.
client should be a variable name. You already defined client as a type.
Here is working code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct client_mod
{
/* Client ad_file */
char *ad_filenames[10];
/* Client's current ad array index*/
unsigned int ad_index;
}CLIENT1;
CLIENT1 *client;
int func( char *var1 ) {
client->ad_filenames[0] = strdup(var1);
}
int
main(void)
{
char str1[10];
client = malloc( sizeof client );
strcpy( str1, "Hello" );
func( str1 );
printf("%s\n", client->ad_filenames[0] );
free(client->ad_filenames[0]);
free (client);
}
Your problem is with the line,
size_t byte_count = fread(buf, 1, 1000 - 1, ptr);
Read the man fread page,
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
you read 1000-1 members of size 1 into buf, which is only allocated buf[512], either expand buf or decrease fread 3rd argument,
buf[1000+1];
size_t byte_count = fread(buf, 1, sizeof(buf)-1, ptr);

Concatenate path and variable in C

This is my code. I need create 10 files with this format:
0.txt
1.txt
...
But I can't do it, the result is different. Can anyone help?
#include <stdlib.h>
#include <string.h>
char* direccion(char *dirD,int number){
char buffer[100];
char *n;
char *q;
char* formato=".txt";
sprintf(buffer, "%i", number);
n= strcat(dirD,buffer);
q= strcat(n,formato);
return q;
}
int main(){
int u;
int number= 0;
int t= 0;
char* q = "e:/c9/";
for(t=0; t< 10 ; t++){
char* dir = direccion(q,number);
if(fopen(dir,"w")){
fopen(dir,"w");
u=1;
}
else{
u=0;
printf("Error\n");
}
number++;
}
return 0;
}
Thanks !
Problem No 1. is:
char *q = "e:/c9/";
for(t = 0; t < 10; t++)
{
char *dir = direccion(q, number);
}
The variable q is a string literal. However, in the function direccion(), you pass it as the first argument to strcat(), which tries to modify it — BOOM, a segfault. Solution:
char buf[1000];
strcpy(buf, "e:/c9/");
char *dir = direccion(buf, number);
Problem #2 is as #Charlie Martin pointed out is that you return a statically automatically allocated local string, which is invalid after the direccion() function returns. Maybe
return strdup(buffer);
in this case; don't forget to free it after use.
Edit: seems you don't even ask about this. You can create a file using the open() system call (function):
#include <fcntl.h>
#include <stdio.h>
int fd = open("file.txt", O_RDWR | O_CREAT, 0644);
close(fd);
Of course substitute the actual file mode you want for 0644 (but don't make the file executable unless it contains a program to be executed).
Edit 2: I didn't even catch this one... So, in the for loop, you want to reset the base filename over and over:
for (yadda yadda)
{
strcpy(buf, "e:/c9/");
char *dir = direccion(buf, i);
}
etc.
You're allocating the buffer in automatic inside your function, which means it's allocated on the stack. When you return, that memory is no longer valid. You want to allocate the buffer in the caller and pass a pointer to it, or allocate it as static.
Windows has a series of path handling functions that you can use to do things like add an extension to a path.
For example:
PathAddExtension(buffer,".txt");
For more information see http://msdn.microsoft.com/en-us/library/windows/desktop/bb773559(v=vs.85).aspx
Also, it's a good idea to work with buffers of _MAX_PATH size when dealing with paths.

Copying a file line by line into a char array with strncpy

So i am trying to read a text file line by line and save each line into a char array.
From my printout in the loop I can tell it is counting the lines and the number of characters per line properly but I am having problems with strncpy. When I try to print the data array it only displays 2 strange characters. I have never worked with strncpy so I feel my issue may have something to do with null-termination.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
FILE *f = fopen("/home/tgarvin/yes", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos); fread(bytes, pos, 1, f);
int i = 0;
int counter = 0;
char* data[counter];
int length;
int len=strlen(data);
int start = 0;
int end = 0;
for(; i<pos; i++)
{
if(*(bytes+i)=='\n'){
end = i;
length=end-start;
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
printf("%d\n", counter);
printf("%d\n", length);
start=end+1;
counter=counter+1;
}
}
printf("%s\n", data);
return 0;
}
Your "data[]" array is declared as an array of pointers to characters of size 0. When you assign pointers to it there is no space for them. This could cause no end of trouble.
The simplest fix would be to make a pass over the array to determine the number of lines and then do something like "char **data = malloc(number_of_lines * sizeof(char *))". Then doing assignments of "data[counter]" will work.
You're right that strncpy() is a problem -- it won't '\0' terminate the string if it copies the maximum number of bytes. After the strncpy() add "data[counter][length ] = '\0';"
The printf() at the end is wrong. To print all the lines use "for (i = 0; i < counter; i++) printf("%s\n", data[counter]);"
Several instances of bad juju, the most pertinent one being:
int counter = 0;
char* data[counter];
You've just declared data as a variable-length array with zero elements. Despite their name, VLAs are not truly variable; you cannot change the length of the array after allocating it. So when you execute the lines
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
data[counter] is referring to memory you don't own, so you're invoking undefined behavior.
Since you don't know how many lines you're reading from the file beforehand, you need to create a structure that can be extended dynamically. Here's an example:
/**
* Initial allocation of data array (array of pointer to char)
*/
char **dataAlloc(size_t initialSize)
{
char **data= malloc(sizeof *data * initialSize);
return data;
}
/**
* Extend data array; each extension doubles the length
* of the array. If the extension succeeds, the function
* will return 1; if not, the function returns 0, and the
* values of data and length are unchanged.
*/
int dataExtend(char ***data, size_t *length)
{
int r = 0;
char **tmp = realloc(*data, sizeof *tmp * 2 * *length);
if (tmp)
{
*length= 2 * *length;
*data = tmp;
r = 1;
}
return r;
}
Then in your main program, you would declare data as
char **data;
with a separate variable to track the size:
size_t dataLength = SOME_INITIAL_SIZE_GREATER_THAN_0;
You would allocate the array as
data = dataAlloc(dataLength);
initially. Then in your loop, you would compare your counter against the current array size and extend the array when they compare equal, like so:
if (counter == dataLength)
{
if (!dataExtend(&data, &dataLength))
{
/* Could not extend data array; treat as a fatal error */
fprintf(stderr, "Could not extend data array; exiting\n");
exit(EXIT_FAILURE);
}
}
data[counter] = malloc(sizeof *data[counter] * length + 1);
if (data[counter])
{
strncpy(data[counter], bytes+start, length);
data[counter][length] = 0; // add the 0 terminator
}
else
{
/* malloc failed; treat as a fatal error */
fprintf(stderr, "Could not allocate memory for string; exiting\n");
exit(EXIT_FAILURE);
}
counter++;
You are trying to print data with a format specifier %s, while your data is a array of pointer s to char.
Now talking about copying a string with giving size:
As far as I like it, I would suggest you to use
strlcpy() instead of strncpy()
size_t strlcpy( char *dst, const char *src, size_t siz);
as strncpy wont terminate the string with NULL,
strlcpy() solves this issue.
strings copied by strlcpy are always NULL terminated.
Allocate proper memory to the variable data[counter]. In your case counter is set to 0. Hence it will give segmentation fault if you try to access data[1] etc.
Declaring a variable like data[counter] is a bad practice. Even if counter changes in the subsequent flow of the program it wont be useful to allocate memory to the array data.
Hence use a double char pointer as stated above.
You can use your existing loop to find the number of lines first.
The last printf is wrong. You will be printing just the first line with it.
Iterate over the loop once you fix the above issue.
Change
int counter = 0;
char* data[counter];
...
int len=strlen(data);
...
for(; i<pos; i++)
...
strncpy(data[counter], bytes+start, length);
...
to
int counter = 0;
#define MAX_DATA_LINES 1024
char* data[MAX_DATA_LINES]; //1
...
for(; i<pos && counter < MAX_DATA_LINES ; i++) //2
...
strncpy(data[counter], bytes+start, length);
...
//1: to prepare valid memory storage for pointers to lines (e.g. data[0] to data[MAX_DATA_LINES]). Without doing this, you may hit into 'segmentation fault' error, if you do not, you are lucky.
//2: Just to ensure that if the total number of lines in the file are < MAX_DATA_LINES. You do not run into 'segmentation fault' error, because the memory storage for pointer to line data[>MAX_DATA_LINES] is no more valid.
I think that this might be a quicker implementation as you won't have to copy the contents of all the strings from the bytes array to a secondary array. You will of course lose your '\n' characters though.
It also takes into account files that don't end with a new line character and as pos is defined as long the array index used for bytes[] and also the length should be long.
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_LINE_ARRAY_DIM 100
int main(int argc, char* argv[])
{
FILE *f = fopen("test.c", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos+1); /* include an extra byte incase file isn't '\n' terminated */
fread(bytes, pos, 1, f);
if (bytes[pos-1]!='\n')
{
bytes[pos++] = '\n';
}
long i;
long length = 0;
int counter = 0;
size_t size=DEFAULT_LINE_ARRAY_DIM;
char** data=malloc(size*sizeof(char*));
data[0]=bytes;
for(i=0; i<pos; i++)
{
if (bytes[i]=='\n') {
bytes[i]='\0';
counter++;
if (counter>=size) {
size+=DEFAULT_LINE_ARRAY_DIM;
data=realloc(data,size*sizeof(char*));
if (data==NULL) {
fprintf(stderr,"Couldn't allocate enough memory!\n");
exit(1);
}
}
data[counter]=&bytes[i+1];
length = data[counter] - data[counter - 1] - 1;
printf("%d\n", counter);
printf("%ld\n", length);
}
}
for (i=0;i<counter;i++)
printf("%s\n", data[i]);
return 0;
}

Resources