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

#define MAXLINE 1000
#define MAXTABS 20
#define COLSTART 1
#define TABINTERVAL 2
#define TABARRAY 3
#define TOOMANY 4
#define ON 1
#define OFF 0

/* detab syntax
 detab -[start column] +[tab interval]
 example: detab -2 +8
 detab -t [list of tab stops]
 example: detab -t 8 16 24 31 60 62 76

detab replaces tabs with spaces. Unless otherwise specified detab uses the value of default_interval as the tab interval. 

*/

int testop(char *);

int default_interval = 4, user_interval = OFF, colstart = OFF, tab_array = OFF, tabstops[MAXTABS];

int main(int argc, char *argv[])
{
 int argi, c, col, coltemp, good = OFF, i, interval, nspaces, ntabs = 0, ntabs_found, len, result = 0, *t1 = tabstops, *t2, tmp, tmp2;
 char line[MAXLINE], templine[MAXLINE], *lineptr = line, *p1, *p2, *q1, *q2;
 size_t maxline = MAXLINE;
 char *p, **pp = argv;

 for (argi = 1; argi < argc; argi++) {
 c = testop(*++pp);
 switch (c) {
 case COLSTART:
 colstart = (-1 * atoi(*pp)); /* compensate for hyphen, make positive */
 break;
 case TABINTERVAL:
 user_interval = atoi(*pp);
 break;
 case TABARRAY:
 good = (isdigit(**pp) && tab_array) ? ON : OFF; /* it's a number, add to array */
 if (good) {
 *t1++ = atoi(*pp); /* convert string to int */
 *t1 = -1; /* terminate int array */
 ++ntabs; /* count number of tabs in array */
 }
 break;
 case TOOMANY:
 printf("detab: too many arguments\n");
 argi += argc;
 result = -1;
 break;
 default:
 printf("detab: illegal option %s\n", *pp);
 argi += argc;
 result = -1;
 break;
 }
 }
 if (argi != argc) { /* error message */
 printf("%s\n%3s%s\n", "Usage: detab -[start column] +[tab interval]", "", "ex. detab -4 +8");
 printf("%7s%s\n%3s%s\n", "", "detab -t [list of tab stops]", "", "ex. detab -t 4 8 22");
 }
 else
 while ((len = getline(&lineptr, &maxline, stdin)) > 0) { /* get a line */
 ntabs_found = 0;
 for (i=0; i < (strlen(line)); i++) { /* substitute tabs with spaces */
 col = 1, nspaces = 0, p1 = line, q1 = templine, q2 = templine, t2 = tabstops;
 for (; (*p1 != '\t') && (*p1 != '\0'); col++, p1++)
 ;
 good = (*p1 == '\t') ? ON : OFF;
 if (good) { 
 p2 = p1++, ++ntabs_found; /* record position of tab, increment tab count */
 while ((*q1++ = *p1++) != '\n') /* copy everything past tab to templine */
 ;
 *--q1 = '\0'; /* delete '\n', terminate templine */
 /*** start todo ***/
 if (tab_array) { /* tab array */
 if (ntabs_found > ntabs)
 col = *t2; /* out of tab stops */
 else {
 t2 += (ntabs_found - 1); /* t2 index must correspond to number of tabs found */
 if (col == *t2) /* tab found on tab stop */
 ++t2;
 else if (col > *t2) {
 for (; col > *t2; t2++) /* find nearest tabstop */
 ;
 if (col == *t2) /* tab found on a tab stop */
 ++t2;
 }
 }
 }
 /*** end todo ***/
 coltemp = col; /* record column */
 interval = (user_interval) ? user_interval : default_interval;
 if (!tab_array && colstart) { /* start column */
 if (col < colstart) /* tab found before start column */
 col = colstart;
 else if (col == colstart) /* tab found on the start column */
 col += interval; /* go past it */
 else {
 tmp = colstart, tmp2 = interval;
 while (col > (tmp + tmp2)) /* find next tab stop */
 tmp2 += tmp2;
 col = (col == tmp + tmp2) ? col += interval : tmp + tmp2; /* if tab found on tab stop, go past it */
 }
 }
 if (!tab_array && !colstart && !(col % (interval +1))) /* tab was found on a tab stop */
 ++col; /* go past it */
 while (!tab_array && !colstart && (col++ % interval)) /* find nearest tab stop */
 ;
 nspaces = (tab_array) ? *t2 - col : col - coltemp; /* number of spaces to insert */
 while (nspaces-- > 0) /* insert spaces */
 *p2++ = '*';
 while (*q2 != '\0') /* concatenate templine back to line */
 *p2++ = *q2++;
 *p2 = '\0'; /* terminate line */
 }
 }
 printf("%s\n", line);
 result = (good) ? 0 : -1;
 }
 return result;
}
 
int testop(char *p) /* test arguments are valid */
{
 int c, good = OFF, result = 0, *t = tabstops;

 if (*p == '-') { /* may be a start column or a tab stop flag */
 good = (!tab_array && !colstart) ? ON : OFF;
 if (good && isdigit(*++p) && *p != '0') { /* can't start with zero */
 while ((c = isdigit(*p++))) /* check contains only digits */
 ;
 if (c == '\0') { /* it's a number */
 result = COLSTART;
 colstart = ON;
 }
 }
 else if (good && *p == 't') {
 result = TABARRAY;
 tab_array = ON;
 }
 else result = (!good) ? TOOMANY : -1;
 }
 else if (*p == '+') { /* may be a tab interval */
 good = (!tab_array && !user_interval) ? ON : OFF;
 if (good && isdigit(*++p) && *p != '0') { /* column interval can't start with zero */
 while ((c = isdigit(*p++))) /* check contains only digits */
 ;
 result = (c == '\0') ? TABINTERVAL : -1;
 user_interval = ON;
 }
 else result = (!good) ? TOOMANY : -1;
 }
 else if (isdigit(*p) && tab_array) { /* may be a tab stop */
 good = (*p != '0') ? ON : OFF; /* can't start with zero */
 while (good && isdigit(c = *p++)) /* check contains only digits */
 ;
 good = (c == '\0') ? ON : OFF;
 result = (good) ? TABARRAY : -1; 
 }
 else result = -1; /* invalid argument */

 return result;
}