C Program - Custom Text Editor Program - c

I am building a program for class that is supposed to function as a very basic text-based text editor. It has 9 commands that can be passed to it, each resulting in a different command and we are using a doubly linked list to manage the lines of text that can be appended, inserted, removed and navigated through. I have been given a few functions to work with, and although most of my questions I think are more conceptual, I'll provide these given functions as a basis of background information:
// Function: get_line
// Reads a line (of arbitrary length) from an input source (stdin or file)
// and returns a pointer to the array that stores the line read
//
char *get_line(FILE *f)
{
int size = 80; // initial size of memory block allocated
char *linePtr = malloc(size); // allocate memory block
if (linePtr != NULL) { // if allocation successful
int c = EOF;
// read a line of text and replace newline character
// with a string terminator character
int i = 0;
while ((c = getc(f)) != '\n' && c != EOF) {
linePtr[i++] = (char) c;
if (i == size) {
size *= 2;
linePtr = realloc(linePtr, size);
}
}
linePtr[i] = '\0';
// if end-of-file before any characters were read,
// release the whole buffer
if (c == EOF && i == 0) {
free(linePtr);
linePtr = NULL;
} else {
// release unused portion of memory
linePtr = realloc(linePtr, i+1);
}
}
return linePtr;
}
My self-defined "append" function:
//
// Function: append_line
// Inserts a line after the current line (or node) in the linked-list
//
void append_line(char *t)
{
line *new_stack;
line *tmp;
new_stack = malloc(sizeof(line));
tmp = malloc(sizeof(line));
if((new_stack == NULL) || (tmp == NULL)) {
fprintf(stderr, "Insufficient memory to allocate. Closing application.\n");
exit(1);
}
if(current_line == NULL) {
if(head == NULL) {
head = new_stack;
current_line = new_stack;
new_stack->prev = NULL;
new_stack->next = NULL;
}
}
else {
tmp = current_line->next;
current_line->next = new_stack->prev;
new_stack->next = tmp;
current_line = new_stack;
}
new_stack->text = t;
free(tmp);
}
And this is my read_file function that doesn't really do anything yet, but I'm not exactly sure if I've got the right mind-set going into the creation of this function:
// Function: read_file
// Reads text from the specified file and calls append_line to insert lines
// into the linked-list. It returns the number of lines read.
//
int read_file(char *filename)
{
char * temp, no_command;
temp = strtok(filename, " ");
while(temp != NULL) {
no_command = temp;
temp = strtok (NULL, " ");
}
/* By doing this --^, I hope it will set temp to the actual
// file name after tokenization and completely ignore
// the command that comes with filename */
FILE *fin;
int counter = 0;
fin = fopen(no_command, "r");
if(fin == NULL) {
printf("You have entered a file that does not exist.");
exit(0);
}
get_line(fin);
fclose(fin);
}
If I wanted to send get_line input entered by the user from another function, could I send get_line stdin for it to recognize user input typed on-screen? Or would I have to make use of some form of fgets to send it the information?
If the user should be allowed to enter in multiple lines by separating them with the enter key (aka \n), and is expected to be allowed to press CTRL+D to continue with the function, how does one tell the application to make CTRL+D the EOF?
My get_line function takes in an entire file, and outputs a line. I am instructed to make use of multiple calls of get_line to take files of multiple lines and send each line into their own respective stack entry. How do I tell the application, "Here's the same file, but I want you to NOW check the next line instead of the one you output previously"? I assume that once I figure this out, I can apply the same logic to input entered in on the fly by the user.
I have been instructed that the get_line function is complete, so I feel like in the places I would call get_line (such as in read_file), I would need to control how much is sent to get_line at a time by making read_file read up to the point where a \n is encountered, change it to an end-of-line symbol (aka '\0') and send it to get_line, and then somehow have read_file continue after that point doing the same thing until EOF is reached. But I also feel like a lot of this functionality is within the get_line function as well... so I suppose I'm confused on the implementation of my given functions.
I'll probably have more questions in the future, but for now, I believe this initial question is long-winded enough. I'm hoping that figuring these things out, the rest of it will just click in place within my mind. Thank you for your time!

There are a couple of more errors in the append_line function, for example you use new_stack->prev before it's initialized.
Here comes my take on it:
void append_line(char *t)
{
/* Allocate and clear (i.e. set all to 0) */
line *new_stack = calloc(1, sizeof(line));
if(current_line == NULL) {
if(head == NULL) {
head = current_line = new_stack;
}
}
else {
new_stack->next = current_line;
new_stack->prev = current_line->prev;
if (current_line->prev)
current_line->prev->next = new_stack;
else
head = new_stack; /* No previous node, means current_line is at head */
current_line = new_stack;
}
new_stack->text = t;
}

Related

Pipe not clearing - I'm reading the same first character every time

I'm currently reading a pipe from a child process as follows. The problem is that while the fgetc is working, it's not starting a new line. It's continuously reading so my program always breaks at '!'
E.g:
! -> !example -> !exampleExample
instead of
! -> example -> Example
Below is my read function, but the pipe is basically not clearing when read.
char* read_line(FILE* file) {
char* result = malloc(sizeof(char) * 80);
int position = 0;
int next = 0;
while (1) {
next = fgetc(file);
if (next == '!') {
return "!";
}
if (next == EOF || next == '\n') {
result[position] = '\0';
return result;
} else {
result[position++] = (char)next;
}
}
}
There is a possibility of crash as there is no limit over the access result[position]. The memset is not done after malloc. Ignoring all these the issue seems to be in the function calling the read_line. Check on the handling of the returned address , as return "!" is not correct.

I am trying to make recursive function which is reading from file character wise but it's not working in C

I have been getting hands on recursive technique using C , here the problem I am facing -
bool FetchInputFromFile(int file_dis ){
// file_dis is the file descriptor which I have used in main with `open` sys call
char ch; //I want to read char wise
size_t ch_size =sizeof(ch );
char temp[30];
size_t index =0;
size_t numread;
//memset(temp, 0, 30 );
numread =read(file_dis, &ch, ch_size );
if(ch == ' ' ){
temp[index ] = '\0';
index =0;
InsertInList(temp ); //calling function with temp
}else temp[index++] = ch;
//1//
//base case and recursive call
if(numread ==0 ) return true;
else if(numread == -1 )return false;
else FetchInputFromFile(file_dis );
}
if I put printf("%s", temp ); where I have mentioned //1// above then the output is coming fine but if I call function over there , its going character wise.
What I am trying to do is I am reading file with open sys call and I am passing the file to the above function and trying to read char by char.But, it's not happening.
Please help me how I can call function where the output goes word by word.
THANKS!!!!
I think this might be a better approach
bool FetchInputFromFile(int file_dis, char* nextWord, int* index)
{
#define MAXSTRINGLENGTH (30)
/* file_dis is the file descriptor which I have used in main with `open` sys call */
const size_t ch_size = sizeof(char); /* size of types not variables.*/
char currentChar; /* I want to read char wise*/
size_t readResult;
if (!nextWord)
{ /* Allocate memory for the buffer if there is none */
nextWord = (char*)malloc(sizeof(char) * MAXSTRINGLENGTH);
}
readResult = read(file_dis, &currentChar, ch_size);
if (currentChar == ' ')
{
nextWord[(*index)] = '\0'; /* Terminate the string*/
InsertInList(nextWord); //calling function with temp */
*index = 0; /* Reset the index to zero to reuse the buffer nextWord*/
}
else if ((*index) < MAXSTRINGLENGTH)
{
nextWord[(*index)++] = currentChar;
//1*/
}
//base case and recursive call*/
if (readResult == 0)
{
if (nextWord) /* Should be a function/macro*/
{
free(nextWord);
nextWord = NULL;
}
return true;
}
else if (readResult == -1)
{
if (nextWord) /* Should be a function/macro*/
{
free(nextWord);
nextWord = NULL;
}
return false;
}
else
{
return FetchInputFromFile(file_dis, nextWord, index);
}
}
(I have not compiled this!)
Firstly when you write to the string you need to check that you don't over flow the buffer. You need to ensure every branch returns a value OR just have a single return statement and return a variable that the different branches set.
And most importantly, format you code clearly, you write the code one, but it is read many more times, so take those few moments to add curly braces and tabs.
And I still think this is a bad way to read words, but I assume there is a reason to do it this way.
What I have trying to do is making temp and index local variables. hence, I changed my function call-
bool FetchInputFromFile(char * temp, size_t * value, int file_dis );
and I added one line inside the function, which is,
size_t index =*value;
and removed memset call.
Please post me if I can make this function call better.
Thanks for help.

Using fgetc to pass only part of a text file to a buffer

I have the following text file:
13.69 (s, 1H), 11.09 (s, 1H).
So far I can quite happily use either fgets or fgetc to pass all text to a buffer as follows:
char* data;
data = malloc(sizeof(char) * 100);
int c;
int n = 0;
FILE* inptr = NULL;
inptr = fopen("NMR", "r");
if(NULL == fopen("NMR", "r"))
{
printf("Error: could not open file\n");
return 1;
}
for (c = fgetc(inptr); c != EOF && c != '\n'; c = fgetc(inptr))
{
data[n++] = c;
}
for (int i = 0, n = 100; i < n; i++)
{
printf ("%c", data[i]);
}
printf("\n");
and then print the buffer to the screen afterwards. However, I am only looking to pass part of the textfile to the buffer, namely:
13.69 (s, 1H),
So this means I want fgetc to stop after ','. However, this means the that the text will stop at 13.69 (s, and not 13.69 (s, 1H),
Is there a way around this? I have also experimented with fgets and then using strstr as follows:
char needle[4] = ")";
char* ret;
ret = strstr(data, needle);
printf("The substring is: %s\n", ret);
However, the output from this is:
), 11.09 (s, 1H)
thus giving me the rest of the string which I do not want. It's an interesting one and if anyone has any tips it would be much appreciated!
If you know that the closing parenthesis is the last character you want, you can use that as your stopping point in the fgetc() loop:
char data[100]; //No need to dynamically allocate if we know the size at compile time
int c;
int n = 0;
FILE* inptr = NULL;
inptr = fopen("NMR", "r");
if(inptr == NULL) //We want to check the value of the file we just opened
{ //and plan to use
printf("Error: could not open file\n");
return 1;
}
//We'll keep the original value guards (EOF and '\n') below and add two more
//to make sure we break from the loop
//We use n<98 below to make sure we can always create a null-terminated string,
//If we used 99, the 100th character might be a ')', then we have no room for a
//terminating null-char
for (c = fgetc(inptr); c != ')' && n < 98 && c != EOF && c != '\n'; c = fgetc(inptr))
{
data[n++] = c;
}
if(c != ')') //We hit EOF, \n, or ran out of space in data[]
{
printf("Error: no matching sequence found\n");
return 2;
}
data[n]=')'; //Could also write data[n]=c here, since we know it's a ')'
data[n+1]='\0'; //Add the terminating null character
printf("%s\n",data); //Since it's a properly formatted string, we can use %s
(Note that this example will handle null input characters differently from yours. If you expect null characters to be in the input stream (NMR file) then change the printf("%s",...) line back to the for loop you originally had.
Well with only one example of the format you are trying to parse it's not totally possible to give an answer, however if your input is always like this I would simply have a counter and break after the second comma.
int comma = 0;
for (c = fgetc(inptr); c != EOF && c != '\n' && c != ',' && comma < 1; c = fgetc(inptr))
{
if (data[n] = ',')
comma++;
data[n++] = c;
}
In case the characters inside the parenthesis can be more complex I would simply maintain a boolean state to know if I am actually inside or outside a parenthesis and break when I read a comma outside of it.
Simply read using fgets and store desired string in char * using sscanf-
char *new_data;
new_data=malloc(100); // allocate memory
...
fgets(data,100,inptr); // read from file but check its return
sscanf(data,"%[^)]",new_data); // store string untill ')' in new_data from data
strcat(new_data,")"); // concatenating new_data and ")"
printf("%s",new_data); // print new_data
...
free(new_data); // remember to free memory
Also you should check return of malloc though not done in my example and also close the file opened .

fscanf is somehow changing a node (in c)

I am new to c and have been stuck on this bug for hours. My code reads each word from a txt file and then stores the word in a node in a trie.
I have narrowed the problem down to the area marked off by asterisks:
at that point I have successfully added the first word to the trie and check that the correct node's value matched the word. Then, I use fscanf to save the next word in the file as the char 'add'. Then, printing out the exact same thing as before, the node's word should have remained the same. However, it has somehow been changed to the new word that was just read from the file.
How is this even possible??
int main(int argc, char** argv) {
trie_t* trie = malloc(sizeof(trie_t));
trie_init(trie);
int ret;
char* add = malloc(128);
FILE* file = fopen(argv[5], "r");
if (file == NULL) {
/* Failed to open the file for reading */
return 0;
}
while (1) {*********************
if (trie->head->children != NULL) {
printf("%s\n", trie->head->children->word);
}
ret = fscanf(file, "%s", add);
//printf("word = %s\n",toAdd);
if (trie->head->children != NULL) {
printf("%s\n", trie->head->children->word);
}****************************
if (ret == EOF) {
/* End of file */
ret = 1;
break;
} else if (ret <= 0) {
printf("fails");
/* Failed to read a word from the file */
break;
} else {
printf("gets here\n");
/* Succesfully read a word */
int x = trie_add(trie, add);
printf("%d\n",x);
}
}
You are only assigning memory once for add at:
char* add = malloc(128);
You need to assign memory for each word, that is, you need to move the malloc to your read cycle.
What the code is doing as you posted it is: allocate 128 bytes once, and then overwrite that memory space once and again every time you scanf().
Also, at char* add = malloc(128); you should assign it as char* add = malloc(128 * sizeof(char)); just to be clear and portable :)
I assume you are storing the pointer add in trie_t in trie_add function. In this case, since you are reusing the same memory location add for reading the next string, the content of the pointer pointed by add changes. Since you are just storing this pointer in trie the content of that node also changes because of this. To solve this, you need to allocate memory again using malloc just before the new string is read from file using fscanf.

input string through scanf

i wanted to ask is there way to input blank in string through scanf,
i am using this [^\n] to input what so ever excluding newline .is it correct ?but is creating lot of problem as it seems to be stored in the input buffer.what is the best way to input string .both gets and fgets are creating lot of problems,
while(strcmp(buf,"quit"))
{
scanf("%*[^\n]",buf);
n=send(connected,buf,strlen(buf),0);
if(n<0)
{
perror("send");
printf("error sending");
exit(1);
}
//printf("server has send\n");
n=recv(connected,buf,100,0);
if(n<0)
{
perror("recv");
printf("error recieving");
exit(1);
}
//printf("waiting to recieve something\n");
buf[n]='\0';
printf("client:%s\n",buf);
}
this is creating infinite loop the same thing is repeated again and again.
A far better way to read a line of input is
char line[128]; /* Or whatever. */
while(fgets(stdin, line, sizeof line) != NULL)
{
/* Filter out whitespace before checking tokens. */
}
If all you are having trouble with is empty lines, use strcmp("\n", buffer) == 0.
The regexp you posted won't work very well because C will translate the '\n' char in "%*[^\n]" to a literal newline. To have it work better you need to scape the slash: "%*[^\\n]".
However, it seems the trouble is also with the reading, i recommend you use a better function for that.
I have used the following code before to read sequential lines of arbitrary size from a file.
Couple of notes, though:
The returned buffer needs to be free()d after you are done with it
The code wastes a couple of bytes every iteration, but this is not really noticeable unless BUFFER_SIZE very small in comparision to the length of the lines.
The code, however, guarantees that one full line will be read from the FILE * and it will end in '\n'.
/*
* Initial size of the read buffer
*/
#define DEFAULT_BUFFER 1024
/*
* Standard boolean type definition
*/
typedef enum{ false = 0, true = 1 }bool;
/*
* Flags errors in pointer returning functions
*/
bool has_err = false;
/*
* Reads the next line of text from file and returns it.
* The line must be free()d afterwards.
*
* This function will segfault on binary data.
*/
char *readLine(FILE *file){
char *buffer = NULL;
char *tmp_buf = NULL;
bool line_read = false;
int iteration = 0;
int offset = 0;
if(file == NULL){
fprintf(stderr, "readLine: NULL file pointer passed!\n");
has_err = true;
return NULL;
}
while(!line_read){
if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
free(tmp_buf);
break;
}
if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
line_read = true;
offset = DEFAULT_BUFFER * (iteration + 1);
if((buffer = realloc(buffer, offset)) == NULL){
fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
free(tmp_buf);
has_err = true;
return NULL;
}
offset = DEFAULT_BUFFER * iteration - iteration;
if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
fprintf(stderr, "readLine: Cannot copy to buffer\n");
free(tmp_buf);
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
free(tmp_buf);
iteration++;
}
return buffer;
}
You can also use getline() if your development environment supports it
There are two problems with scanf("%*[^\n]",buf);:
The asterisk in the specifier causes value to be scanned, but not stored.
It scans up until the next newline character, but doesn't consume that character. That means every call after the first one will read nothing.
I think the scanf you're looking for is:
scanf("%[^\n]%*c", buf);
The %[^\n] gets your string, the %*c ignores the newline.
But, this is literally what you get if you google "how not to read a string in c"
Non-blocking input is something else entirely. You might google "cbreak mode." But, that's a unix-y terminal-y thing, and if you're on Windows, that probably doesn't make sense. X/Gtk/Qt will have other ways of doing that sort of thing.

Resources