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,59 @@ 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+ FILE *fp = fopen (path, " rt" );
255+ if (!fp)
256+ return false ;
257+ std::unique_ptr<FILE, decltype (&fclose)> fp_deleter (fp, fclose);
258+ std::string buf (1024 , ' \0 ' );
259+ if (fgets (const_cast <char *>(buf.data ()), buf.size (), fp) == nullptr )
260+ return false ;
261+ // TODO: check if line is a comment
262+ const auto pos1 = buf.find (" -*-" );
263+ if (pos1 == std::string::npos)
264+ return false ;
265+ const auto pos2 = buf.find (" -*-" , pos1 + 3 );
266+ // TODO: make sure we read the whole line before bailing out
267+ if (pos2 == std::string::npos)
268+ return false ;
269+
270+ std::cout /* << path << " -*/ << " Emacs marker: '" << buf.substr (pos1, (pos2 + 3 ) - pos1) << " '" << std::endl;
271+
272+ // there are more variations with lowercase and no whitespaces
273+ // -*- C++ -*-
274+ // -*- Mode: C++; -*-
275+ // -*- Mode: C++; c-basic-offset: 8 -*-
276+ const auto marker = trim (buf.substr (pos1 + 3 , pos2 - pos1 - 3 ), " " );
277+ // TODO: support extended format with "mode: C++;" and "Mode: C++;"
278+ if (marker == " C++" || marker == " c++" )
279+ return true ;
280+
281+ // if (marker == "C" || marker == "c")
282+ // return false;
283+ std::cout << path << " - unmatched Emacs marker: '" << marker << " '" << std::endl;
284+
285+ return false ;
286+ }
287+
288+ Standards::Language Path::identify (const std::string &path, bool cppProbe, bool *header)
249289{
250290 // cppcheck-suppress uninitvar - TODO: FP
251291 if (header)
252292 *header = false ;
253293
254294 std::string ext = getFilenameExtension (path);
295+ // standard library headers have no extension
296+ if (cppProbe && ext.empty ()) {
297+ if (hasEmacsCppMarker (path.c_str ())) {
298+ if (header)
299+ *header = true ;
300+ return Standards::Language::CPP;
301+ }
302+ }
255303 if (ext == " .C" )
256304 return Standards::Language::CPP;
257305 if (c_src_exts.find (ext) != c_src_exts.end ())
@@ -262,7 +310,9 @@ Standards::Language Path::identify(const std::string &path, bool *header)
262310 if (ext == " .h" ) {
263311 if (header)
264312 *header = true ;
265- return Standards::Language::C; // treat as C for now
313+ if (cppProbe && hasEmacsCppMarker (path.c_str ()))
314+ return Standards::Language::CPP;
315+ return Standards::Language::C;
266316 }
267317 if (cpp_src_exts.find (ext) != cpp_src_exts.end ())
268318 return Standards::Language::CPP;
@@ -277,7 +327,7 @@ Standards::Language Path::identify(const std::string &path, bool *header)
277327bool Path::isHeader2 (const std::string &path)
278328{
279329 bool header;
280- (void )Path:: identify (path, &header);
330+ (void )identify (path, false , &header);
281331 return header;
282332}
283333
0 commit comments