/*!@file \brief APC Directory Scanner \details This hand-written parser/scanner traverses a directory tree and tokenizes elements of the structure which correspond to APC grammar. The parser is implemented as a 2D stack which populates a list of child directories at each depth, handling only the leaf nodes (regular files) of the directory open at the current depth to conserve memory and speed up traversal. The scanner works with the lexer to lexically analyze text, and assumes the existence of an external 'lex' function \author Jordan Lavatai \date Aug 2016 ----------------------------------------------------------------------------*/ /* Standard */ #include //print #include //errno /* Posix */ #include //warnx #include //exit #include //chdir #include //opendir #include //unicode strings #include //strlen /* Internal */ #include "parser.tab.h" /* Public */ void scanner_quit(void); int scanner_scandir(DIR*); /* Private */ static 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); /* Scan directory stream Recursively scans the provided directory, sending CLOPEN and CLCLOSE tokens to the parser when entering new directories (classes) */ int scanner_scandir ( DIR* dirp ) { int scandir_status; if (lexer_init()) return -1; scandir_status = scanner_scandir_r(dirp); lexer_quit(); return scandir_status; } 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); default: goto scan_next_dirent; } } return closedir(dirp); libfail: perror("scanner_scandir"); return -1; }