2424#include " utils.h"
2525
2626#include < algorithm>
27+ #include < cstdio>
2728#include < cstdlib>
29+ #include < memory>
2830#include < sys/stat.h>
2931#include < unordered_set>
3032#include < utility>
@@ -235,7 +237,7 @@ bool Path::isCPP(const std::string &path)
235237bool Path::acceptFile (const std::string &path, const std::set<std::string> &extra)
236238{
237239 bool header = false ;
238- return (identify (path, &header) != Standards::Language::None && !header) || extra.find (getFilenameExtension (path)) != extra.end ();
240+ return (identify (path, false , &header) != Standards::Language::None && !header) || extra.find (getFilenameExtension (path)) != extra.end ();
239241}
240242
241243// cppcheck-suppress unusedFunction
@@ -245,13 +247,75 @@ bool Path::isHeader(const std::string &path)
245247 return startsWith (extension, " .h" );
246248}
247249
248- Standards::Language Path::identify (const std::string &path, bool *header)
250+ #include < iostream>
251+
252+ static bool hasEmacsCppMarker (const char * path)
253+ {
254+ // TODO: identify is called three times for each file
255+ std::cout << path << ' \n ' ;
256+
257+ FILE *fp = fopen (path, " rt" );
258+ if (!fp)
259+ return false ;
260+ std::unique_ptr<FILE, decltype (&fclose)> fp_deleter (fp, fclose);
261+ std::string buf (1024 , ' \0 ' );
262+ // TODO: read first line only
263+ if (fgets (const_cast <char *>(buf.data ()), buf.size (), fp) == nullptr )
264+ return false ; // failed to read file
265+ // TODO: replace with regular expression
266+ const auto pos1 = buf.find (" -*-" );
267+ if (pos1 == std::string::npos)
268+ return false ; // no start marker
269+ const auto pos_nl = buf.find_first_of (" \r\n " );
270+ if (pos_nl != std::string::npos && (pos_nl < pos1))
271+ return false ; // not on first line
272+ const auto pos2 = buf.find (" -*-" , pos1 + 3 );
273+ // TODO: make sure we have read the whole line before bailing out
274+ if (pos2 == std::string::npos)
275+ return false ; // no end marker
276+ const std::string buf_trim = trim (buf); // trim whitespaces
277+ if (buf_trim[0 ] != ' /' || buf_trim[1 ] != ' /' )
278+ return false ; // not a comment
279+
280+ std::cout /* << path << " -*/ << " Emacs marker: '" << buf.substr (pos1, (pos2 + 3 ) - pos1) << " '" << ' \n ' ;
281+
282+ // there are more variations with lowercase and no whitespaces
283+ // -*- C++ -*-
284+ // -*- Mode: C++; -*-
285+ // -*- Mode: C++; c-basic-offset: 8 -*-
286+ std::string marker = trim (buf.substr (pos1 + 3 , pos2 - pos1 - 3 ), " ;" );
287+ // cut off additional attributes
288+ const auto pos_semi = marker.find (' ;' );
289+ if (pos_semi != std::string::npos)
290+ marker.resize (pos_semi);
291+ findAndReplace (marker, " mode:" , " " );
292+ findAndReplace (marker, " Mode:" , " " );
293+ marker = trim (marker);
294+ if (marker == " C++" || marker == " c++" )
295+ return true ; // C++ marker found
296+
297+ // if (marker == "C" || marker == "c")
298+ // return false;
299+ std::cout << path << " - unmatched Emacs marker: '" << marker << " '" << ' \n ' ;
300+
301+ return false ; // marker is not a C++ one
302+ }
303+
304+ Standards::Language Path::identify (const std::string &path, bool cppProbe, bool *header)
249305{
250306 // cppcheck-suppress uninitvar - TODO: FP
251307 if (header)
252308 *header = false ;
253309
254310 std::string ext = getFilenameExtension (path);
311+ // standard library headers have no extension
312+ if (cppProbe && ext.empty ()) {
313+ if (hasEmacsCppMarker (path.c_str ())) {
314+ if (header)
315+ *header = true ;
316+ return Standards::Language::CPP;
317+ }
318+ }
255319 if (ext == " .C" )
256320 return Standards::Language::CPP;
257321 if (c_src_exts.find (ext) != c_src_exts.end ())
@@ -262,7 +326,9 @@ Standards::Language Path::identify(const std::string &path, bool *header)
262326 if (ext == " .h" ) {
263327 if (header)
264328 *header = true ;
265- return Standards::Language::C; // treat as C for now
329+ if (cppProbe && hasEmacsCppMarker (path.c_str ()))
330+ return Standards::Language::CPP;
331+ return Standards::Language::C;
266332 }
267333 if (cpp_src_exts.find (ext) != cpp_src_exts.end ())
268334 return Standards::Language::CPP;
@@ -277,7 +343,7 @@ Standards::Language Path::identify(const std::string &path, bool *header)
277343bool Path::isHeader2 (const std::string &path)
278344{
279345 bool header;
280- (void )Path:: identify (path, &header);
346+ (void )identify (path, false , &header);
281347 return header;
282348}
283349
0 commit comments