2
/*
 * 1-20. Write a program detab that replaces tabs in the input with the proper number
 * of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns.
 * Should n be a variable or a symbolic parameter?
 *
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define  N 4

void detab(char **str);

int main(){
    char *line=NULL;
    char *newline;
    int len;
    while(getline(&line,&len,stdin)!=-1){
        detab(&line);
        printf("%s",line);
    }
    free(line);
    return 0;
}
void detab(char **str){
    int len=0,num=0;
    int i=0;
    char c;
    while((c=(*str)[i])!='\0'){
        if(c=='\t'){   // get the number of tab
            num++;
        }
        len++;          // get length of string
        i++;
    }
    char *newline;
    newline=(char *)malloc(len+(N-1)*num+1);   //use four blank replace one tab
    if(newline==NULL){
        fprintf(stderr,"can't malloc space\n");
    }
    i=0;
    int j=0;        //index of newline
    while((c=(*str)[i])!='\0'){
        if(c=='\t'){
            int k;
            for(k=0;k<N;k++){
                newline[j]=' ';
                ++j;
            }
        }
        else{
            newline[j]=c;
            ++j;
        }
        ++i;
    }
    newline[j]='\0';
    free(*str);
    *str=newline;
}

When I enter a short string, it works correctly, but if I enter a long string of maybe 50 characters, it says this:

*** Error in `./a.out': free(): invalid next size (fast): 0x0961b068 ***
Aborted (core dumped)

I have been stuck here for almost three hours. Please help me.

It works OK, if I try to use single pointer, like this:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define  N 4

char* detab(char *str);

int main(){
    char *line=NULL;
    char *newline;
    int len;
    while(getline(&line,&len,stdin)!=-1){
        newline = detab(line);
        printf("%s",newline);
        free(newline);
    }
    free(line);
    return 0;
}
char* detab(char *str){
    int len=0,num=0;
    int i=0;
    char c;
    while((c=str[i])!='\0'){
        if(c=='\t'){
            num++;
        }
        len++;
        i++;
    }
    char *newline;
    newline=(char *)malloc(len+(N-1)*num+1);   //use four blank replace one tab
    if(newline==NULL){
        fprintf(stderr,"can't malloc space\n");
    }
    i=0;
    int j=0;        //index of newline
    while((c=str[i])!='\0'){
        if(c=='\t'){
            int k;
            for(k=0;k<N;k++){
                newline[j]=' ';
                ++j;
            }
        }
        else{
            newline[j]=str[i];
            ++j;
        }
        ++i;
    }
    newline[j]='\0';
    return newline;
}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
wy-ei
  • 21
  • 1
  • 2
    [Don't cast the result of malloc (and friends)](http://stackoverflow.com/q/605845). Also, while it's nice you check for allocation failure, shouldn't you `abort()` or something, instead of just writing to `stderr`? – Deduplicator Jan 21 '15 at 11:56
  • `getline` expects a pointer to a buffer, not only a pointer. Check it out [here](http://crasseux.com/books/ctutorial/getline.html) – dmg Jan 21 '15 at 12:12
  • The only error that I can see is that the second argument to `getline()` should be `size_t *` instead of `int *`. Can you give an example of the long string that you used to cause the error? I wasn't able to trigger an error with either valgrind or manual inspection. – Bill Lynch Jan 23 '15 at 16:01
  • For example, I had no issues with this long and heavily tabbed test: https://gist.github.com/sharth/d58c7998c3113232ed34 – Bill Lynch Jan 23 '15 at 16:07
  • @dmg: You are allowed to pass a null pointer to `getline()`, which is what happens here. Quoting the [man page](http://man7.org/linux/man-pages/man3/getline.3.html): _If `*lineptr` is set to `NULL` and `*n` is set `0` before the call, then `getline()` will allocate a buffer for storing the line._ – Bill Lynch Jan 23 '15 at 16:08
  • @Deduplicator He is reading K&R. The crappy book is teaching out that incorrect practice, among many other blatantly incorrect or bad things. – Lundin Aug 25 '15 at 11:18

2 Answers2

1

I solved problem 1_20 by reading each input line into a doubly linked list. Each node in the list represents one character of the line in sequence.

After creating the linked list, I delete white space from the end of the list, i.e. from the end of the line.

Then I walk the linked list, scanning for tabs, keeping a column count as I go.

When I encounter a tab, I mark its column number and calculate how many spaces I need until the next tabstop. The formula is:

tabstop = ((col + (m-1)) / m) * m;

where:

  • tabstop is the next tabstop
  • m is the distance between tabstops
  • col is the column where the tab occurs

I replace the tab with a space and insert new nodes of single spaces in the linked list until I reach the next tabstop.

I continue walking the linked list from the column of the tabstop, searching for the next tab character and repeat the conversion process.

Once I reach the end of the linked list, I print it out as the output line.

Using a doubly linked list may seem tedious, but it greatly simplifies the logic of the main() function in the program.

The main() function says:

while (not end of file)
    {
    getline()
    remove_final_white_space()
    convert_tabs_to_spaces()
    putline()
    }

The getline() function creates the linked list with tab characters.

The putline() function flushes the linked list while it prints one character at a time.

Greenonline
  • 1,330
  • 8
  • 23
  • 31
knr_fan
  • 11
  • 1
0

okay first thing:

  • consider the scope of what you have read so far can only be used

  • you cannot use pointers and out of scope functions like malloc()

  • this ex1-20 is designed to use only what you have learned so far so don't complicate things.

ex1-20:

#include <stdio.h>
#define MAXLINE 1000

void getendtab(char s[],int lim,int n){

int c, i;
i=0;

while((c = getchar()) != EOF){
  if( c == '\t'){
   while( n != 0){
     s[i] = ' ';
     i++;
     n--;
   }
  n = 3;
  }else{s[i] = c; i++;}
}

}

int main(){

int n = 3;
int len;
char bytes[MAXLINE] = {0};
getendtab(bytes,MAXLINE,n);
printf("%s",bytes);

return 0;
}