--- /dev/null
+/*!@file\r
+ \brief APC Directory Scanner\r
+ \details This hand-written parser/scanner traverses a directory tree and\r
+ tokenizes elements of the structure which correspond to APC grammar.\r
+ The parser is implemented as a 2D stack which populates a list of\r
+ child directories at each depth, handling only the leaf nodes\r
+ (regular files) of the directory open at the current depth to\r
+ conserve memory and speed up traversal.\r
+ The scanner works with the lexer to lexically analyze text, and\r
+ assumes the existence of an external 'lex' function\r
+ \author Jordan Lavatai\r
+ \date Aug 2016\r
+ ----------------------------------------------------------------------------*/\r
+/* Standard */\r
+#include <stdio.h> //print\r
+#include <errno.h> //errno\r
+/* Posix */\r
+#include <err.h> //warnx\r
+#include <stdlib.h> //exit\r
+#include <unistd.h> //chdir\r
+#include <dirent.h> //opendir\r
+\r
+#include "parser.tab.h"\r
+/* Public */\r
+int scanner_init(void);\r
+void scanner_quit(void);\r
+int scanner(void);\r
+/* Private */\r
+#ifndef DL_STACKSIZE\r
+#define DL_STACKSIZE 64\r
+#endif\r
+#ifndef DL_CD_STACKSIZE\r
+#define DL_CD_STACKSIZE DL_STACKSIZE //square tree\r
+#endif\r
+extern //lexer.c\r
+int lexer_lex(const char*);\r
+extern //lexer.c\r
+void lexer_pushtok(int, int);\r
+static\r
+int dredge_current_depth(void);\r
+extern //lexer.c\r
+struct dirent* lexer_direntpa[], **lexer_direntpp;\r
+extern //SRC_DIR/bin/tools/apc.c\r
+const char* cargs['Z'];\r
+\r
+struct dirlist\r
+{ DIR* dirp;\r
+ struct dirent* child_directory_stack[DL_CD_STACKSIZE],** cds;\r
+} directory_list_stack[DL_STACKSIZE + 1],* dls; //+1 for the root dir\r
+\r
+/* Directory Listing Stack\r
+ FILO Stack for keeping an open DIR* at each directory depth for treewalk.\r
+ This stack is depth-safe, checking its depth during push operations, but not\r
+ during pop operations, to ensure the thread doesn't open too many files at\r
+ once (512 in c runtime), or traverse too far through symbolic links.\r
+ A directory listing includes a DIR* and all DIR-typed entity in the directory\r
+ as recognized by dirent, populated externally (and optionally).\r
+ This stack behaves abnormally by incrementing its PUSH operation prior to\r
+ evaluation, and the POP operations after evaluation. This behavior allows\r
+ the 'DL_CURDEPTH' operation to map to the current element in the 'dl_stack'\r
+ array, and it is always treated as the "current depth". This also allows us\r
+ to init the root directory to 'directory_list_stack'[0] and pop it in a safe\r
+ and explicit manner.\r
+*/\r
+#define DL_STACK (directory_list_stack)\r
+#define DL_STACKP (dls)\r
+#define DL_CD_STACK ((*DL_STACKP).child_directory_stack)\r
+#define DL_CD_STACKP ((*DL_STACKP).cds)\r
+#define DL_CURDIR() ((*DL_STACKP).dirp)\r
+#define DL_LEN() (DL_STACKP - DL_STACK)\r
+#define DL_CD_LEN() (DL_CD_STACKP - DL_CD_STACK)\r
+#define DL_INIT() (DL_STACKP = DL_STACK)\r
+#define DL_CD_INIT() (DL_CD_STACKP = DL_CD_STACK)\r
+#define DL_POP() ((*DL_STACKP--).dirp)\r
+#define DL_CD_POP() (*--DL_CD_STACKP)\r
+#define DL_PUSH(D) ((*++DL_STACKP).dirp = D)\r
+#define DL_CD_PUSH(E) (*DL_CD_STACKP++ = E)\r
+\r
+\r
+/* Initializer\r
+ Initializer expects a function pointer to its lexical analysis function.\r
+ Sets up stack pointers and returns boolean true if 'opendir' encounters an\r
+ error, or if dredge_current_depth returns boolean true.\r
+*/\r
+int scanner_init\r
+#define CWDSTR "./"\r
+#define ROOTDIR (cargs['d'] ? cargs['d'] : CWDSTR)\r
+()\r
+{ DL_INIT();\r
+ DL_STACK[0].dirp = opendir(ROOTDIR);\r
+ printf("Root dir %s\n",ROOTDIR);\r
+ return !chdir(ROOTDIR) && (DL_STACK[0].dirp == NULL || dredge_current_depth() == -1);\r
+}\r
+\r
+/* Quit */\r
+void scanner_quit\r
+()\r
+{ if (DL_CURDIR())\r
+ closedir(DL_CURDIR());\r
+}\r
+\r
+/* Scanner\r
+ The main driver of the scanner will advance the current treewalk state and\r
+ tokenize tree-based push/pop operations. It will call 'lexer_lex' to\r
+ tokenize directory names prior to making a push operation. safe checking for\r
+ all returns from the filesystem handler will exit on serious system errors.\r
+\r
+ after pushing a new directory to the directory list, the scanner will dredge\r
+ the directory and alphabetically sort all file entries into the lexer's file\r
+ array, while placing all subdirectory entries in the current depth's child\r
+ directory stack to be scanned later.\r
+\r
+ Returns the number of elements added to the lexer's file array, or -1 on\r
+ error\r
+*/\r
+int scanner\r
+#define $($)#$ //stringifier\r
+#define ERR_CHILD "Fatal: Maximum of " $(DL_CD_STACKSIZE) \\r
+ " child directories exceeded for directory at depth %i\n" \\r
+ ,DL_LEN()\r
+#define ERR_DEPTH "Fatal: Maximum directory depth of " $(DL_STACKSIZE) \\r
+ " exceeded during directory scan\n"\r
+#define ERR_DL "Fatal: Directory List Stack Corruption %x\n", DL_LEN()\r
+#define TOK_CLOPEN 0x55, 1 //TODO\r
+#define TOK_CLCLOSE 0x56, 1 //TODO\r
+()\r
+{ struct dirent* direntp;\r
+ struct DIR* DIRp;\r
+ parse:\r
+ if (DL_CD_LEN() >= DL_CD_STACKSIZE)//fail if maxchildren exceeded\r
+ { fprintf(stderr, ERR_CHILD);\r
+ goto fail;\r
+ }\r
+ if (DL_CD_LEN() > 0) //There are entities to process\r
+ { if ((direntp = DL_CD_POP()) == NULL)//If the dirent is null, the library\r
+ goto libfail; //function in dirent has failed\r
+ lexer_lex(direntp->d_name); //lex the directory name\r
+ if (DL_LEN() >= DL_STACKSIZE) //fail if maxdepth exceeded\r
+ { fprintf(stderr, ERR_DEPTH);\r
+ goto fail;\r
+ }\r
+ if (chdir(direntp->d_name)) //move into the new directory\r
+ goto libfail;\r
+ DL_PUSH(opendir(CWDSTR));\r
+ if (DL_CURDIR() == NULL) //open the cwd\r
+ goto libfail;\r
+ lexer_pushtok(TOK_CLOPEN); //Push "Open Directory" token\r
+ return dredge_current_depth(); //Filter and sort the current depth\r
+ }\r
+ else if (DL_LEN() >= 0) //Any dirs left? (Including root)\r
+ { if (closedir(DL_POP())) //close the directory we just left\r
+ goto libfail;\r
+ if (DL_LEN() == -1) //If we just popped root,\r
+ return 0; //we're done\r
+ lexer_pushtok(TOK_CLCLOSE); //Else push "Close Directory" token,\r
+ if (!chdir("..")) //move up a directory and\r
+ goto parse; //start over\r
+ }\r
+ fprintf(stderr, ERR_DL);\r
+ libfail:\r
+ perror("parsedir");\r
+ fail:\r
+ exit(EXIT_FAILURE);\r
+}\r
+\r
+/* Directory Entity Sort and Filter (Dredge)\r
+ This filter removes all unhandled file types, and places any 'DT_DIR' type\r
+ files in the current Directory List's directory stack. Upon finishing,\r
+ the 'CE_STACK' is sorted alphabetically, and the current 'DL_CD_STACK' is\r
+ populated. Prints warnings for unhandled files.\r
+\r
+ Returns -1 if 'readdir' encounters an error, otherwise returns the number of\r
+ directory entries sent to the external 'lexer_direntpa' array.\r
+*/\r
+typedef\r
+int (*qcomp)(const void*, const void*);\r
+static inline\r
+int dredge_current_depth\r
+#define READDIR_ERROR (-1)\r
+#define READDIR_DONE (0)\r
+#define DPS_LEN() (lexer_direntpp - lexer_direntpa)\r
+#define DPS_PUSH(E) (*lexer_direntpp++ = E)\r
+()\r
+{ struct dirent** direntpp = lexer_direntpa;\r
+ DIR* cwd = DL_CURDIR();\r
+ struct dirent* direntp;\r
+ DL_CD_INIT();\r
+ scan_next:\r
+ if ((direntp = readdir(cwd)) != NULL)\r
+ { switch (direntp->d_type)\r
+ { case DT_REG:\r
+ DPS_PUSH(direntp);\r
+ goto scan_next;\r
+ case DT_DIR:\r
+ if (*(direntp->d_name) == '.') //skip hidden files and relative dirs\r
+ goto scan_next;\r
+ DL_CD_PUSH(direntp);\r
+ goto scan_next;\r
+ case DT_UNKNOWN:\r
+ warnx("unknown file %s: ignoring", direntp->d_name);\r
+ default:\r
+ goto scan_next;\r
+ }\r
+ }\r
+ if (errno)\r
+ return -1;\r
+ qsort(lexer_direntpa, DPS_LEN(), sizeof direntp, (qcomp)alphasort);\r
+ return DPS_LEN();\r
+}\r
+\r