Program copying files incorrectly - c

So for an assignment in my Systems class, we have to make a program that takes a file in a given source and copies it to a given destination using one of three copyfile() functions.
When running the program in the command line, it will take in three arguments: ./cptest, the input file location, and the output file location, as well as two optional arguments: the number for which copyfile() function you would like to use as well as the buffer size.
If you choose 1 for the copyfile() function parameter, the program should copy the file using formatted I/O (file handles), and then copy the file one character at a time.
If you choose 2 for the copyfile() function parameter, the program should copy the file using integer file descriptors, and then copy the file one character at a time.
If you choose 3 for the copyfile() function parameter, the program should allocate a buffer of size equal to what size you put in for the buffer size parameter (say 1024), then use read() to read from the input file up to that many bytes at a time.
If you do not put in a third parameter (copyfile() function number), then the program will automatically use copyfile3() with whatever buffer size you decided with the fourth parameter.
If you do not enter a fourth parameter (buffer size), then the program will set it to 1024 by default and use it if necessary (not needed for copyfile1() or 2()).
Before and after each copyfile() function is called, the program uses gettimeofday() to make a timestamp. The program then makes a new timestamp to find how long the copying took using the difference in seconds and microseconds between the after timestamp and the before timestamp, then prints all this information out.
For example if I put in: ./cptest ~/My_Documents/photo.JPG ~/assig6/photo.JPG 3
The program should return:
Timestamp Before Copying:
Seconds: 1425150842, Microseconds: 914511
Timestamp After Copying:
Seconds: 1425150842, Microseconds: 927662
Copying took 0 seconds, 13151 microseconds
So now that you know what the program does, let's get down to the issue. Every time I run the programs with any of the copyfile() functions, be it 1, 2, or 3, technically speaking, the copying works fine, the file ends up in the correct destination, and the program returns all the correct information in terms of the timestamps.
However, in reality, it does not work, as when I use copyfile1(), it copies all of the bytes, but when I try to open the file - say a .jpg - it says that it can't view the file because it appears to be damaged, corrupted, or too large. When I use copyfile2() and copyfile3(), the file in the destination has only 1 KB, and when I don't pass in a third or fourth parameter, the output file has 0 KB. The same thing happens with word documents.
Is there a reason why it copies the files fine but the file is damaged in the process? It seems as though the code is right but the end result is not.
Here's the main file cptest.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include "cptest.h"
/** cptest.cpp
* A file copying program.
* Derived partially from caesar.cpp by Horstmann and Budd from big C++
*/
/** Prints usage instructions.
* #param program_name the name of this program
*/
void usage(char* program_name) {
// Prints out usage instructions if incorrect number of parameters is passed in
printf("Usage: %s infile outfile copy_function_number buffer_size \n", program_name);
}
/** Prints file opening error message
* #param filename the name of the file that could not be opened
*/
void open_file_error(char* filename) {
// Error message if unable to open the file
printf("Error opening file %s\n", filename);
}
/** Main program: copies a file.
* #param argc Number of command-line arguments (including program name).
* #param argv Array of pointers to character arays holding arguments.
* #return 0 if successful, 1 if failure.
*/
int main(int argc, char* argv[]) {
char* infilename; // Name of input file
char* outfilename; // Name of output file
int copy_function_number; // Chooses which copy function is used based on argv[3]
int buffer_size; // Size of buffer
int returnstatus; // 0 or 1 depending on success or failure to copy the file to the destination
struct timeval* before = malloc(sizeof(struct timeval)); // Struct for time before copying is done
struct timeval* after = malloc(sizeof(struct timeval)); // Struct for time after copying is done
if (argc != 3 && argc != 4 && argc != 5) {
usage(argv[0]); // Must have 2, 3, or 4 arguments in addition to ./cptest.
return 1; // Failure!
}
infilename = argv[1]; // Sets first parameter to the input file name
outfilename = argv[2]; // Sets second parameter to the output file name
if(argv[3] == NULL) {
copy_function_number = 3; // Program uses copyfile3 if no third parameter is entered
}
else {
copy_function_number = atoi(argv[3]); // Otherwise program uses whatever function is passed by third parameter
}
if (argv[4] == NULL) {
buffer_size = 1024; // Sets buffer size to 1024 if no fourth parameter is entered
}
else {
buffer_size = atoi(argv[4]); // Otherwise buffer size is whatever user enters as fourth parameter
}
if (copy_function_number == 1) {
gettimeofday(before, NULL); // Get timestamp before the copying
// Perform the copying with copyfile1() if the third parameter is 1
returnstatus = copyfile1(infilename, outfilename);
gettimeofday(after, NULL); // Get timestamp after the copying
}
if (copy_function_number == 2) {
gettimeofday(before, NULL); // Get timestamp before the copying
// Perform the copying with copyfile2() if the third parameter is 2
returnstatus = copyfile2(infilename, outfilename);
gettimeofday(after, NULL); // Get timestamp after the copying
}
if (copy_function_number == 3) {
gettimeofday(before, NULL); // Get timestamp before the copying
// Perform the copying with copyfile3() if the third parameter is 3
returnstatus = copyfile3(infilename, outfilename, buffer_size);
gettimeofday(after, NULL); // Get timestamp after the copying
}
else {
if (copy_function_number != 1 || copy_function_number != 2 || copy_function_number != 3 || argv[3] == NULL) {
gettimeofday(before, NULL); // Get timestamp before the copying
// Perform the copying with copyfile3() if no third parameter is entered
returnstatus = copyfile3(infilename, outfilename, buffer_size);
gettimeofday(after, NULL); // Get timestamp after the copying
}
}
struct timeval *copytime = difference_in_time(before, after); // Struct for time after copying is done
// Print out information of the timestamp before copying
printf("Timestamp Before Copying: \n Seconds: %d, Microseconds: %d\n", before->tv_sec, before->tv_usec);
// Print out information of the timestamp after copying
printf("Timestamp After Copying: \n Seconds: %d, Microseconds: %d\n", after->tv_sec, after->tv_usec);
// Print out information of the difference between the timestamps (how long the copying took)
printf("Copying took %d seconds, %d microseconds\n", copytime->tv_sec, copytime->tv_usec);
return returnstatus; // 0 if successful copy, 1 if unsuccessful.
}
/** Copies one file to another using formatted I/O, one character at a time.
* #param infilename Name of input file
* #param outfilename Name of output file
* #return 0 if successful, 1 if error.
*/
int copyfile1(char* infilename, char* outfilename) {
FILE* infile; //File handle for source.
FILE* outfile; // File handle for destination.
infile = fopen(infilename, "r"); // Open the input file.
if (infile == NULL) {
open_file_error(infilename); // Error message if there was a problem opening the input file.
return 1; // Failure!
}
outfile = fopen(outfilename, "w"); // Open the output file.
if (outfile == NULL) {
open_file_error(outfilename); // Error message if there was a problem opening the output file.
return 1; // Failure!
}
int intch; // Character read from input file. must be an int to catch EOF.
unsigned char ch; // Character stripped down to a byte.
// Read each character from the file, checking for EOF.
while ((intch = fgetc(infile)) != EOF) {
ch = (unsigned char) intch; // Convert to one-byte char.
fputc(ch, outfile); // Write out.
}
fclose(infile); // Close the input file.
fclose(outfile); // Close the output file.
return 0; // Success!
}
/** Copies one file to another using integer file descriptors instead of file handles, one character at a time.
* #param infilename Name of input file
* #param outfilename Name of output file
* #return 0 if successful, 1 if error.
*/
int copyfile2(char* infilename, char* outfilename) {
int infile; //File handle for source.
int outfile; // File handle for destination.
// Allocates a buffer for the chars that will be read from the input and written to the output.
char buffer[1024];
infile = open(infilename, O_RDONLY); // Open the input file.
if (infile < 0) {
open_file_error(infilename); // Error message if there was a problem opening the input file.
return 1; // Failure!
}
outfile = open(outfilename, O_WRONLY | O_CREAT); // Open the output file.
if (outfile < 0) {
open_file_error(outfilename); // Error message if there was a problem opening the output file.
return 1; // Failure!
}
int intchin; // Character read from input file. must be an int to catch EOF.
int intchout; // Character written to the output file. must be an int to catch EOF.
// Size of the buffer so that when you are copying extremely large files, it does not have to go through 200000 loop iterations
int buffer_size = 1024;
unsigned char ch; // Character stripped down to a byte.
// Read each character from the file, checking for 0.
while ((intchin = read(infile, buffer, buffer_size)) != 0) {
ch = (unsigned char) intchin; // Convert to one-byte char.
intchout = write(outfile, buffer, ch); // Write out.
}
close(intchin); // Close the input file.
close(intchout); // Close the output file.
return 0; // Success!
}
/** Copies one file to another using integer file descriptors, buffer_size characters at a time.
* #param infilename Name of input file
* #param outfilename Name of output file
* #param buffer_size Size of the buffer for reading and writing
* #return 0 if successful, 1 if error.
*/
int copyfile3(char* infilename, char* outfilename, int buffer_size) {
int infile; //File handle for source.
int outfile; // File handle for destination.
// Allocates a buffer of size buffer_size for the chars that will be read from the input and written to the output.
char* buffer = (char*) malloc(sizeof(char)* buffer_size);
infile = open(infilename, O_RDONLY); // Open the input file.
if (infile < 0) {
open_file_error(infilename); // Error message if there was a problem opening the input file.
return 1; // Failure!
}
outfile = open(outfilename, O_WRONLY | O_CREAT); // Open the output file.
if (outfile < 0) {
open_file_error(outfilename); // Error message if there was a problem opening the output file.
return 1; // Failure!
}
int intchin; // Character read from input file. must be an int to catch EOF.
int intchout; // Character written to the output file. must be an int to catch EOF.
unsigned char ch; // Character stripped down to a byte.
// Read each character from the file, checking for 0.
while ((intchin = read(infile, buffer, buffer_size)) != 0) {
ch = (unsigned char) intchin; // Convert to one-byte char.
intchout = write(outfile, buffer, ch); // Write out.
}
close(intchin); // Close the input file.
close(intchout); // Close the output file.
free(buffer); // Free the buffer that was allocated.
return 0; // Success!
}
/** Makes a new timeval struct that determines the difference between two timestamps
* #param time1 Struct containing the information for the first timestamp
* #param time2 Struct containing the information for the second timestamp
* #return The struct made using the two parameters
*/
struct timeval* difference_in_time(struct timeval* time1, struct timeval* time2) {
struct timeval* copytime = malloc(sizeof(struct timeval)); // Allocates a struct to hold the difference between the two timestamps.
if ((time2->tv_sec - time1->tv_sec) < 0) {
// Error message for if the first timestamp entered was before the second timestamp.
printf("Seconds value is negative! time1 should be before time2!\n");
}
if ((time2->tv_usec - time1->tv_usec) < 0) {
// Handles if the difference in microseconds between the second and first timestamps would be negative, subtracting 1 from the seconds.
copytime->tv_sec = (time2->tv_sec - time1->tv_sec) - 1;
// Handles if the difference in microseconds between the second and first timestamps would be negative, subtracting the difference from 1000000.
copytime->tv_usec = 1000000 - (time2->tv_usec - time1->tv_usec);
}
else {
// Otherwise the seconds for the third timestamp is the difference between the seconds of the second and first timestamps.
copytime->tv_sec = (time2->tv_sec - time1->tv_sec);
// Otherwise the microseconds for the third timestamp is the difference between the microseconds of the second and first timestamps.
copytime->tv_usec = (time2->tv_usec - time1->tv_usec);
}
return copytime; // Return the new timestamp created.
}
Here's the header file cptest.h:
#ifndef CPTEST_H
#define CPTEST_H
// function prototypes
void usage(char* program_name);
void open_file_error(char* filename);
int copyfile1(char* infilename, char* outfilename);
int copyfile2(char* infilename, char* outfilename);
int copyfile3(char* infilename, char* outfilename, int buffer_size);
struct timeval* difference_in_time(struct timeval* time1, struct timeval* time2);
#endif
And here's the makefile:
cptest: cptest.o
gcc -g cptest.o -o cptest
cptest.o: cptest.c cptest.h
gcc -c -g cptest.c
clean:
rm -f *.o cptest
docs:
doxygen
chmod a+r html/*
cp -p html/* ~/public_html/cs2303assig6
EDIT: Thought it might be easier to show you what happens using the readme.txt we need to include with our program as an example. Shortened it down to the useful information so you don't have to through more walls of text.
Original:
Name: *My name*
Section: CS2303 C01
What Program Does: *Explanation of what program does*
Example: *Example of what should show up when running program in the command line*
Results: *Test data for time it took for each function and using different buffer sizes to find most efficient function and buffer size*
Compiling:
To compile the code, use 'make' in the command line. The makefile should already link all of the .o files to the executable, so the work is done for you.
Result when using copyfile1():
so the work is done for you.S2303 C01
What Program Does: *Explanation of what program does*
Example: *Example of what should show up when running program in the command line*
Results: *Test data for time it took for each function and using different buffer sizes to find most efficient function and buffer size*
Compiling:
To compile the code, use 'make' in the command line. The makefile should already link all of the .o files to the executable, so the work is done for you.
Result when using copyfile2() and 3():
so the work is done for you.

copyfile2 and 3 are completely broken because they cast the number of characters read into a char. As the buffer size is often 1024, this cast gives a result of 0 so nothing is written.
For copyfile 1, I'd recommend opening the files with a binary flag ("rb" or "wb") although I'm not convinced that is the issue.

Related

Check multiple files with "strstr" and "fopen" in C

Today I decided to learn to code for the first time in my life. I decided to learn C. I have created a small program that checks a txt file for a specific value. If it finds that value then it will tell you that that specific value has been found.
What I would like to do is that I can put multiple files go through this program. I want this program to be able to scan all files in a folder for a specific string and display what files contain that string (basically a file index)
I just started today and I'm 15 years old so I don't know if my assumptions are correct on how this can be done and I'm sorry if it may sound stupid but I have been thinking of maybe creating a thread for every directory I put into this program and each thread individually runs that code on the single file and then it displays all the directories in which the string can be found.
I have been looking into threading but I don't quite understand it. Here's the working code for one file at a time. Does anyone know how to make this work as I want it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//searches for this string in a txt file
char searchforthis[200];
//file name to display at output
char ch, file_name[200];
FILE *fp;
//Asks for full directory of txt file (example: C:\users\...) and reads that file.
//fp is content of file
printf("Enter name of a file you wish to check:\n");
gets(file_name);
fp = fopen(file_name, "r"); // read mode
//If there's no data inside the file it displays following error message
if (fp == NULL)
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
//asks for string (what has to be searched)
printf("Enter what you want to search: \n");
scanf("%s", searchforthis);
char* p;
// Find first occurrence of searchforthis in fp
p = strstr(searchforthis, fp);
// Prints the result
if (p) {
printf("This Value was found in following file:\n%s", file_name);
} else
printf("This Value has not been found.\n");
fclose(fp);
return 0;
}
This line,
p = strstr(searchforthis, fp);
is wrong. strstr() is defined as, char *strstr(const char *haystack, const char *needle), no file pointers in it.
Forget about gets(), its prone to overflow, reference, Why is the gets function so dangerous that it should not be used?.
Your scanf("%s",...) is equally dangerous to using gets() as you don't limit the character to be read. Instead, you could re-format it as,
scanf("%199s", searchforthis); /* 199 characters + \0 to mark the end of the string */
Also check the return value of scanf() , in case an input error occurs, final code should look like this,
if (scanf("%199s", searchforthis) != 1)
{
exit(EXIT_FAILURE);
}
It is even better, if you use fgets() for this, though keep in mind that fgets() will also save the newline character in the buffer, you are going to have to strip it manually.
To actually perform checks on the file, you have to read the file line by line, by using a function like, fgets() or fscanf(), or POSIX getline() and then use strstr() on each line to determine if you have a match or not, something like this should work,
char *p;
char buff[500];
int flag = 0, lines = 1;
while (fgets(buff, sizeof(buff), fp) != NULL)
{
size_t len = strlen(buff); /* get the length of the string */
if (len > 0 && buff[len - 1] == '\n') /* check if the last character is the newline character */
{
buff[len - 1] = '\0'; /* place \0 in the place of \n */
}
p = strstr(buff, searchforthis);
if (p != NULL)
{
/* match - set flag to 1 */
flag = 1;
break;
}
}
if (flag == 0)
{
printf("This Value has not been found.\n");
}
else
{
printf("This Value was found in following file:\n%s", file_name);
}
flag is used to determine whether or not searchforthis exists in the file.
Side note, if the line contains more than 499 characters, you will need a larger buffer, or a different function, consider getline() for that case, or even a custom one reading character by character.
If you want to do this for multiple files, you have to place the whole process in a loop. For example,
for (int i = 0; i < 5; i++) /* this will execute 5 times */
{
printf("Enter name of a file you wish to check:\n");
...
}

How to replace the text into a particular location of a file by passing the argument via command line argument

My intention is to read the second element in the 1st row and replace it with the value which we pass as an command line argument and replace it in the input.txt file
Input file:logic.txt
one=1234
two=3456
I want my file to be changed like this after compiling the code.
./a.out 1567
Currently i am getting output like this
./filelogic 1567
1567=1234
two=5678
Expected Output file should be modified like this after the compilation
logic.txt
one=1567
two=5678
char buf[MAXSIZE] = {};
int num = 0;
int i = 0;
num = atoi(argv[1]);
printf("%d",num);
FILE *fp1;
fp1 = fopen("logic.txt","r");//currently reading the file.
if(fp1 != NULL)
{
fseek(fp1,3,0);//Need to Move the pointer to the 3rd position where i need to replace the num(fseek(fp1,??,0))-->how we can achieve that.
//Using which method i can replace the num value into a file (means need to replace 1234 inplace of 1567)
//Once the changes are done need to replace in the same file.
fread(buf, 1, MAXSIZE, fp1);
printf("%s\n",buf);
fclose(fp1);
}else {
printf("Cannot open file"");
exit(1);
}
Could someone guide me to resolve this issue?Thanks in advance
You can make replacements to a file in-place, but you should not do this in practice. You will likely corrupt the file if you attempt to replace characters and do not make an exact one-to-one replacement of characters already in the file.
To safely change the contents of a file, read the entire file contents into memory, make the changes needed, and then truncate the current file and write the new contents to the truncated file. (if the file is too large for in-memory operations, then use a temporary file)
You do not want to use atoi to convert the string "1567" to an integer. You are replacing characters in a file, not integer values in a binary file, so work with characters instead.
Your project is complicated by only wanting to replace the text after the first '=' sign. This may or may not be on the first line of the file, so you will need some flag to indicate when the first '=' is found and the replacement is made. (once the replacement is made, you can simply break your read loop and close the file -- but below the example output all lines for convenience)
Any time you close a file after writing to it, you should validate the return of fclose to catch any stream errors, or errors that occurred on the last write that will not be apparent until the next file operation.
With those cautions and caveats in mind, you could do something similar to:
#include <stdio.h>
#include <string.h>
#define MAXSIZE 64 /* max line/buffer size */
#define FNAME "logic.txt" /* default file name */
#define REPLACE "1567" /* default replacement text */
int main (int argc, char **argv) {
char buf[MAXSIZE] = ""; /* line buffer */
const char *str = argc > 1 ? argv[1] : REPLACE; /* replacement string */
int replaced = 0; /* flag indicating replacement made */
FILE *fp = fopen (FNAME, "r+"); /* open file reading/writing */
if (!fp) { /* validate file open for reading/writing */
perror ("fopen-FNAME");
return 1;
}
while (fgets (buf, MAXSIZE, fp)) { /* read each line in file */
if (!replaced) { /* if replacement not yet made */
char *p = strchr (buf, '='); /* search for '=' in line */
if (p) { /* if found */
size_t plen = 0; /* var for length to end */
p++; /* advance past '=' sign */
plen = strlen (p); /* get length to end */
if (plen < strlen (str)) { /* check avail length */
fprintf (stderr, "error: not enough space in line.\n");
return 1;
}
strcpy (p, str); /* copy str to p */
if (fseek (fp, -plen, SEEK_CUR)) { /* backup plen chars */
perror ("fseek(fp)");
return 1;
}
fputs (p, fp); /* overwite contents with replacement */
replaced = 1; /* set flag indicating replacement */
} /* (you can break, and remove output */
} /* here if not writing to stdout) */
fputs (buf, stdout); /* output lines to stdout (optional) */
}
if (fclose (fp) == EOF) /* always validate close-after-write */
perror ("fclose-FNAME");
return 0;
}
Using your file logic.txt as an example input, and naming the executable filelogic as you have, the use and operation of the code above yields:
logic.txt File Before
$ cat logic.txt
one=1234
two=3456
Example Use/Output
$ ./filelogic
one=1567
two=3456
logic.txt File After
$ cat logic.txt
one=1567
two=3456
Again, this is fine for a learning endeavor, but in practice, avoid making changes to files in-place as the risk of inadvertent file corruption far outweighs writing a new file with the changes.

How to read values from a text file and store them in different variable types in C

I need to read a text file with 7 lines into 7 different variables. The text file looks like this:
.2661
A.txt
B.txt
C.txt
1
2
0.5 0.6
These are the variables that I need to store each line into:
float value1; // line 1 from .txt file
char *AFileName; // line 2 from .txt file
char *BFileName; // line 3 from .txt file
char *CFileName; // line 4 from .txt file
int value2; // line 5 from .txt file
int lastLineLength; // line 6 from .txt file
double lastLine[lastLineLength]; // line 7 from .txt file - this can be different lengths
I have currently been doing this by just using the arguments when I call my program from the command line and the argv command.
First open the file using fopen with read access:
FILE *inputFile = fopen(filename, "r");
if(!inputFile) {
// Error opening file, handle it appropriately.
}
Then read the data from the file using fscanf. The first parameter is the FILE * we created above. The second parameter is a format string that specifies what fscanf should expect while reading the file. The remaining parameters are pointers to variables that will hold the data read from the file.
int variablesFound;
variablesFound = fscanf(inputFile, "%f\n%s\n%s\n%s\n%d\n%d\n", &value1, AFileName, BFileName, CFileName, &value2, &lastLineLength);
if(variablesFound < 6) {
// There was an error matching the file contents with the expected pattern, handle appropriately.
}
double lastLine[lastLineLength];
// Iterate over the last line.
int lastLineIndex;
for(lastLineIndex = 0; lastLineIndex < lastLineLength; lastLineIndex++) {
fscanf(inputFile, "%lf", &lastLine[lastLineIndex]);
fscanf(inputFile, " "); // Eat the space after the double.
}
Edit
After comments I realized it might be worth noting that you have to allocate memory to your variables as the real first step. The primitives (those with an & below) can be declared as normal. For the string (char array), you'll want to do one of the following:
char *aFileName = calloc(MAX_FILENAME_SIZE + 1, sizeof(char));
or
char aFileName[MAX_FILENAME_SIZE + 1];
Depending on what your purpose with aFileName would be determines which method would be appropriate. However, assuming this code appears in the main or doesn't need to exist beyond the scope of the function, the latter would be better as it doesn't require free()ing the variable after you're done with it.
It also may be worth while singling out the code that deals with reading input if your requirements change often.
You can read from the file as follows:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE * fp;
char * line = NULL;
size_t len = 80;
fp = fopen("<path to your file>", "r");
if (fp == NULL)
exit(-1);
while (getline(&line, &len, fp) != -1)
printf("%s", line);
fclose(fp);
return 0;
}
getline reads character strings from the file, so you'd have to parse the lines as needed (atoi, atof).

ARRAYS in C a hassle

I have been trying to program a UNIX style shell command prompt in C. Within this program I need it to keep track of the commands that have already been used, so the user can recall the last command by entering 'r'. I made a globally initialized array to hold strings. Whenever the array of characters a user entered needs to be saved, I add it to the global array. I have tried memcpy, simply copying each value using a loop, and just copying the pointer. None of these have been working. I am not super familiar with C and I am sure it is a pointer problem.
Whenever I copy the pointer of inputBuffer to my global array string (it does get copied), however upon leaving the setup function this pointer disappears? I am not exactly sure what I am doing wrong.
Test:
(1)user input --> ls
string[0] = ls
(2)user input --> r
inputBuffer = ls
string[recent] = ls
incorrectly does...
inputBuffer = r
string[recent] = r
(I have included the relevant parts of the code.)
#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
#define SAVED_BUFFER 100
char *string[SAVED_BUFFER];
int p = 0;
int recent = -1;
int stringSize = 0;
void setup(char inputBuffer[], char *args[],int *background)
{
int length, /* # of characters in the command line *
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
exit(0); /* ^d was entered, end of user command stream */
if (length < 0){
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}
if (inputBuffer[0] == 'r' && inputBuffer[1] == '\n' && stringSize > 0) {
int k;
memcpy(inputBuffer, string[recent], strlen(string[recent]) + 1);
printf("%s",inputBuffer);
printf("%s",string[recent]);
}
else {
string[p] = inputBuffer;
printf("%s", string[0]);
stringSize++;
recent++; // one behind strings current array location, to get history
p++; // current string array spot
}
}
int main(void)
{
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2+1];/* command line (of 80) has max of 40 arguments */
while (1) { /* Program terminates normally inside setup */
background = 0;
printf("COMMAND2->");
fflush(0);
setup(inputBuffer, args, &background); /* get next command */
}
}
When you "save the input buffer" you actually only store a pointer to the inputBuffer array:
string[p] = inputBuffer;
The actual data is not copied, you just store a pointer to the global input buffer. When the next input replaces the old content of inputBuffer, you will see the new content even if you access it through string[recent].
The calls to memcpy don't actually do anything, since the passed input and output buffer all refer to the same memory.
To actually store a copy of the data you have to allocate new memory to store the copy. Since you are dealing with strings, this is most easily done with strdup(), which duplicates a string:
string[p] = strdup(inputBuffer);
Later, once you are done with such a copy and don't need it anymore you have to free the memory used by the copy:
free(string[p]);
Have you tried changing
char *string[SAVED_BUFFER];
to
char string[SAVED_BUFFER][MAX_LINE];
I think that's how you're treating it in the code

Can I pass a string into fopen()? in c

My goal is to gather input and open files based on that input.
FILE*
open_input_file (char* fileName) //opens source file to be read
{
return fopen(fileName, "r");
}
In an earlier function, I collect input from the user and save it to fileName. When I debug the program, it tells me fopen is returning NULL. That's not what I want, and I'm not sure where the problem is.
int main(void)
{ FILE* inFile = NULL;
char infileName[32] = {'\0'};
gather_input(infileName); // infileName is an output parameter for this
inFile = open_input_file(infileName);
}
I don't know what the problem is. Any thoughts?
If fopen returns NULL, the open failed. errno will hold the failure code and strerror(errno) will return a short description of why the open failed.
#include <errno.h>
#include <string.h>
...
int main(void)
{ FILE* inFile = NULL;
char infileName[32] = {'\0'};
gather_input(infileName); // infileName is an output parameter for this
if (!(inFile = open_input_file(infileName))) {
fprintf(stderr, "Error opening '%s': %s\n",
infileName, strerror(errno));
} else {
// open successful
...
}
}
Off-topic
gather_input better make sure infileName is null-terminated to prevent buffer overflows. The simplest way to do this is to define the size of the file name buffer as a macro and set the last character to 0.
#define FILENAMELEN 32
void gather_input(char infileName[]) {
...
infileName[FILENAMELEN-1]=0;
}
int main(void)
{ FILE* inFile = NULL;
char infileName[FILENAMELEN] = {'\0'};
This isn't very flexible. You could instead pass the size of the file name buffer into gather_input.
#define LENGTH(a) (sizeof(a) / sizeof(a[0]))
void gather_input(char infileName[], size_t len) {
...
infileName[len-1]=0;
}
int main(void)
{ FILE* inFile = NULL;
char infileName[32] = {'\0'};
gather_input(infileName, LENGTH(infileName)); // infileName is an output parameter for this
An alternative to setting the last character, if using standard string manipulation functions, is to use the strl* functions (strlcpy and strlcat) rather than their unbounded cousins. If you aren't using strl*, you should be using strncpy and strncat.
Have you checked that the file pointed to by inFilename exists on your HDD ?
Check the value of infileName in your debugger or put a printf statement to show the value on screen. printf("'%s'\n", infileName);
Did you call fclose() on your file inside the open_input_file() call. Maybe the file is still locked ?
Edit: I just checked the code. I have modified your english_to_morse() function. 1. The while statement is easier to follow than the for. 2. fgetc() returns an int and not a char.
At the top of the initialise I added this. This initialises every string in the array with and undefined string of ".??.". This will make it easier to find strange bugs as everything in your array is at least initialised.
I have modified different sections of the code but you should be able to follow.
initialize_morse_alphanum (char morseStrings[91][6])
{
for (int i=0;i<91;i++)
strcpy(morseStrings[i], ".??.");
....
....
void
english_to_morse(FILE* inputFile, FILE* outputFile, char morseStrings[91][6])
{ int convert;
convert = fgetc(inputFile);
while (convert != EOF)
{
fputs(morseStrings[convert], outputFile);
fputc(' ', outputFile);
printf ("%s ", morseStrings[convert]);
convert = fgetc(inputFile);
}
}
open_output_file (char* fileName) //opens destination file to be written
{ FILE* handle = NULL;
handle = fopen (fileName, "w"); <---- Remove the * from filename
return handle; }
Also, as mentioned in a different answer, it would be good to add some bounds checks to different areas of the code. At the moment it is quite prone to crashing. If my input file contains a lowercase 'a' (ascii 96) your program will be accessing memory that is out of bounds. So you should add a line like if (convert >= '0' && convert <= 'Z') in there somewhere. I will let you work that out.
Make sure that gather_input works properly. Could it be a problem because you're trying to read a file you're also writing on? In this case, try to close and open again the stream.

Resources