+++ /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