6dcbc9daf0d10d13e5c81b92d1e804c0b8598179
[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 /* Internal */
24 #include "parser.tab.h"
25 /* Public */
26 int scanner_init(void);
27 void scanner_quit(void);
28 int scanner_scanpath(char const*);
29 int scanner_scandir(DIR*);
30 yypstate* apc_pstate;
31 yycstate* apc_cstate;
32 /* Private */
33 extern //lexer.c
34 int lexer_lexfile(uint8_t const*);
35 extern //lexer.rl
36 int lexer_lexstring(uint8_t const*, int);
37 #define PUSHTOK(T,L) yypush_parse(apc_pstate, T, L, apc_cstate)
38
39 /* Init
40 Establishes yy states
41 */
42 int scanner_init
43 ( void )
44 { if (apc_pstate != NULL || apc_cstate != NULL)
45 scanner_quit();
46 apc_pstate = yypstate_new();
47 apc_cstate = yycstate_new();
48 return (apc_pstate != NULL && apc_cstate != NULL);
49 }
50
51 /* Quit
52 Free initialized memory
53 */
54 void scanner_quit
55 ( void )
56 { yypstate_delete(apc_pstate);
57 yycstate_delete(apc_cstate);
58 apc_pstate = NULL;
59 apc_cstate = NULL;
60 }
61
62 /* Scan the provided path
63 Changes working directory to the provided pathname and, if successful, sends
64 a directory stream of the provided path to scanner_scandir
65 */
66 int scanner_scanpath
67 ( char const* pathname )
68 { DIR* dirp;
69 errno = 0;
70 if ((dirp = opendir(pathname)) == NULL || errno)
71 return -1;
72 if (chdir(pathname))
73 return -1;
74 return scanner_scandir(dirp);
75 }
76
77 /* Scan directory stream
78 Recursively scans the provided directory, sending CLOPEN and CLCLOSE tokens
79 to the parser when entering new directories (classes)
80 */
81 int scanner_scandir
82 ( DIR* dirp )
83 { DIR* cdirp;
84 struct dirent* direntp;
85 scan_next_dirent:
86 errno = 0;
87 direntp = readdir(dirp);
88 if (errno)
89 goto libfail;
90 if (direntp != NULL)
91 { if (*(direntp->d_name) == '.') //skip hidden or relative files
92 goto scan_next_dirent;
93 switch (direntp->d_type)
94 { case DT_REG:
95 printf("lexfile %s\n",direntp->d_name);
96 //lexer_lexfile((uint8_t*)direntp->d_name);
97 goto scan_next_dirent;
98 case DT_DIR:
99 //lexer_lexstring((uint8_t*)direntp->d_name); //lex the dirname
100 printf("lexdir %s\n",direntp->d_name);
101 if (chdir(direntp->d_name)) //change to the specified dir
102 goto libfail;
103 errno = 0;
104 if ((cdirp = opendir(".")) == NULL || errno) //open it
105 goto libfail;
106 //PUSHTOK(CLOPEN, 0); //push "Open Directory" token
107 printf("Scanner entered [%s]\n",direntp->d_name);
108 if(scanner_scandir(cdirp)) //scan the directory
109 goto libfail;
110 if (chdir("..")) //return to the parent dir
111 goto libfail;
112 //PUSHTOK(CLCLOSE, 0); //push "Close Directory" token
113 printf("Scanner returned\n");
114 goto scan_next_dirent; //continue scan
115 case DT_UNKNOWN:
116 warnx("unknown file %s: ignoring", direntp->d_name);
117 default:
118 goto scan_next_dirent;
119 }
120 }
121 return closedir(dirp);
122 libfail:
123 perror("scanner_scandir");
124 return -1;
125 }