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

#include <ctype.h>

#include <exec/types.h>

#include <libraries/dosextens.h>

#include <dlg/user.h>

#include <proto/dos.h>
#include <proto/dlg.h>

#include <pragmas/dlg.h>

struct MyString {char *str;
                 long  pos;
                 BPTR  fh;
                };

typedef void __regargs (*PFI)(char, void *);

char __regargs *fmtcvt(char *ap, SHORT base, char *cp, SHORT len)

{long        val;
 static char digits[] = "0123456789abcdef";

 if (len == sizeof(long))
     val = *(long *)ap;
   else
     if (base > 0)
         val = *(unsigned short *)ap;
       else
         val = *(signed short *)ap;

 len = 0;
 if (base < 0)
    {base = -base;
     if ((long)val < 0)
        {val = -val;
         len = 1;
        }
    }

 do {*--cp = digits[(SHORT)(val%base)];
    }  while((val /= base) != 0);

 if (len)  *--cp = '-';

 return(cp);
}


LONG __saveds __asm LIBAFormat(register __a0 struct USER_DATA *User,
                               register __a1 void             *data,
                               register __a2 PFI               aputsub,
                               register __d0 char             *afmt,
                               register __a3 char             *argp)

{char *fmt       = afmt;
 PFI   putsub    = aputsub;
 LONG  charcount =   0;

 union {SHORT *ip;
        char  *cp;
        char **cpp;
       } args; 

 SHORT ansitype;
 SHORT rj;
 SHORT maxwidth;
 SHORT width;
 SHORT i, k;

 char *cp;
 char  fillc;
 char  c;

 char  s[200];

 args.cp = argp;

 while ((c = *fmt++))
       {if (c != '%')
           {(*putsub)(c,data);
            ++charcount;
            continue;
           }

        s[14]    =  0;
        rj       =  1;
        fillc    = ' ';
        maxwidth = 10000;

        if ((c = *fmt++) == '-')
           {rj = 0;
            c  = *fmt++;
           }

        if (c == '0')
           {fillc = '0';
            c     = *fmt++;
           }

        if (c == '*') 
           {width    = *(long *)args.cp;
            args.cp +=   4;
            c        = *fmt++;
           }
         else
           {for(width = 0; c>='0'  &&  c<='9'; c = *fmt++)
                width = width*10 + c - '0';
           }

        if (c == '.')
           {if ((c = *fmt++) == '*')
               {maxwidth = *(long *)args.cp;
                args.cp +=   4;
                c        = *fmt++;
               }
             else
               {for(maxwidth = 0; c>='0'  &&  c<='9'; c = *fmt++)
                    maxwidth = maxwidth*10 + c - '0';
               }
           }

        i = sizeof(LONG);
        if (c == 'l')
            c = *fmt++;
          else
            if (c == 'h')
               {i = sizeof(SHORT);
                c = *fmt++;
               }

        switch(c)
              {case 'o':  k = 8;
                          goto do_conversion;

               case 'u':  k = 10;
                          goto do_conversion;

               case 'x':  k = 16;
                          goto do_conversion;

               case 'd':  k = -10;

          do_conversion:  cp       = fmtcvt(args.cp, k, s+14, i);
                          args.cp += i;
                          break;

               case 's':  i = strlen(cp = *args.cpp++);
                          goto havelen;

               case 'b':
               case 'a':  if (!User)
                             {*(cp = s+13) = c;
                                break;
                             }

                          ansitype = *fmt++;
                          if (!(User->Ansi_Flag & ANSI_COLOR))
                             {i  =  0;
                              cp = "";
                              goto havelen;
                             }

                          if (c == 'a')
                             {if (ansitype>='0'  &&  ansitype<='7')
                                  i = 5;
                                else
                                  i = 6;

                              switch(ansitype)
                                    {case '0':  cp = "\033[30m";
                                                break;

                                     case '1':  cp = "\033[31m";
                                                break;

                                     case '2':  cp = "\033[32m";
                                                break;

                                     case '3':  cp = "\033[33m";
                                                break;

                                     case '4':  cp = "\033[34m";
                                                break;

                                     case '5':  cp = "\033[35m";
                                                break;

                                     case '6':  cp = "\033[36m";
                                                break;

                                     case '7':  cp = "\033[37m";
                                                break;

                                     case 'b':  cp = "\033[1m";
                                                break;

                                     case 'i':  cp = "\033[3m";
                                                break;

                                     case 'u':  cp = "\033[4m";
                                                break;

                                     case 'r':  cp = "\033[7m";
                                                break;

                                     case 'f':  cp = "\033[5m";
                                                break;

                                     case 'o':  cp = "\033[0m";
                                                break;
                                    }
                             }
                           else
                             {i = 5;

                              switch(ansitype)
                                    {case '0':  cp = "\033[40m";
                                                break;

                                     case '1':  cp = "\033[41m";
                                                break;

                                     case '2':  cp = "\033[42m";
                                                break;

                                     case '3':  cp = "\033[43m";
                                                break;

                                     case '4':  cp = "\033[44m";
                                                break;

                                     case '5':  cp = "\033[45m";
                                                break;

                                     case '6':  cp = "\033[46m";
                                                break;

                                     case '7':  cp = "\033[47m";
                                                break;
                                    }
                             }
                          goto havelen;

               case 'c':  if (i==sizeof(long))
                             {c        = *(long *)args.cp;
                              args.cp +=   i;
                             }
                           else
                             c = *args.ip++;

               default:   *(cp = s+13) = c;
                          break;
              }

        i = (s+14) - cp;
        havelen: if (i > maxwidth)  i = maxwidth;

                 if (rj)
                    {if ((*cp == '-' || *cp == '+') && fillc == '0')
                        {--width;
                         (*putsub)(*cp++,data);
                        }

                     for(; width-- > i ; ++charcount)
                        (*putsub)(fillc,data);
                    }

                 for(k = 0; *cp && k < maxwidth; ++k )
                    (*putsub)(*cp++,data);

                 charcount += k;

                 if (!rj)
                    {for(; width-- > i ; ++charcount)
                        (*putsub)(' ',data);
                    }
       }

 return(charcount);
}


void __regargs PutInStr(char c, struct MyString *ms)

{ms->str[ms->pos] = c;
 ms->pos++;

 return;
}


void __regargs PutInFile(char c, struct MyString *ms)

{if (c)
    {ms->str[ms->pos] = c;
     ms->pos++;

     if (ms->pos == 256)
        {Write(ms->fh, ms->str, 256);
         ms->pos = 0;
        }
    }

 return;
}


LONG __saveds __asm LIBXASPrintf(register __a0 struct USER_DATA *User,
                                 register __a1 char             *buf,
                                 register __a2 char             *fmt,
                                 register __a3 void             *args)

{LONG            charcount;
 struct MyString ms;

 ms.str = buf;
 ms.pos = 0;
 ms.fh  = 0;

 charcount   = LIBAFormat(User,&ms,PutInStr,fmt,args);
 buf[ms.pos] = 0;

 return(charcount);
}


LONG __saveds __asm LIBXAFPrintf(register __a0 struct USER_DATA *User,
                                 register __a1 BPTR              fh,
                                 register __a2 char             *fmt,
                                 register __a3 void             *args)

{LONG            charcount;
 char            buf[256];
 struct MyString ms;

 ms.str = buf;
 ms.pos = 0;
 ms.fh  = fh;

 charcount = LIBAFormat(User,&ms,PutInFile,fmt,args);
 if (ms.pos)  Write(fh, buf, ms.pos);

 return(charcount);
}


LONG __stdargs ASPrintf(struct USER_DATA *User,char *buf,char *fmt,...)

{LONG            charcount;
 struct MyString ms;

 ms.str = buf;
 ms.pos = 0;
 ms.fh  = 0;

 charcount   = LIBAFormat(User,&ms,PutInStr,fmt,(char *)(&fmt+1));
 buf[ms.pos] = 0;

 return(charcount);
}


LONG __stdargs AFPrintf(struct USER_DATA *User,BPTR fh,char *fmt,...)

{LONG            charcount;
 char            buf[256];
 struct MyString ms;
 
 ms.str = buf;
 ms.pos = 0;
 ms.fh  = fh;

 charcount = LIBAFormat(User,&ms,PutInFile,fmt,(char *)(&fmt+1));
 if (ms.pos)  Write(fh, buf, ms.pos);

 return(charcount);
}
