/*!@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 */ int scanner_init(void); void scanner_quit(void); int scanner_scanpath(char const*); int scanner_scandir(DIR*); yypstate* apc_pstate; yycstate* apc_cstate; /* Private */ extern //lexer.rl int lexer_lexfile(uint8_t*); extern //lexer.rl int lexer_lexdir(uint8_t*); extern //lexer.rl void lexer_closedir(void); /* Init Establishes yy states */ int scanner_init ( void ) { if (apc_pstate != NULL || apc_cstate != NULL) scanner_quit(); apc_pstate = yypstate_new(); apc_cstate = yycstate_new(); return (apc_pstate == NULL || apc_cstate == NULL); } /* Quit Free initialized memory */ void scanner_quit ( void ) { yypstate_delete(apc_pstate); yycstate_delete(apc_cstate); apc_pstate = NULL; apc_cstate = NULL; } /* Scan the provided path Changes working directory to the provided pathname and, if successful, sends a directory stream of the provided path to scanner_scandir */ int scanner_scanpath ( char const* pathname ) { DIR* dirp; errno = 0; if ((dirp = opendir(pathname)) == NULL || errno) { fprintf(stderr, "Path %s could not be accessed\n", pathname); return -1; } if (chdir(pathname)) return -1; return scanner_scandir(dirp); } /* 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 ) { 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: printf("lexfile %s\n",direntp->d_name); lexer_lexfile((uint8_t*)direntp->d_name); goto scan_next_dirent; case DT_DIR: lexer_lexdir((uint8_t*)direntp->d_name); //lex the dirname printf("lexdir %s\n",direntp->d_name); if (chdir(direntp->d_name)) //change to the specified dir goto libfail; errno = 0; if ((cdirp = opendir(".")) == NULL || errno) //open it goto libfail; printf("Scanner entered [%s]\n",direntp->d_name); if(scanner_scandir(cdirp)) //scan the directory goto libfail; if (chdir("..")) //return to the parent dir goto libfail; lexer_closedir(); //push "Close Directory" token printf("Scanner returned\n"); 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; }