Daily bump.
[platform/upstream/gcc.git] / c++tools / resolver.cc
1 /* C++ modules.  Experimental!  -*- c++ -*-
2    Copyright (C) 2017-2020 Free Software Foundation, Inc.
3    Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
4
5    This file is part of GCC.
6
7    GCC is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3, or (at your option)
10    any later version.
11
12    GCC is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3.  If not see
19 <http://www.gnu.org/licenses/>.  */
20
21 #include "config.h"
22
23 #include "resolver.h"
24 // C++
25 #include <algorithm>
26 // C
27 #include <cstring>
28 // OS
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <sys/mman.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #ifndef DIR_SEPARATOR
36 #define DIR_SEPARATOR '/'
37 #endif
38
39 module_resolver::module_resolver (bool map, bool xlate)
40   : default_map (map), default_translate (xlate)
41 {
42 }
43
44 module_resolver::~module_resolver ()
45 {
46   if (fd_repo >= 0)
47     close (fd_repo);
48 }
49
50 bool
51 module_resolver::set_repo (std::string &&r, bool force)
52 {
53   if (force || repo.empty ())
54     {
55       repo = std::move (r);
56       force = true;
57     }
58   return force;
59 }
60
61 bool
62 module_resolver::add_mapping (std::string &&module, std::string &&file,
63                               bool force)
64 {
65   auto res = map.emplace (std::move (module), std::move (file));
66   if (res.second)
67     force = true;
68   else if (force)
69     res.first->second = std::move (file);
70
71   return force;
72 }
73
74 int
75 module_resolver::read_tuple_file (int fd, char const *prefix, bool force)
76 {
77   struct stat stat;
78   if (fstat (fd, &stat) < 0)
79     return -errno;
80
81   if (!stat.st_size)
82     return 0;
83
84   // Just map the file, we're gonna read all of it, so no need for
85   // line buffering
86   void *buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
87   if (buffer == MAP_FAILED)
88     return -errno;
89
90   size_t prefix_len = prefix ? strlen (prefix) : 0;
91   unsigned lineno = 0;
92
93   for (char const *begin = reinterpret_cast <char const *> (buffer),
94          *end = begin + stat.st_size, *eol;
95        begin != end; begin = eol + 1)
96     {
97       lineno++;
98       eol = std::find (begin, end, '\n');
99       if (eol == end)
100         // last line has no \n, ignore the line, you lose
101         break;
102
103       auto *pos = begin;
104       bool pfx_search = prefix_len != 0;
105
106     pfx_search:
107       while (*pos == ' ' || *pos == '\t')
108         pos++;
109
110       auto *space = pos;
111       while (*space != '\n' && *space != ' ' && *space != '\t')
112         space++;
113
114       if (pos == space)
115         // at end of line, nothing here 
116         continue;
117
118       if (pfx_search)
119         {
120           if (size_t (space - pos) == prefix_len
121               && std::equal (pos, space, prefix))
122             pfx_search = false;
123           pos = space;
124           goto pfx_search;
125         }
126
127       std::string module (pos, space);
128       while (*space == ' ' || *space == '\t')
129         space++;
130       std::string file (space, eol);
131
132       if (module[0] == '$')
133         {
134           if (module == "$root")
135             set_repo (std::move (file));
136           else
137             return lineno;
138         }
139       else
140         {
141           if (file.empty ())
142             file = GetCMIName (module);
143           add_mapping (std::move (module), std::move (file), force);
144         }
145     }
146
147   munmap (buffer, stat.st_size);
148
149   return 0;
150 }
151
152 char const *
153 module_resolver::GetCMISuffix ()
154 {
155   return "gcm";
156 }
157
158 module_resolver *
159 module_resolver::ConnectRequest (Cody::Server *s, unsigned version,
160                                  std::string &a, std::string &i)
161 {
162   if (!version || version > Cody::Version)
163     s->ErrorResponse ("version mismatch");
164   else if (a != "GCC")
165     // Refuse anything but GCC
166     ErrorResponse (s, std::string ("only GCC supported"));
167   else if (!ident.empty () && ident != i)
168     // Failed ident check
169     ErrorResponse (s, std::string ("bad ident"));
170   else
171     // Success!
172     s->ConnectResponse ("gcc");
173
174   return this;
175 }
176
177 int
178 module_resolver::ModuleRepoRequest (Cody::Server *s)
179 {
180   s->PathnameResponse (repo);
181   return 0;
182 }
183
184 int
185 module_resolver::cmi_response (Cody::Server *s, std::string &module)
186 {
187   auto iter = map.find (module);
188   if (iter == map.end ())
189     {
190       std::string file;
191       if (default_map)
192         file = std::move (GetCMIName (module));
193       auto res = map.emplace (module, file);
194       iter = res.first;
195     }
196
197   if (iter->second.empty ())
198     s->ErrorResponse ("no such module");
199   else
200     s->PathnameResponse (iter->second);
201
202   return 0;
203 }
204
205 int
206 module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags,
207                                       std::string &module)
208 {
209   return cmi_response (s, module);
210 }
211
212 int
213 module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags,
214                                       std::string &module)
215 {
216   return cmi_response (s, module);
217 }
218
219 int
220 module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
221                                           std::string &include)
222 {
223   auto iter = map.find (include);
224   if (iter == map.end () && default_translate)
225     {
226       // Not found, look for it
227       auto file = GetCMIName (include);
228       struct stat statbuf;
229       bool ok = true;
230
231 #if HAVE_FSTATAT
232       int fd_dir = AT_FDCWD;
233       if (!repo.empty ())
234         {
235           if (fd_repo == -1)
236             {
237               fd_repo = open (repo.c_str (),
238                               O_RDONLY | O_CLOEXEC | O_DIRECTORY);
239               if (fd_repo < 0)
240                 fd_repo = -2;
241             }
242           fd_dir = fd_repo;
243         }
244
245       if (!repo.empty () && fd_repo < 0)
246         ok = false;
247       else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0
248                || !S_ISREG (statbuf.st_mode))
249         ok = false;
250 #else
251       auto append = repo;
252       append.push_back (DIR_SEPARATOR);
253       append.append (file);
254       if (stat (append.c_str (), &statbuf) < 0
255           || !S_ISREG (statbuf.st_mode))
256         ok = false;
257 #endif
258       if (!ok)
259         // Mark as not present
260         file.clear ();
261       auto res = map.emplace (include, file);
262       iter = res.first;
263     }
264
265   if (iter == map.end () || iter->second.empty ())
266     s->BoolResponse (false);
267   else
268     s->PathnameResponse (iter->second);
269
270   return 0;
271 }
272