#include <u.h>
#include <libc.h>
#include <bio.h>
#include "../common/common.h"
#include "tr2post.h"

int
isspace(Rune r)
{
       return(r==' ' || r=='\t' || r=='\n' || r == '\r' || r=='\f');
}

int
Bskipws(Biobufhdr *bp) {
       int r;
       char c[UTFmax];
       int sindex = 0;

       /* skip over initial white space */
       do {
               r = Bgetrune(bp);
               if (r == '\n') inputlineno++;
               sindex++;
       } while (r>=0 && isspace(r));
       if (r<0) {
               return(-1);
       } else if (!isspace(r)) {
               Bungetrune(bp);
               --sindex;
       }
       return(sindex);
}

int
asc2dig(char c, int base) {
       if (c >= '0' && c <= '9')
               if (base == 8 && c > '7') return(-1);
               else return(c - '0');

       if (base == 16)
               if (c >= 'a' && c <= 'f') return(10 + c - 'a');
               else if (c >= 'A' && c <= 'F') return(10 + c - 'A');

       return(-1);
}

/* get a string of type: "d" for decimal integer, "u" for unsigned,
* "s" for string", "c" for char,
* return the number of characters gotten for the field.  If nothing
* was gotten and the end of file was reached, a negative value
* from the Bgetrune is returned.
*/

int
Bgetfield(Biobufhdr *bp, int type, void *thing, int size) {
       int r;
       Rune R;
       char c[UTFmax];
       int sindex = 0, i, j, n = 0;
       int negate = 0;
       int base = 10;
       BOOLEAN bailout = FALSE;
       int dig;
       unsigned int u = 0;

       /* skip over initial white space */
       if (Bskipws(bp) < 0)
               return(-1);

       switch (type) {
       case 'd':
               while (!bailout && (r = Bgetrune(bp))>=0) {
                       switch (sindex++) {
                       case 0:
                               switch (r) {
                               case '-':
                                       negate = 1;
                                       continue;
                               case '+':
                                       continue;
                               case '0':
                                       base = 8;
                                       continue;
                               default:
                                       break;
                               }
                               break;
                       case 1:
                               if ((r == 'x' || r == 'X') && base == 8) {
                                       base = 16;
                                       continue;
                               }
                       }
                       if ((dig = asc2dig(r, base)) == -1) bailout = TRUE;
                       else n = dig + (n * base);
               }
               if (r < 0) return(-1);
               *(int *)thing = (negate)?-n:n;
               Bungetrune(bp);
               break;
       case 'u':
               while (!bailout && (r = Bgetrune(bp))>=0) {
                       switch (sindex++) {
                       case 0:
                               if (*c == '0') {
                                       base = 8;
                                       continue;
                               }
                               break;
                       case 1:
                               if ((r == 'x' || r == 'X') && base == 8) {
                                       base = 16;
                                       continue;
                               }
                       }
                       if ((dig = asc2dig(r, base)) == -1) bailout = TRUE;
                       else u = dig + (n * base);
               }
               *(int *)thing = u;
               if (r < 0) return(-1);
               Bungetrune(bp);
               break;
       case 's':
               j = 0;
               while ((size>j+UTFmax) && (r = Bgetrune(bp))>=0 && !isspace(r)) {
                       R = r;
                       i = runetochar(&(((char *)thing)[j]), &R);
                       j += i;
                       sindex++;
               }
               ((char *)thing)[j++] = '\0';
               if (r < 0) return(-1);
               Bungetrune(bp);
               break;
       case 'r':
               if ((r = Bgetrune(bp))>=0) {
                       *(Rune *)thing = r;
                       sindex++;
                       return(sindex);
               }
               if (r <= 0) return(-1);
               Bungetrune(bp);
               break;
       default:
               return(-2);
       }
       if (r < 0 && sindex == 0)
               return(r);
       else if (bailout && sindex == 1) {
               return(0);
       } else
               return(sindex);
}