test complete
[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_scanpath(char const*);
29 int scanner_scandir(DIR*);
30 /* Private */
31 static
32 int scanner_scandir_r(DIR*);
33 extern //lexer.rl
34 int lexer_init(void);
35 extern //lexer.rl
36 void lexer_quit(void);
37 extern //lexer.rl
38 int lexer_lexfile(uint8_t*);
39 extern //lexer.rl
40 int lexer_lexdir(uint8_t*);
41 extern //lexer.rl
42 void lexer_closedir(void);
43 /* Scan the provided path
44 Changes working directory to the provided pathname and, if successful, sends
45 a directory stream of the provided path to scanner_scandir
46 */
47 int scanner_scanpath
48 ( char const* pathname )
49 { DIR* dirp;
50 errno = 0;
51 if ((dirp = opendir(pathname)) == NULL || errno)
52 { fprintf(stderr, "Path %s could not be accessed\n", pathname);
53 return -1;
54 }
55 if (chdir(pathname))
56 return -1;
57 return scanner_scandir(dirp);
58 }
59
60 /* Scan directory stream
61 Recursively scans the provided directory, sending CLOPEN and CLCLOSE tokens
62 to the parser when entering new directories (classes)
63 */
64 int scanner_scandir
65 ( DIR* dirp )
66 { int scandir_status;
67 if (lexer_init())
68 return -1;
69 scandir_status = scanner_scandir_r(dirp);
70 lexer_quit();
71 return scandir_status;
72 }
73
74 static
75 int scanner_scandir_r
76 ( DIR* dirp )
77 { DIR* cdirp;
78 struct dirent* direntp;
79 scan_next_dirent:
80 errno = 0;
81 direntp = readdir(dirp);
82 if (errno)
83 goto libfail;
84 if (direntp != NULL)
85 { if (*(direntp->d_name) == '.') //skip hidden or relative files
86 goto scan_next_dirent;
87 switch (direntp->d_type)
88 { case DT_REG:
89 lexer_lexfile((uint8_t*)direntp->d_name);
90 goto scan_next_dirent;
91 case DT_DIR:
92 lexer_lexdir((uint8_t*)direntp->d_name); //lex the dirname
93 if (chdir(direntp->d_name)) //change to the specified dir
94 goto libfail;
95 errno = 0;
96 if ((cdirp = opendir(".")) == NULL || errno) //open it
97 goto libfail;
98 if(scanner_scandir_r(cdirp)) //scan the directory
99 goto libfail;
100 if (chdir("..")) //return to the parent dir
101 goto libfail;
102 lexer_closedir(); //push "Close Directory" token
103 goto scan_next_dirent; //continue scan
104 case DT_UNKNOWN:
105 warnx("unknown file %s: ignoring", direntp->d_name);
106 default:
107 goto scan_next_dirent;
108 }
109 }
110 return closedir(dirp);
111 libfail:
112 perror("scanner_scandir");
113 return -1;
114 }