/*!@file \brief lexical analyzer implementation for APC \details this lexer scans a root directory given from the command line for subdirectories and files structured for the APC grammar. \author Jordan Lavatai \date Aug 2016 ----------------------------------------------------------------------------*/ /* Standard */ #include #include #include /* Posix */ #include #include #include /* Local */ #include "parser.tab.h" #define TOKEN_BUF_SIZE 1024 #define ENTITY_BUF_SIZE 512 #define SCANDIR_ERROR -1 int lexer_init(void); int lexer(void); extern //main.c const char* cargs['Z']; static inline int scan(void); static inline int qcomp(const void*, const void*); /* 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 S(S)#S #ifndef DL_DEPTHMAX #define DL_DEPTHMAX 64 #endif #ifndef DL_CHILDMAX #define DL_CHILDMAX DL_DEPTHMAX //square tree #endif #define DL_STACK (directory_list_stack) #define DL_STACKP (dls) #define DL_STACKSIZE (DL_DEPTHMAX + 1) //+1 because push increments early #define DL_LEN (DL_STACKP - DL_STACK) #define DL_CURDEPTH (DL_LEN) #define DL_CD_STACK ((*DL_STACKP).child_entity_stack) #define DL_CD_STACKP ((*DL_STACKP).cds) #define DL_CD_STACKSIZE (DL_DEPTHMAX) //square tree #define DL_CD_LEN (DL_CD_STACKP - DL_CD_STACK) #define ERR_CHILD "Fatal: Maximum of " S(MAX_CHILD) \ " child directories exceeded for directory %s at depth %i\n" \ ,DL_STACK[DL_DEPTH()].label,DL_DEPTH() #define ERR_DEPTH "Fatal: Maximum directory depth of " S(DL_DEPTHMAX) \ " exceeded during directory scan\n" #define DL_INIT() (DL_STACKP = DL_STACK) #define DL_CD_INIT() (DL_CD_STACKP = DL_CD_STACK) #define DL_POP() ((DIR*)(*DL_STACKP--)) #define DL_CD_POP() (*--DL_CD_STACKP) #define DL_PUSH(D) (*(DIR**)(++DL_STACKP) = DIRP) #define DL_CD_PUSH(E) (*DL_CD_STACKP++ = E) #define DL_SAFEPUSH(D) \ do { \ if (DL_DEPTH() >= DL_DEPTHMAX) \ { fprintf(stderr, ERR_DEPTH); \ exit(EXIT_FAILURE); \ } \ if ((*(DIR**)(++DL_STACKP) = D) == NULL) \ { perror("DL_SAFEPUSH"); \ exit(EXIT_FAILURE); \ } \ } while(0) #define DL_CD_POPULATE() \ do { \ DL_CD_INIT(); \ while ((*DL_CD_STACKP = readdir(*DL_STACKP)) != NULL) \ { switch((*DL_CD_STACKP)->d_type) \ { case DT_REG: \ case DT_DIR: \ DL_CD_STACKP++; \ break; \ default: \ } \ } \ qsort(DL_CD_STACKP, DL_CD_LEN, sizeof *DL_CD_STACKP, qcomp); \ } while (0) static struct dirlist { DIR* dirp; struct dirent** child_directory_stack[DL_CD_STACKSIZE],*** cds; const char* label; } directory_list_stack[DL_STACKSIZE],* dls; static /* Directory Entity Stack Simple stack for keeping track of dirents still being processed at the current depth. */ #define CE_STACK (child_entity_stack) #define CE_STACKP (ces) #define CE_STACKSIZE 1024 static struct dirent** child_entity_stack[CE_STACKSIZE],*** ces; #define CE_INIT() (CE_STACKP = CE_STACK) #define CE_POP() (*--CE_STACK) #define CE_PUSH(E) (*CE_STACK++ = E) /* Token Stack This is a FIFO stack whose pointers are a union of either a pointer to an integer, or a pointer to two integers (a struct tok). This way, integers may be added or removed from the stack either singularly (IPUSH/IPOP), or as a full token of two integers (PUSH/POP). An alignment error will occur if IPOP or IPUSH are used a non-even number of times in a sequence! */ #define TK_STACK (token_stack) #define TK_STACKP (tks.t) #define TK_STACKPI (tks.i) #define TK_STACKX (tkx.t) #define TK_STACKXI (tkx.i) #ifndef TK_STACKSIZE #define TK_STACKSIZE 1024 #endif #define TK_EMPTY (TK_STACKP == TK_STACKX) #define TK_INIT() (TK_STACKP = TK_STACKX = TK_STACK) #define TK_POP() (*TK_STACKP++) #define TK_IPOP() (*TK_STACKPI++); #define TK_PUSH(TOK,LVAL) (*TKSTACKX++ = (struct tok){(LVAL),(TOK)}) #define TK_IPUSH(I) (*TKSTACKXI++ = (I)) static struct tok { int lval; int tok; } token_stack[TK_STACKSIZE]; static union tokp { int* i; struct tok* t; } tks, tkx; /* Initializer The initializer returns boolean true if opendir contains an error. */ int lexer_init() #define ERR_UF "Fatal: Unknown file [%s] encountered in directory %s", \ DL_CD_STACK[i]->d_name, DL_STACK[DL_DEPTH] { int i = 0; TK_INIT(); DL_INIT(); DL_STACK[0].label = cargs['r'] ? cargs['r'] : "./"; if ((DL_STACK[0].dirp = opendir(DL_STACK[0].label)) == NULL) return -1; errno = 0; while ((DL_CD_STACK[i] = readdir(DL_STACK[0].dirp)) != NULL) switch(DL_CD_STACK[i]->d_type) { default: fprintf(stderr, "Fatal: Unkown File %s\n", DL_CD_STACK[i]->d_name); } return (errno); } /* Lexer If the token buffer is empty, 'lexer' will initialize the token buffer and call 'lexer_scandir'. If #SCANDIR_ERROR is returned, an error is printed before sending a null return to bison. If 0 tokens are generated, the error printing is skipped. In all other cases, 'yylval' is set, and the token's integer representation is returned. */ int lexer() { if (TOK_BUF_EMPTY) { TOK_BUF_INIT(); switch (lexer_scan) { case SCANDIR_ERROR: perror("lexer_scan"); case 0: yylval = 0; return 0; default: } } yylval = TOK_BUF_IPOP(); return TOK_BUF_IPOP(); } static inline int lexer_scan() { static DIR* } /* Scanner Scans a filename from its alphabetically ordered list of file elements and tokenizes the result. If the file list is empty, then the stack of directory elements will be popped and processed as they are encountered. Returns the number of tokens generated. */ #define MAX_ENTITIES 256 static int lexer_scan() { static struct dirent* entity; static struct dirent* files[MAX_ENTITIES] = {0}; static int num_files = 0; #define NO_FILES (num_files == 0) if (NO_FILES) //sort out files and directories, grow directories from bottom up while ((entity = readdir(dirp)) != NULL) { switch (entity->d_type) { case DT_LNK: case DT_REG: files[num_files++] = entity; break; case DT_DIR: *(dirs - num_dirs++) = entity; break; case DT_UNKNOWN: default: printf("Ignoring unknown file: %s\n", entity->d_name); break; } } if (errno) perror("readdir"); qsort(&files[0], num_files, sizeof struct dirent*, qalpha); num_ents = scandirat(dirfd, ".", &namelist, scanfilter, scancompar); if (num_ents < 0) { perror("scandirat"); return -1; } //process files //recurse into directories return tbx - tbp; } /* Quicksort comparison function sort each dirent encountered first by its file type (regular files first) and then by their alphabetical ordering. */ static int qcompar ( const void* a, const void* b ) { }