Bug in utoi(), which made it ignore a leading minus sign when
[sgt/halibut] / input.c
diff --git a/input.c b/input.c
index 365fec0..1410022 100644 (file)
--- a/input.c
+++ b/input.c
@@ -103,6 +103,8 @@ static int get(input *in, filepos *pos, rdstringc *rsc) {
     }
     else if (in->stack) {
        wchar_t c = in->stack->text[in->stack->ptr];
+        if (pos)
+            *pos = in->stack->pos;
        if (in->stack->text[++in->stack->ptr] == L'\0') {
            macrostack *tmp = in->stack;
            in->stack = tmp->next;
@@ -117,7 +119,8 @@ static int get(input *in, filepos *pos, rdstringc *rsc) {
            int c = getc(in->currfp);
 
            if (c == EOF) {
-               fclose(in->currfp);
+               if (in->wantclose)
+                   fclose(in->currfp);
                in->currfp = NULL;
                return EOF;
            }
@@ -424,6 +427,7 @@ token get_token(input *in) {
            c == '#' || c == '{' || c == '}' || c == '.') {
            /* single-char command */
            rdadd(&rs, c);
+           prevpos = rsc.pos;
        } else if (c == 'u') {
            int len = 0;
            do {
@@ -589,11 +593,11 @@ static paragraph *addpara(paragraph newpara, paragraph ***hptrptr) {
 /*
  * Reads a single file (ie until get() returns EOF)
  */
-static void read_file(paragraph ***ret, input *in, indexdata *idx) {
+static void read_file(paragraph ***ret, input *in, indexdata *idx,
+                     tree234 *macros) {
     token t;
     paragraph par;
     word wd, **whptr, **idximplicit;
-    tree234 *macros;
     wchar_t utext[2], *wdtext;
     int style, spcstyle;
     int already;
@@ -607,7 +611,7 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
            stack_style = 2,           /* \e, \c, \cw */
            stack_idx = 4,             /* \I, \i, \ii */
            stack_hyper = 8,           /* \W */
-           stack_quote = 16,          /* \q */
+           stack_quote = 16           /* \q */
        } type;
        word **whptr;                  /* to restore from \u alternatives */
        word **idximplicit;            /* to restore from \u alternatives */
@@ -629,7 +633,6 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
 
     t.text = NULL;
     t.origtext = NULL;
-    macros = newtree234(macrocmp);
     already = FALSE;
 
     crossparastk = stk_new();
@@ -806,6 +809,11 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
            continue;
        }
 
+       while (t.type == tok_cmd &&
+              macrolookup(macros, in, t.text, &t.pos)) {
+           dtor(t), t = get_token(in);
+       }
+
        /*
         * This token begins a paragraph. See if it's one of the
         * special commands that define a paragraph type.
@@ -828,8 +836,10 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
                needkw = 4;
                break;
              case c__comment:
-               if (isbrace(in))
+               if (isbrace(in)) {
+                   needkw = -1;
                    break;             /* `\#{': isn't a comment para */
+               }
                do {
                    dtor(t), t = get_token(in);
                } while (t.type != tok_eop && t.type != tok_eof);
@@ -896,7 +906,21 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
                /* Get keywords. */
                dtor(t), t = get_token(in);
                fp = t.pos;
-               while (t.type == tok_lbrace) {
+               while (t.type == tok_lbrace ||
+                      (t.type == tok_white && (needkw & 24))) {
+                   /*
+                    * In paragraph types which can't accept any
+                    * body text (such as \cfg), we are lenient
+                    * about whitespace between keywords. This is
+                    * important for \cfg in particular since it
+                    * can often have many keywords which are long
+                    * pieces of text, so it's useful to permit the
+                    * user to wrap the line between them.
+                    */
+                   if (t.type == tok_white) {
+                       dtor(t), t = get_token(in); /* eat the space */
+                       continue;
+                   }
                    /* This is a keyword. */
                    nkeys++;
                    /* FIXME: there will be bugs if anyone specifies an
@@ -954,7 +978,8 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
                            rdadd(&macrotext, L'\n');
                        rdadds(&macrotext, t.text);
                        dtor(t), t = get_token(in);
-                       if (t.type == tok_eop) break;
+                       if (t.type == tok_eop || t.type == tok_eof)
+                            break;
                    }
                    macrodef(macros, rs.text, macrotext.text, fp);
                    continue;          /* next paragraph */
@@ -1522,7 +1547,17 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
        }
        stk_free(parsestk);
        prev_para_type = par.type;
-       addpara(par, ret);
+       /*
+        * Before we add the paragraph to the output list, we
+        * should check that there was any text in it at all; there
+        * might not be if (for example) the paragraph contained
+        * nothing but an unrecognised command sequence, and if we
+        * put an empty paragraph on the list it may confuse the
+        * back ends later on.
+        */
+       if (par.words) {
+           addpara(par, ret);
+       }
        if (t.type == tok_eof)
            already = TRUE;
     }
@@ -1540,27 +1575,89 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
      * this cleanup doesn't happen.
      */
     dtor(t);
-    macrocleanup(macros);
 
     stk_free(crossparastk);
 }
 
+struct {
+    char const *magic;
+    size_t nmagic;
+    int binary;
+    void (*reader)(input *);
+} magics[] = {
+    { "%!FontType1-",     12, FALSE, &read_pfa_file },
+    { "%!PS-AdobeFont-",  15, FALSE, &read_pfa_file },
+    { "\x80\x01",          2, TRUE,  &read_pfb_file },
+    { "StartFontMetrics", 16, FALSE, &read_afm_file },
+    { "\x00\x01\x00\x00",  4, TRUE,  &read_sfnt_file },
+    { "true",             4, TRUE,  &read_sfnt_file },
+};
+
 paragraph *read_input(input *in, indexdata *idx) {
     paragraph *head = NULL;
     paragraph **hptr = &head;
+    tree234 *macros;
+    char mag[16];
+    size_t len, i;
+    int binary;
+    void (*reader)(input *);
+
+    macros = newtree234(macrocmp);
 
     while (in->currindex < in->nfiles) {
-       in->currfp = fopen(in->filenames[in->currindex], "r");
+       setpos(in, in->filenames[in->currindex]);
+       in->charset = in->defcharset;
+       in->csstate = charset_init_state;
+       in->wcpos = in->nwc = 0;
+       in->pushback_chars = NULL;
+
+       if (!in->filenames[in->currindex]) {
+           in->currfp = stdin;
+           in->wantclose = FALSE;     /* don't fclose stdin */
+           /*
+            * When reading standard input, we always expect to see
+            * an actual Halibut file and not any of the unusual
+            * input types like fonts.
+            */
+           reader = NULL;
+       } else {
+           /*
+            * Open the file in binary mode to look for magic
+            * numbers. We'll switch to text mode if we find we're
+            * looking at a text file type.
+            */
+           in->currfp = fopen(in->filenames[in->currindex], "rb");
+           binary = FALSE; /* default to Halibut source, which is text */
+           if (in->currfp) {
+               in->wantclose = TRUE;
+               reader = NULL;
+               len = fread(mag, 1, sizeof(mag), in->currfp);
+               for (i = 0; i < lenof(magics); i++) {
+                   if (len >= magics[i].nmagic &&
+                       memcmp(mag, magics[i].magic, magics[i].nmagic) == 0) {
+                       reader = magics[i].reader;
+                       binary = magics[i].binary;
+                       break;
+                   }
+               }
+               rewind(in->currfp);
+           }
+           if (!binary) {
+               fclose(in->currfp);
+               in->currfp = fopen(in->filenames[in->currindex], "r");
+           }
+       }
        if (in->currfp) {
-           setpos(in, in->filenames[in->currindex]);
-           in->charset = in->defcharset;
-           in->csstate = charset_init_state;
-           in->wcpos = in->nwc = 0;
-           in->pushback_chars = NULL;
-           read_file(&hptr, in, idx);
+           if (reader == NULL) {
+               read_file(&hptr, in, idx, macros);
+           } else {
+               (*reader)(in);
+           }
        }
        in->currindex++;
     }
 
+    macrocleanup(macros);
+
     return head;
 }