Add a field-handling capability, so sorting may be done on fields within lines,each field sorted according to an independent set of options. (e.g -df for index category, -n for page numbers of a book.

#include <stdio.h>
#inclue <ctype.h>

#define   NUMERIC   1  /*numeric sort */
#define   DECR      2  /* sort in decreasing order */
#define   FOLD      4  /*fold upper and lower cases */
#define   DIR       8  /*directory order */
#define   LINES     100 /* max # of lines to be sorted */

int charcmp(char *, char *);
void error(char *);
int numcmp(char *, char *);
void readargs(int argc, char *argv[]);
int readlines(char *lineptr[], int maxlines);
void qsort(char *v[], int left, int right, int (*comp)(void*, void*));
void writelines(char *lineptr[], int nlines, int order);

char option = 0;   
int pos1 = 0;   /*field beginning with pos1 */
int pos2 = 0;   /*ending just before pos2 */

/* sort input lines */
int main(int argc , char *argv){
    char *lineptr[LINES];   /*pointers to text lines */
    int nlines;            /*number of input lines read */
    int rc =0;

    readargs(argc, argv);
    if((nlines = readlines(lineptr, LINES)) > 0){
       if(option & NUMERIC)
           qsort((void **) lineptr, 0, nlines -1,
                  (int (*)(void *, void*)) numcmp);
      else
          qsort((void **) lineptr, 0, nlines -1,
                (int (*)(void *, void *) charcmp);
     writelines(lineptr, nlines, option & DECR);
    }else{
       printf("input too big to sort \n");
       rc = -1;
    }
    return rc;
}
/*readargs: read program arguments   */
void readargs(int argc, char *argv[]){
    int c;
    int atoi(char *);

    while(--argc > 0 && (c =(*++argv)[0]) == '-' || c =='+'){
       if(c == '-' && !isdigit(*(argv[0] + 1)))
            while(c == *++argv[0])
                switch(c){
                case 'd':  /*directory order */
                     option |= DIR;
                     break;
                case 'f':  /*fold upper and lower */
                      option |= FOLD;
                      break;
               case 'n': /*numeric sort */
                    option |= NUMERIC;
                    break;
               case 'r':  /* sort in decr order */
                     option |= DECR;
                     break;
               default:
                     printf("sort: illegal option %c\n", c);
                     error("Usage; sort -dfnr [+pos1] [-pos2]");
                     break;
                }
        else if (c == '-')
             pos2 = atoi(argv[0] + 1));
       else if ((pos1 = atoi(argv[0] + 1)) < 0)
            error("Usage: sort -dfnr [+pos1] [-pos2]");
    }
     if (argc || pos1 > pos2)
         error("Usage: sort -dfnr [+pos1] [-pos2]");
}

/*numcmp.c */

#include <math.h>
#include <ctype.h>
#include <string.h>

#define   MASTR   100

void substr(char *s, char *t, int maxstr);
/*numcmp: compare s1 and s2 numerically  */
int numcmp(char *s1, char *s2){
    double v1, v2;
    char str[MAXSTR];

    substr(s1, str, MAXSTR);
    v1 = atof(str);
   substr(s2,str, MAXSTR);
   v2 = atof(str);
   if(v1 < v2)
     return -1;
  else if (v1 > v2)
     return 1;
  else
    return 0;
}

#define  FOLD  4   /*fold upper and lower cases */
#define  DIR   8   /*directory order */

/*charcmp: return <0 if s<t, 0 if s==t, >0 if s>t  */
int charcmp(char *s, char *t){
    char a, b;
    int i, j, option;
    extern char option;
    extern int pos1, pos2;
    int fold = (option & FOLD) ? 1 : 0;
    int dir =  (option & DIR) ?  1 : 0;

    i = j = pos1;
    if (pos2 > 0)
       endpos = pos2;
    else if((endpos = strlen(s)) > strlen(t))
      endpos =strlen(t);
   do{
      if(dir){
         while(i < endpos && !isalnum(s[i]) &&
             s[i] != ' ' && s[i] != '\0')
            i++;
        while(j < endpos && !isalnum(t[j]) &&
           t[j] != ' ' && t[j] != '\0')
           j++;
      }
      if(i <endpos & j < endpos){
         a = fold ? tolower(s[i]) : s[i];
         i++;
        b = fold ? tolower(t[j]) ; t[j];
        j++;
        if(a == b && a == '\0')
            return 0;
      }
   }while(a == b && i<endpos && j< endpos);
   return a - b;
}

/*substr.c */
#include <string.h>

void error(char *);

/*substr: get a substring of s and put in str */
void substr(char *s, char *str){
     int i, j, len;
     extern int pos1, pos2;

    len = strlen(s);
    if(pos2 > 0 && len > pos2)
       len = pos2;
    else if (pos2 > 0 && len < pos2)
     error("substr: string too short");
   for(j =0; i = pos1; i < len; i++, j++)
       str[j] = s[i];
   str[j] = '\0';
}

Add the -d(“directory order”) option, which makes comparisons only on letters, numbers, and blanks .Make sure it works in conjuction with -f.

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

#define   NUMERIC  1  /*numeric sort */
#define     DECR   2   /*sort in decreasing order */
#define     FOLD   4   /* fold upper and lower cases */
#define     DIR    8  /*directory order              */
#define     LINES   100 /* max # of lines to be sorted */

int charcmp(char *, char *);
int numcp(char *, char *);
int readlines(char *lineptr[], int maxlines);
void qsort(char *v[], int left, int right, int (*comp)(void*, void*));
void writelines(char *lineptr[], int nlines, int order);

static char option = 0;

/*sort input lines       */
int main(int argc, char *argv[]){
    char *lineptr[LINES];  /*pointers to text lines */
    int nlines;           /* number of input lines read */
    int c, rc = 0;

    while(--argc > 0 && (*++argv)[0] == '-')
        while(c == *++argv[0])
          switch(c){
            case 'd':   /* directory order */
                 option |= DIR;
                 break;
            case 'f':  /*fold upper and lower cases */
                option |= FOLD;
                break;
            case 'n':   /*numeric sort */
                 option |= NUMERIC;
                 break;
            case 'r':  /* sort in decreasing order */
                  option |= DECR;
                  break;
            default:
                  printf("sort: illegal option %c\n", c);
                  argc = 1;
                  rc = -1;
                  break;
           }
       if(argc)
           printf("Usage: sort -dfnr \n");
       else{
           if((nlines = readlines(lineptr, LINES)) > 0){
              if(option & NUMERIC)
                 qsort((void **) lineptr, 0, nlines -1,
                       (int (*)(void *, void *)) numcmp);
             else
                qsort((void **) lineptr, 0, nlines -1,
                      (int (*) (void *, void *)) charcmp);
             writelines(lineptr, nlines, option & DECR);
           }else{
               printf("input too big to sort \n");
               rc = -1;
           }
       }
       return rc;
}
/*charcmp: return <0 if s<t, 0 if s==t, >0 if s>t */
int charcmp(char *s, char *t){
    char a, b;
    int fold = (option & FOLD) ? 1 : 0;
    int dir =(option & DIR) ? 1 : 0;
    do{
       if(dir){
          while(!isalnum(*s) && *s != ' ' && *s != '\0') 
              s++;
         while(!isalnum(*t) && *t != ' ' && *t != '\0')
              t++;
       }
        a = fold ? tolower(*s) : *s;
        s++;
        b =fold ? tolower(*t) : *t;
        t++;
       if(a ==b && a == '\0')
           return 0;
    }while(a == b);
    return a - b;
}

Add the option -f to fold upper and lower case together, so that case distinctions are not made during sorting;for e.g, a and A compare equal.

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

#define         NUMERIC   1  /*numeric sort */
#define         DECR      2  /* sort in decreasing order */
#define         FOLD      4  /* fold upper and lower cases */
#define        LINES      100 /* max # of lines to be sorted */

int charcmp(char *, char *);
int numcmp(char *, char *);
int readlines(char *lineptr[], int maximum);
void qsort(char *v[], int left, int right, int (*comp)(void*, void*));
void writelines(char *lineptr[], int nlines, int order);

static char option = 0;

/*sort input lines */
int main(int argc, char *argv[]){
    char *lineptr[LINES];  /*pointers to text lines */
    int nlines;          /*number of input lines read */
    int c, rc = 0;

    while(--argc > 0 && (*++argv)[0] == '-'))
       while(c =*++argv[0])
          switch(c){
             case 'f':   /*fold upper and lower cases */
                  option |= FOLD;
                  break;
            case 'n':   /*numeric sort */
                 option |= NUMERIC;
                 break;
            case 'r':   /*sort in decreasing order */
                 option |= DECR;
                 break;
            default:
                printf("sort: illegal option %c\n", c);
                argc = 1;
                rc = -1;
                break;
             }
           if(argc)
               printf("Usage: sort -fnr \n");
           else{
               if((nlines = readlines(lineptr, LINES)) > 0){
                  if(option & NUMERIC)
                    qsort((void **) lineptr, 0, nlines - 1,
                           (int (*)(void *, void*)) numcmp);
                 else if (option & FOLD)
                   qsort((void **) lineptr, 0, nlines- 1,
                       (int (*)(void *, void *)) charcmp);
                else
                   qsort((void **) lineptr, 0, nlines -1,
                         (int (*)(void *, void *)) strcmp);
               }else{
                    printf(" input too big to sort \n");
                    rc = -1;
               }
           }
         return rc;
}
/* charcmp: return <0 if s <t , 0 if s==t, >0 if s >t */
int charcmp(char *s, char *t){
    for(;tolower(*s) == tolower(*t);s++, t++)
        if (*s == '\0')
             return 0;
    return tolower(*s) - tolower(*t);
}

Modify the sort program to handle a -r flag, which indicates sorting in reverse (decreasing) order.Be sure that -r works with -n.

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

#define  NUMERIC  1  /*numeric sort     */
#define  DECR     2  /* sort in decreasing order */
#define  LINES    100  /*max # of lines to be sorted */

int  numcmp(char *, char *);
int readlines(char *lineptr[], int maxlines);
void qsort(char *v[], int left, int right,
              int (*comp)(void *, void *));
void writelines(char *lineptr[], int nlines, int decr);

static char option = 0;

/*sort input lines  */
int main(int argc, char *argv[]){
    while(c ==*++argv[0])
      switch(c) {
        case 'n':  /*numeric sort */
              option |= NUMERIC;
              break;
       case 'r':   /* sort in decreasing order */
             option |= DECR;
             break;
      default:
            printf("sort: illegal option %c\n", c);
            argc = 1;
            rc = -1;
            break;
      }
      if(argc)
         printf(:Usage: sort -nr\n");
      else
        if(nlines = readlines(lineptr, LINES)) > 0){
           if(option & NUMERIC)
               qsort((void **) lineptr, 0, nlines- 1,
                     (int (*) (void *, void *)) numcmp);
           else
              qsort((void **) lineptr, 0, nlines -1,
                    (int (*) (void *, void *)) strcmp);
          writelines(lineptr, nlines, option & DECR)
        } else{
             printf("input too big to sort \n");
             rc = -1;
        }
       return rc;
}
/* writelines: write output lines     */
void writelines(char *lineptr[], int nlines, int decr){
     int i;

    if(decr)    /*print in decreasing order */
       for(i =nlines -1; i>= 0; i--)
           printf("%s\n", lineptr[i]);
    else
       for(i = 0; i <nlines; i++)
         printf("%s\n", lineptr[i]);
}

Write the program tail, which prints the last n lines of its input.By default, n is 10, let us say, but it can be changed by an optional argument, so that tail -n prints the last n lines.The program should behave rationally no matter how unreasonable the input or the value of n.Write the program so that it makes the best use of available storage;lines should be stored as in a sorting program, not in a 2d array of fixed size.

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

#define   DEFLINES   10   /*default # of lines to print */
#define   LINES      100  /* max # of lines to print */
#define   MAXLEN      100 /* max length of an input line */

void error(char *);
int getline(char *, int);

/*print last n lines of the input */
int main(int argc, char *argv[]){
    char *p;
    char *buf;     /*pointer to large buffer */
    char *bufend;    /* end of the buffer */
    char line[MAXLEN];  /*current input line */
    char *lineptr[LINES];   /*pointers to lines read */
    int first, i, last, len, n, nlines;

    if(argc == 1)  /*no argument present   */
       n = DEFLINES;   /* use default # of lines */
    else if (argc == 2 && (*++argv)[0] =='-')
      n = atoi(argv[0] + 1);
    else
       error(" usage: tail [-n]");
    if(n < 1 || n > LINES)   /*unreasoable value for n? */
          n =LINES;
    for(i =0; i <LINES;i++)
       lineptr[i] = NULL;
   if((p=buf=malloc(LINES * MAXLEN)) == NULL)
      error("tail: cannot allocate buf");
   bufend = buf + LINES *MAXLEN;
   last = 0;   /*index of last line read */
   nlines = 0;   /*number of lines read */
   while((len = getline(line, MAXLINE)) > 0){
       if(p + len + 1 >= bufend)
           p = buf;    /* buffer wrap around */
       lineptr[last] = p;
       strcpy(lineptr[last], line)
       if(++last > LINES)
           last = 0;  /* ptrs to buffer wrap around */
       p +=len + 1;
       nlines++;
   }
    if (n > nlines )   /* req. lines more than reqd */
        n = nlines;
    first = last - n;
    if(first < 0)   /* it did wrap around the list */
       first += LINES;
    for(i = first; n--> 0; i=(i + 1) % LINES)
        printf("%s", lineptr[i]);
    return 0;
}
/* error: print error message and exit */
void error(char *s){
     printf("%s\n", s);
     exit(1);
}

Extend entab and detab to accept the shorthand entab -m +n to mean tab stops every n columns, starting at column m.Choose convenient(for the user) default behavior.

#include <stdio.h>

#define   MAXLINE   100    /*maximum line size */
#define   TABINC     8    /*default tab increment size */
#define     YES      1   
#define     NO       0
void esettab(int argc,  char *argv[], char *tab);
void entab(char *tab);

/* replace strings of blanks with tabs */
int main(int argc, char *argv[]){
    char tab[MAXLINE + 1];

    esettab(argc, argv, tab);    /* initiliaze tab stops */
    entab(tab);                 /* replace blanks w/tab */
    return 0;
}
/*esettab.c */
#include<stdlib.h>
#define  MAXLINE   100   /* maximum line size */
#define  TABINC     8    /*default tab increment size */
#define   YES       1   
#define   NO        0
/* esettab: set tab stops in array tab */
void esettab(int argc, char *argv[], char *tab){
    int i, inc, pos;

   if(argc <= 1)    /* default tab stops */
     for(i =1; i <= MAXLINE;i++)
         if(i % TABINC == 0)
            tab[i] = YES;
         else
           tab[i] = NO;
   else if (argc == 3 && /*user provided range   */
              *argv[1] == '-' && *argv[2] == '+'){
       pos = atoi(&(*++argv)[1]);
       inc = atoi(&(*+=argv)[1]);
       for(i = 1; i <= MAXLINE;i++)
           if(i != pos)
                tab[i] = NO;
           else{
               tab[i] = YES;
               pos += inc;
           }
   }else{    /* user provided tab stops */
       for(i = 1; i <= MAXLINE;i++)
           tab[i] = NO;   /* turn off tab stops */
       while(--argc > 0) { /* walk through argument list */
             pos = atoi(*++argv);
             if(pos > 0 && pos <= MAXLINE)
                 tab[pos] = YES;
    }
}

Modify the programs entab and detab to accept a list of tab stops as arguments. Use the default tab settings if there are no arguments.

#include <stdio.h>

#define   MAXLINE  100 /*maximum line size */
#define   TABINC     8   /*default tab increment size */
#define   YES        1   
#define   NO         0

void settab(int argc, char *argv[], char *tab);
void entab(char *tab);
int tabpos(int pos, char *tab);

/*replace strings of blanks with tabs    */
int main(int argc, char *argv[]){
    char tab[MAXLINE + 1];

    settab(argc, argv, tab);    /* initializ tab stops */
    entab(tab);            /* replace blanks w/tab */
    return 0;
}

/* entab: replace strings of blanks with tabs and blanks */
 void entab(char *tab){
      int c, pos;
      int nb = 0;     /* # of blanks necessary  */
      int  nt = 0;   /* # of tabs necessary */
      for(pos = 1; (c =getchar()) != EOF; pos++){
         if(c == ' '){
           if(tabpos(pos, tab) == NO) 
                ++nb;        /* increment # of blanks */
         }else{
              nb = 0;   /* reset # of blanks */
              ++nt;     /* one more tab */
         }else{
             for(; nt > 0; nt--)
                  putchar('\t');   /* output tab(s)    */
             if(c == '\t')      /* forget the blank(s)  */
                 nb = 0;
             else              /* output blank(s) */
                for(; nb > 0 ;nb--)
                     putchar(' ');
            putchar(c);
           if(c == '\n')
              pos = 0;
           else if (c == '\t')
             while(tabpos(pos, tab) != YES)
                   ++pos;
         }
      }
 }   

#include <stdlib.h>
#define  MAXLINE   100     /*maximum line size */
#define  TABINC     8      /* default tab increment size */
#define   YES      1
#define   NO       0
/*settab: set tab stops  in array tab      */
void settab(int argc, char *argv[], char *tab){
     int i, pos;
     if (argc <= 1)   /*default tab stops   */
        for(i =1; i <= MAXLINE;i++)
           if(i %TABINC == 0)
              tab[i] = YES;
           else
              tab[i] = NO;
    else{     /*user provided tab stops */
       for(i =1;i <= MAXLINE;i++)
            tab[i] = NO;    /* turn off all tab stops */
      while(--argc > 0) {
          pos = atoi(*++argv);
          if(pos > 0 && pos <= MAXLINE)
               tab[pos] = YES;
      }
    }
} 
/* tabpos.c */
#define  MAXLINE   100     /* maximum line size */
#define  YES        1     
/* tabpos: determine if pos is at a tab stop  */
int tabpos(int pos, char *tab){
     if(pos > MAXLINE)
        return YES;
     else
       return tab[pos];
}
/*detab: replace tab with blanks */
void detab(char *tab){
     int c, pos = 1;
     while((c =getchar()) != EOF)
        if(c =='\t'){
          do
             putchar(' ');
         while(tabpos(pos++, tab) != YES);
        }else if (c == '\n'){
            putchar(c);
            pos = 1;
        }else{         /* all other characters */
            putchar(c);
            ++pos;
        }
}

Write the program expr, which evaluates a reverse Polish expression from the command line, where each operator or operand is a separate argument.For e.g expr 2 3 4 + * evaluates 2 *(3 + 4)

#include <stdio.h>
#include <math.h>   /* atof()   */

#define   MAXOP   100   /* max size of operand or operator */
#define   NUMBER   '0'   /*signal that a number was found */

int getop(char []);
void ungets(char []);
void push(double);
double pop(void);

/* reverse Polish calculator; uses command line */
int main(){
    char s[MAXOP];
    double op2;

    while(--argc > 0){
       ungets(" ");     /*push end of argument */
       ungets(*++argv);   /* push an argument */
       switch(getop(s)){
       case NUMBER:
             push(atof(s));
             break;
       case '+':
            push(pop() + pop());
            break;
       case '*':
            push(pop() * pop());
            break;
       case '-':
            op2 = pop();
            push(pop() - op2);
            break;
       case '\':
           op2 = pop();
           if (op2 != 0.0)
               push(pop() / op2);
           else
              printf("error : zero divisor\n");
           break;
      default:
          printf("error: unknown command %s\n", s);
          argc = 1;
          break;
                
       }
    }
     printf("\t %.8g\n",, pop());
     return 0;
}

Rewrite the routines day_of_year and month_day with pointers instead of indexing.

static char daytab[2][13] = {
     {0,31,28,31,30,31,30,31,31,30,31,30,31},
     {0,31,29,31,30,31,30,31,31,30,31,30,31}
};

/* day_of_year: set day of year from month & day */
int day_of_year(int year, int month, int day){
    int leap;
    char *p;

    leap = year % 4 == 0 && year%100 != 0 || year %400 == 0;
    p = daytab[leap];
    while(--month)
        day += *++p;
    return day;
}

/* month_day: set month, day from day of year   */
void month_day(int year, int yearday, int *pmonth, int *pday){
    int leap;
    char *p;

    leap = year %4 == 0 && year%100 != 0 || year%400 == 0;
    p =daytab[leap];
    while(yearday >*++p)
       yearday -= *p;
    *pmonth = p - *(daytab + leap);
    *pday = yearday;
}

There is no error checking in day_of_year or month_day.Remedy this defect.

static char daytab[2][13]= {
   {0,31,28,31,30,31,30,31,31,30,31,30,31},
   {0,31,29,31,30,31,30,31,31,30,31,30,31}
};

/*day_of_year: set day of year from month & day    */
int day_of_year(int year, int month, int day){
    int i, leap;

    leap =year %4 == 0 && year %100 != 0 || year%400 == 0);
    if(month < 1 || month > 12)
         return -1;
    if(day < 1 || day > daytab[leap][month])
       return -1;
    for(i =1; i < month; i++)
       day +=daytab[leap][i];
    return day;
}

/*month_day: set month, day from day of year    */
void month_day(int year, int yearday, int *pmonth, int *pday){
     int i, leap;

     if (year < 1){
        *pmonth = -1;
        *pday = -1;
        return ;
     }
     leap = year % 4 == 0 && year %100 != 0 || year %400 == 0;
     for(i = 1; i <=12 && yearday > daytab[leap][i];i++)
          yearday -=daytab[leap][i];
    if (i > 12 && yearday > daytab[leap][12]){
       *pmonth = -1;
       *pday = -1;
    }else{
       *pmonth = i;
       *pday = yearday;
    }
}