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,72 @@ 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+ // TODO: read first line only
260+ if (fgets (const_cast <char *>(buf.data ()), buf.size (), fp) == nullptr )
261+ return false ; // failed to read file
262+ // TODO: replace with regular expression
263+ const auto pos1 = buf.find (" -*-" );
264+ if (pos1 == std::string::npos)
265+ return false ; // no start marker
266+ const auto pos_nl = buf.find_first_of (" \r\n " );
267+ if (pos_nl != std::string::npos && (pos_nl < pos1))
268+ return false ; // not on first line
269+ const auto pos2 = buf.find (" -*-" , pos1 + 3 );
270+ // TODO: make sure we have read the whole line before bailing out
271+ if (pos2 == std::string::npos)
272+ return false ; // no end marker
273+ const std::string buf_trim = trim (buf); // trim whitespaces
274+ if (buf_trim[0 ] != ' /' || buf_trim[1 ] != ' /' )
275+ return false ; // not a comment
276+
277+ std::cout /* << path << " -*/ << " Emacs marker: '" << buf.substr (pos1, (pos2 + 3 ) - pos1) << " '" << std::endl;
278+
279+ // there are more variations with lowercase and no whitespaces
280+ // -*- C++ -*-
281+ // -*- Mode: C++; -*-
282+ // -*- Mode: C++; c-basic-offset: 8 -*-
283+ std::string marker = trim (buf.substr (pos1 + 3 , pos2 - pos1 - 3 ), " ;" );
284+ // cut off additional attributes
285+ const auto pos_semi = marker.find (' ;' );
286+ if (pos_semi != std::string::npos)
287+ marker.resize (pos_semi);
288+ findAndReplace (marker, " mode:" , " " );
289+ findAndReplace (marker, " Mode:" , " " );
290+ marker = trim (marker);
291+ if (marker == " C++" || marker == " c++" )
292+ return true ; // C++ marker found
293+
294+ // if (marker == "C" || marker == "c")
295+ // return false;
296+ std::cout << path << " - unmatched Emacs marker: '" << marker << " '" << std::endl;
297+
298+ return false ; // marker is not a C++ one
299+ }
300+
301+ Standards::Language Path::identify (const std::string &path, bool cppProbe, bool *header)
249302{
250303 // cppcheck-suppress uninitvar - TODO: FP
251304 if (header)
252305 *header = false ;
253306
254307 std::string ext = getFilenameExtension (path);
308+ // standard library headers have no extension
309+ if (cppProbe && ext.empty ()) {
310+ if (hasEmacsCppMarker (path.c_str ())) {
311+ if (header)
312+ *header = true ;
313+ return Standards::Language::CPP;
314+ }
315+ }
255316 if (ext == " .C" )
256317 return Standards::Language::CPP;
257318 if (c_src_exts.find (ext) != c_src_exts.end ())
@@ -262,7 +323,9 @@ Standards::Language Path::identify(const std::string &path, bool *header)
262323 if (ext == " .h" ) {
263324 if (header)
264325 *header = true ;
265- return Standards::Language::C; // treat as C for now
326+ if (cppProbe && hasEmacsCppMarker (path.c_str ()))
327+ return Standards::Language::CPP;
328+ return Standards::Language::C;
266329 }
267330 if (cpp_src_exts.find (ext) != cpp_src_exts.end ())
268331 return Standards::Language::CPP;
@@ -277,7 +340,7 @@ Standards::Language Path::identify(const std::string &path, bool *header)
277340bool Path::isHeader2 (const std::string &path)
278341{
279342 bool header;
280- (void )Path:: identify (path, &header);
343+ (void )identify (path, false , &header);
281344 return header;
282345}
283346
0 commit comments