comments updated
[henge/apc.git] / src / scanner.c
1 /*!@file
2 \brief APC Directory Scanner
3 \details This hand-written parser/scanner traverses a directory tree and
4 tokenizes elements of the structure which correspond to APC grammar.
5 The parser is implemented as a 2D stack which populates a list of
6 child directories at each depth, handling only the leaf nodes
7 (regular files) of the directory open at the current depth to
8 conserve memory and speed up traversal.
9 The scanner works with the lexer to lexically analyze text, and
10 assumes the existence of an external 'lex' function
11 \author Jordan Lavatai
12 \date Aug 2016
13 ----------------------------------------------------------------------------*/
14 /* Standard */
15 #include <stdio.h> //print
16 #include <errno.h> //errno
17 /* Posix */
18 #include <err.h> //warnx
19 #include <stdlib.h> //exit
20 #include <unistd.h> //chdir
21 #include <dirent.h> //opendir
22 #include <unistr.h> //unicode strings
23 #include <string.h> //strlen
24 /* Internal */
25 #include "parser.tab.h"
26 /* Public */
27 void scanner_quit(void);
28 int scanner_scandir(DIR*);
29 /* Private */
30 static
31 int scanner_scandir_r(DIR*);
32 extern //lexer.rl
33 int lexer_init(void);
34 extern //lexer.rl
35 void lexer_quit(void);
36 extern //lexer.rl
37 int lexer_lexfile(uint8_t*);
38 extern //lexer.rl
39 int lexer_lexdir(uint8_t*);
40 extern //lexer.rl
41 void lexer_closedir(void);
42
43 /* Scan directory stream
44 Recursively scans the provided directory, sending CLOPEN and CLCLOSE tokens
45 to the parser when entering new directories (classes)
46 */
47 int scanner_scandir
48 ( DIR* dirp )
49 { int scandir_status;
50 if (lexer_init())
51 return -1;
52 scandir_status = scanner_scandir_r(dirp);
53 lexer_quit();
54 return scandir_status;
55 }
56
57 static
58 int scanner_scandir_r
59 ( DIR* dirp )
60 { DIR* cdirp;
61 struct dirent* direntp;
62 scan_next_dirent:
63 errno = 0;
64 direntp = readdir(dirp);
65 if (errno)
66 goto libfail;
67 if (direntp != NULL)
68 { if (*(direntp->d_name) == '.') //skip hidden or relative files
69 goto scan_next_dirent;
70 switch (direntp->d_type)
71 { case DT_REG:
72 lexer_lexfile((uint8_t*)direntp->d_name);
73 goto scan_next_dirent;
74 case DT_DIR:
75 lexer_lexdir((uint8_t*)direntp->d_name); //lex the dirname
76 if (chdir(direntp->d_name)) //change to the specified dir
77 goto libfail;
78 errno = 0;
79 if ((cdirp = opendir(".")) == NULL || errno) //open it
80 goto libfail;
81 if(scanner_scandir_r(cdirp)) //scan the directory
82 goto libfail;
83 if (chdir("..")) //return to the parent dir
84 goto libfail;
85 lexer_closedir(); //push "Close Directory" token
86 goto scan_next_dirent; //continue scan
87 case DT_UNKNOWN:
88 warnx("unknown file %s: ignoring", direntp->d_name);
89 default:
90 goto scan_next_dirent;
91 }
92 }
93 return closedir(dirp);
94 libfail:
95 perror("scanner_scandir");
96 return -1;
97 }