# Thread: Detab - K&R Exercise

1. ## Detab - K&R Exercise

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?

That is the exercise. Here is my attempt. I'm having some issues not printing the correct number of spaces. Can some take a look and see if they spot the issue? Thank you guys!

Code:
```#include <stdio.h>

int stringtointeger(char **str);  /* converts the argument to integer if capable */

int main(int argc,char *argv[]){
if(argc != 2)
printf("Usage: %s n [tabstop every n columns]\n",argv[0]);
else{
int tabstop,k,c;
if((tabstop = stringtointeger(argv))){
if(tabstop > 10)
printf("Tab stop must be less than 10\n");
else{
k = ++tabstop;
while((c = getchar()) != EOF){
if(c == '\t'){
for(k;k > 0;--k)
printf(" ");
}
else
printf("%c",c);
if(k > 0)
--k;
else
k = tabstop;
}
}
}
else
printf("Input Error. Make sure you are using an integer for argument\n");
}
return 0;
}
int stringtointeger(char **str){    /* returns int if argument is int, else returns 0 */
int i= 0,c,t = 0;

if((c = str[1][i]) < '0' || c > '9')
return 0;
else{
for(i = 0;((c = str[1][i]) != '\0' && c >= '0' && c <= '9');++i)
t = (t * 10) + (c - '0');
if(c != '\0')
return 0;
return t;
}
}```

2. What happens when a newline is encountered? Shouldn't you reset 'k' to 'tabstop' at that time?

Edit: Oh, and there's a function called atoi that will convert a string to an integer for you, so you don't have to write your own. There's also strtol, which is more flexible than atoi, but unless you need that flexibility, stick with atoi.

3. I do not know about the rest of it but I did notice this:

After else there is not a opening braket.

Code:
```    else
printf("Input Error. Make sure you are using an integer for argument\n");
}
return 0;```
But even that is just knit picking.

Can you print out the error if any? Or atleast the output of the program?

Another thing I noticed is:
Code:
```else{
int tabstop,k,c;}```
This seems to be the only time the variable tabstop is declared. I would be wrong but I believe once that "else" statement ends that variable disappears.

4. @jwroblewski44:
Code:
`k = ++tabstop;`
You realize this code changes the value of tabstop, which is not good. Also, you simply print that many spaces for each tab, instead of "moving to the next tabstop". You need to count how many characters you have printed on that line so far, in a different variable (initialize it to 0 every time you start a new line). If you encounter a tab, you need to keep printing space character until you reach the next multiple of your tabstop variable. That's how tabstops work, they align you at columns that are a multiple of that value.

Originally Posted by christop
There's also strtol, which is more flexible than atoi, but unless you need that flexibility, stick with atoi.
I usually prefer strol since it provides error checking. This is especially important since the input is from a user and not a "trusted source", so you must properly validate it.

Originally Posted by Layvian
After else there is not a opening braket.

Code:
```    else
printf("Input Error. Make sure you are using an integer for argument\n");
}
return 0;```
That is fine, brackets are not necessary since there is only one statment in that if. The closing bracket you quoted belongs to the else that starts on line 8.
Originally Posted by Layvian
Another thing I noticed is:
Code:
```else{
int tabstop,k,c;}```
This seems to be the only time the variable tabstop is declared. I would be wrong but I believe once that "else" statement ends that variable disappears.
You're right, it disappears that the end of that else statement (the one that starts on line 8), but it's not a problem since the OP doesn't try to use the variable after that point.

5. That is fine, brackets are not necessary since there is only one statment in that if. The closing bracket you quoted belongs to the else that starts on line 8.

You're right, it disappears that the end of that else statement (the one that starts on line 8), but it's not a problem since the OP doesn't try to use the variable after that point.
That would make sense, I had copied it to a online compiler to test it out. But now that you mentioned that it makes sense.

6. I ran this programme, still not sure what the problem is... It replaces tabs with spaces whith equivalent number of spaces specified by command line argument. is what I see... Is it not supposed to do this?

7. Originally Posted by Vespasian
I ran this programme, still not sure what the problem is... It replaces tabs with spaces whith equivalent number of spaces specified by command line argument. is what I see... Is it not supposed to do this?
Code:
```\$ ./tab 3
line 1 (2 tabs)
line 1 (2 tabs)
line 2 (also 2 tabs)
line 2 (also 2 tabs)
line 3 (also 2 tabs)
line 3 (also 2 tabs)
\$ ./tab 3
a       line 1 (tab in the middle)
a   line 1 (tab in the middle)
a       line 2 (also 1 tab in the middle)
a line 2 (also 1 tab in the middle)
a       line 3 (also 1 tab in the middle)
a    line 3 (also 1 tab in the middle)```

It does not line them up consistently (note line 1 is over-indented by 1 space in the first example). This is due to the increment issue i mentioned, and the subsequent manipulation of k, which is a bit awkward. Also, it's just way out of wack when you have tabs in the middle of a line, which is valid input, but processed incorrectly. It's not a straight "each tab char is n spaces", tab takes you to the next column that is a multiple of n.

8. thanks for the help guys. heres my new code.
Code:
```/* detab */
/* john wroblewski */
/* jwroblewski44[at]yahoo[dot]com */
/*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>

int stringtointeger(char **str);  /* converts the argument to integer if capable */

int main(int argc,char *argv[]){
if(argc != 2)
printf("Usage: %s n [tabstop every n columns]\n",argv[0]);  //if only one argument
else{
int tabstop,c;
if((tabstop = stringtointeger(argv))){
if(tabstop > 10)
printf("Tab stop must be less than 10\n");
else{
int k = tabstop;
while((c = getchar()) != EOF){
if(c != '\t'){
printf("%c",c);
if(k > 0)
--k;
else if(k == 0)
k = tabstop;
if(c == '\n')
k = tabstop;
}
else{
while(k > 0){
printf(" ");
--k;
}
k = tabstop;
}
}
}
}
}
return 0;
}
int stringtointeger(char **str){    /* returns int if argument is int, else returns 0 */
int i= 0,c,t = 0;

if((c = str[1][i]) < '0' || c > '9')
return 0;
else{
for(i = 0;((c = str[1][i]) != '\0' && c >= '0' && c <= '9');++i)
t = (t * 10) + (c - '0');
if(c != '\0')
return 0;
return t;
}
}```