comments updated
[henge/apc.git] / src / scanner.c
index 36a2050..0b733c8 100644 (file)
   ----------------------------------------------------------------------------*/
 /* Standard */
 #include <stdio.h>  //print
-#include <string.h> //strncmp
 #include <errno.h>  //errno
-#include <ctype.h>  //tolower
 /* Posix */
 #include <err.h>    //warnx
 #include <stdlib.h> //exit
 #include <unistd.h> //chdir
 #include <dirent.h> //opendir
 #include <unistr.h> //unicode strings
+#include <string.h> //strlen
 /* Internal */
 #include "parser.tab.h"
 /* Public */
-int   scanner_init(void);
 void  scanner_quit(void);
-int   scanner(void);
-int   scanner_scanpixels(int*,int);
+int   scanner_scandir(DIR*);
 /* Private */
-extern //lexer.c
-int   lexer_lexstring(const uint8_t*);
-extern //lexer.c
-void  lexer_pushtok(int, int);
 static
-int   dredge_current_depth(void);
-/* Mem */
-extern //lexer.c
-struct dirent* lexer_direntpa[], **lexer_direntpp;
-extern //SRC_DIR/bin/tools/apc.c
-const char* cargs['Z'];
-#ifndef DL_STACKSIZE
-#define DL_STACKSIZE     64
-#endif
-#ifndef DL_CD_STACKSIZE
-#define DL_CD_STACKSIZE  DL_STACKSIZE //square tree
-#endif
-static
-struct dirlist
-{ DIR*           dirp;
-  struct dirent* child_directory_stack[DL_CD_STACKSIZE],** cds;
-} directory_list_stack[DL_STACKSIZE + 1],* dls; //+1 for the root dir
-static
-FILE* current_open_file = NULL;
-
-/* Directory Listing Stack
-   FILO Stack for keeping an open DIR* at each directory depth for treewalk.
-   This stack is depth-safe, checking its depth during push operations, but not
-   during pop operations, to ensure the thread doesn't open too many files at
-   once (512 in c runtime), or traverse too far through symbolic links.
-   A directory listing includes a DIR* and all DIR-typed entity in the directory
-   as recognized by dirent, populated externally (and optionally).
-   This stack behaves abnormally by incrementing its PUSH operation prior to
-   evaluation, and the POP operations after evaluation.  This behavior allows
-   the 'DL_CURDEPTH' operation to map to the current element in the 'dl_stack'
-   array, and it is always treated as the "current depth". This also allows us
-   to init the root directory to 'directory_list_stack'[0] and pop it in a safe
-   and explicit manner.
-*/
-#define DL_STACK        (directory_list_stack)
-#define DL_STACKP       (dls)
-#define DL_CD_STACK     ((*DL_STACKP).child_directory_stack)
-#define DL_CD_STACKP    ((*DL_STACKP).cds)
-#define DL_CURDIR()     ((*DL_STACKP).dirp)
-#define DL_LEN()        (DL_STACKP - DL_STACK)
-#define DL_CD_LEN()     (DL_CD_STACKP - DL_CD_STACK)
-#define DL_INIT()       (DL_STACKP = DL_STACK)
-#define DL_CD_INIT()    (DL_CD_STACKP = DL_CD_STACK)
-#define DL_POP()        ((*DL_STACKP--).dirp)
-#define DL_CD()         (*DL_CD_STACKP)
-#define DL_CD_CURNAME() (DL_CD()->d_name)
-#define DL_CD_POP()     (*--DL_CD_STACKP)
-#define DL_PUSH(D)      ((*++DL_STACKP).dirp = D)
-#define DL_CD_PUSH(E)   (*DL_CD_STACKP++ = E)
-
-
-/* Initializer
-   Initializer expects a function pointer to its lexical analysis function.
-   Sets up stack pointers and returns boolean true if 'opendir' encounters an
-   error, or if dredge_current_depth returns boolean true.
-*/
-int scanner_init
-#define CWDSTR  "./"
-#define ROOTDIR (cargs['d'] ? cargs['d'] : CWDSTR)
-()
-{ DL_INIT();
-  DL_STACK[0].dirp = opendir(ROOTDIR);
-  if (current_open_file != NULL)
-    { fclose(current_open_file);
-      current_open_file = NULL;
-    }
-  printf("Root dir %s\n",ROOTDIR);
-  return !chdir(ROOTDIR) && (DL_STACK[0].dirp == NULL || dredge_current_depth() == -1);
-}
-
-/* Quit */
-void scanner_quit
-()
-{ if (DL_CURDIR())
-    closedir(DL_CURDIR());
-}
-
-/* Scanner
-   The main driver of the scanner will advance the current treewalk state and
-   tokenize tree-based push/pop operations.  It will call 'lexer_lex' to
-   tokenize directory names prior to making a push operation. safe checking for
-   all returns from the filesystem handler will exit on serious system errors.
+int   scanner_scandir_r(DIR*);
+extern //lexer.rl
+int   lexer_init(void);
+extern //lexer.rl
+void  lexer_quit(void);
+extern //lexer.rl
+int   lexer_lexfile(uint8_t*);
+extern //lexer.rl
+int   lexer_lexdir(uint8_t*);
+extern //lexer.rl
+void  lexer_closedir(void);
 
-   after pushing a new directory to the directory list, the scanner will dredge
-   the directory and alphabetically sort all file entries into the lexer's file
-   array, while placing all subdirectory entries in the current depth's child
-   directory stack to be scanned later.
-
-   Returns the number of tokens generated on success, -1 on error.
+/* Scan directory stream
+   Recursively scans the provided directory, sending CLOPEN and CLCLOSE tokens
+   to the parser when entering new directories (classes)
 */
-int scanner
-#define $($)#$ //stringifier
-#define ERR_CHILD  "Fatal: Maximum of " $(DL_CD_STACKSIZE)                  \
-  " child directories exceeded for directory at depth %i\n"                \
-  ,DL_LEN()
-#define ERR_DEPTH  "Fatal: Maximum directory depth of " $(DL_STACKSIZE)     \
-  " exceeded during directory scan\n"
-#define ERR_DL     "Fatal: Directory List Stack Corruption %x\n", DL_LEN()
-()
-{ int ntok = 0;
- scan:
-  if (DL_CD_LEN() >= DL_CD_STACKSIZE)//fail if maxchildren exceeded
-    { fprintf(stderr, ERR_CHILD);
-      goto fail;
-    }
-  if (DL_CD_LEN() > 0)               //There are entities to process
-    { if (DL_CD_POP() == NULL)            //If the dirent is null, then the
-        goto libfail;                       //lib function in dirent has failed
-      ntok += lexer_lexstring(DL_CD_CURNAME());//lex the directory name
-      if (DL_LEN() >= DL_STACKSIZE)       //fail if maxdepth exceeded
-        { fprintf(stderr, ERR_DEPTH);
-          goto fail;
-        }
-      if (chdir(DL_CD_CURNAME()))         //move into the new directory
-       goto libfail;
-      if (DL_CURDIR() == NULL)            //open the cwd
-       goto libfail;
-      lexer_pushtok(CLOPEN, 0);           //Push "Open Directory" token
-      ntok++;
-      return dredge_current_depth();      //Filter and sort the current depth
-    }
-  else if (DL_LEN() >= 0)            //Any dirs left? (Including root)
-    { if (closedir(DL_POP()))             //close the directory we just left
-        goto libfail;
-      if (DL_LEN() == -1)                 //If we just popped root,
-        goto done;                          //we're done
-      lexer_pushtok(CLCLOSE, 0);          //Else push "Close Directory" token,
-      ntok++;
-      if (!chdir(".."))                   //move up a directory and
-        goto scan;                          //start over
-    }
-  fprintf(stderr, ERR_DL);
- libfail:
-  perror("scanner: ");
- fail:
-  return -1;
- done:
-  return ntok;
-}
-
-/* Scan Pixels 
-   Scans up to 'len' pixels from the current file into 'buf'.
-   Returns the number of pixels scanned from the file, or -1 on error
-*/
-int scanner_scanpixels
-( int*  buf,
-  int   max_len
-)
-{ static int col_len, row_len, row;
-  //Open the current file if not yet open
-  if (current_open_file == NULL) 
-    { if ((current_open_file = fopen(DL_CD_CURNAME(),"rb")) == NULL)
-       { perror("fopen: ");
-         return -1;
-        }
-      //Verify file header, get row_len/col_len
-      //if (read_img_header(&row_len, &col_len))
-      //return -1;
-      row = 0;
-    }
-  //Read pixels into the buffer if there are rows left in the image
-  if (row++ < row_len)
-    //TODO: return read_img_pixels(buf, col_len);
-    printf("SCANPIXELS NOT IMPLEMENTED\n.");
-  //Close the file and return 0
-  fclose(current_open_file);
-  current_open_file = NULL;
-  return 0;
+int scanner_scandir
+( DIR* dirp )
+{ int scandir_status;
+  if (lexer_init())
+    return -1;
+  scandir_status = scanner_scandir_r(dirp);
+  lexer_quit();
+  return scandir_status;
 }
 
-/* Directory Entity Sort and Filter (Dredge)
-   This filter removes all unhandled file types, and places any 'DT_DIR' type
-   files in the current Directory List's directory stack.  Upon finishing,
-   the 'CE_STACK' is sorted alphabetically, and the current 'DL_CD_STACK' is
-   populated.  Prints warnings for unhandled files.
-
-   Returns -1 if 'readdir' encounters an error, otherwise returns the number of
-   directory entries sent to the external 'lexer_direntpa' array.
-*/
-typedef //so we can typecast dirent's 'alphasort()' to take const void*s
-int (*qcomp)(const void*, const void*);
-static inline
-int dredge_current_depth
-#define READDIR_ERROR (-1)
-#define READDIR_DONE  (0)
-#define DPS_LEN()     (lexer_direntpp - lexer_direntpa)
-#define DPS_PUSH(E)   (*lexer_direntpp++ = E)
-()
-{ struct dirent**  direntpp = lexer_direntpa;
-  DIR*             cwd      = DL_CURDIR();
-  struct dirent*   direntp;
-  DL_CD_INIT();
- scan_next:
-  if ((direntp = readdir(cwd)) != NULL)
-    { switch (direntp->d_type)
-        { case DT_REG:
-            DPS_PUSH(direntp);
-            goto scan_next;
-          case DT_DIR:
-           if (*(direntp->d_name) == '.') //skip hidden files and relative dirs
-              goto scan_next;
-           DL_CD_PUSH(direntp);
-            goto scan_next;
+static
+int scanner_scandir_r
+( DIR* dirp )
+{ DIR* cdirp;
+  struct dirent* direntp;
+ scan_next_dirent:
+  errno = 0;
+  direntp = readdir(dirp);
+  if (errno)
+    goto libfail;
+  if (direntp != NULL)
+    { if (*(direntp->d_name) == '.') //skip hidden or relative files
+       goto scan_next_dirent;
+      switch (direntp->d_type)
+       { case DT_REG:
+           lexer_lexfile((uint8_t*)direntp->d_name);
+           goto scan_next_dirent;
+         case DT_DIR:
+           lexer_lexdir((uint8_t*)direntp->d_name);  //lex the dirname
+           if (chdir(direntp->d_name))    //change to the specified dir
+             goto libfail;
+           errno = 0;
+           if ((cdirp = opendir(".")) == NULL || errno) //open it
+             goto libfail;
+           if(scanner_scandir_r(cdirp))  //scan the directory
+             goto libfail;
+           if (chdir(".."))              //return to the parent dir
+             goto libfail;
+            lexer_closedir();            //push "Close Directory" token
+           goto scan_next_dirent;        //continue scan
           case DT_UNKNOWN:
-            warnx("unknown file %s: ignoring", direntp->d_name);
+           warnx("unknown file %s: ignoring", direntp->d_name);
           default:
-            goto scan_next;
-        }
+           goto scan_next_dirent;
+       }
     }
-  if (errno)
-    return -1;
-  qsort(lexer_direntpa, DPS_LEN(), sizeof direntp, (qcomp)alphasort);
-  return DPS_LEN();
+  return closedir(dirp);
+ libfail:
+  perror("scanner_scandir");
+  return -1;
 }