This commit is contained in:
Renan Ribeiro 2024-05-01 00:19:45 +03:00 committed by GitHub
commit 7a3762148f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 339 additions and 15 deletions

View File

@ -1681,6 +1681,16 @@ void somefunc2(int, char *);
</verb></tscreen>
<sect1><tt>#pragma once</tt><label id="pragma-once"><p>
This pragma is used to prevent multiple inclusion of a header file.
If this pragma is preprocessed in a file, any posterior
<tt>#include</tt> which targets the current file will be ignored.
Two files are considered to be the same by this directive if they
share the same absolute path. In the case of symbolic links,
the canonical, resolved path is considered instead.
<sect>Register variables<label id="register-vars"><p>

View File

@ -45,6 +45,7 @@
#include "print.h"
#include "strbuf.h"
#include "xmalloc.h"
#include "pathutil.h"
/* cc65 */
#include "codegen.h"
@ -296,10 +297,11 @@ void OpenMainFile (const char* Name)
void OpenIncludeFile (const char* Name, InputType IT)
void OpenIncludeFile (const char* Name, InputType IT, StringPool *FilesToIgnore)
/* Open an include file and insert it into the tables. */
{
char* N;
char* M;
FILE* F;
IFile* IF;
AFile* AF;
@ -317,6 +319,23 @@ void OpenIncludeFile (const char* Name, InputType IT)
return;
}
/* Resolve real path of file in case of a symlink */
M = FindRealPath(N);
if (M == 0) {
PPError ("Cannot resolve absolute path of '%s'", N);
xfree (N);
return;
}
if (SP_LookupStr(FilesToIgnore, M) != 0) {
/* This file should not be included. */
xfree (M);
xfree (N);
return;
}
xfree (M);
/* Search the list of all input files for this file. If we don't find
** it, create a new IFile object.
*/

View File

@ -43,6 +43,7 @@
/* common */
#include "coll.h"
#include "strbuf.h"
#include "strpool.h"
@ -83,8 +84,10 @@ extern char NextC;
void OpenMainFile (const char* Name);
/* Open the main file. Will call Fatal() in case of failures. */
void OpenIncludeFile (const char* Name, InputType IT);
/* Open an include file and insert it into the tables. */
void OpenIncludeFile (const char* Name, InputType IT, StringPool* FilesToIgnore);
/* Open an include file and insert it into the tables.
** Does nothing if the resolved file is present in the pool.
*/
void CloseIncludeFile (void);
/* Close an include file and switch to the higher level file. Set Input to

View File

@ -44,6 +44,9 @@
#include "inline.h"
#include "print.h"
#include "xmalloc.h"
#include "strpool.h"
#include "abend.h"
#include "pathutil.h"
/* cc65 */
#include "codegen.h"
@ -113,6 +116,8 @@ static StrBuf* PLine; /* Buffer for macro expansion */
static StrBuf* MLine; /* Buffer for macro expansion in #pragma */
static StrBuf* OLine; /* Buffer for #pragma output */
static StringPool* PragmaOnceSeenFiles;
/* Newlines to be added to preprocessed text */
static unsigned PendingNewLines;
static unsigned ContinuedLines;
@ -2804,7 +2809,6 @@ static int DoIfDef (int skip, int flag)
}
static void DoInclude (void)
/* Open an include file. */
{
@ -2854,8 +2858,8 @@ static void DoInclude (void)
NextChar ();
/* Check for extra tokens following the filename */
CheckExtraTokens ("include");
/* Open the include file */
OpenIncludeFile (SB_GetConstBuf (&Filename), IT);
/* Open the include file, if it is not marked with #pragma once */
OpenIncludeFile (SB_GetConstBuf (&Filename), IT, PragmaOnceSeenFiles);
} else {
/* No terminator found */
PPError ("#include expects \"FILENAME\" or <FILENAME>");
@ -2963,14 +2967,28 @@ static void DoLine (void)
MLine = InitLine (MLine);
}
static void DoPragmaOnce (void)
/* Marks the current file as seen by #pragma once. */
{
const char * const Filename = GetCurrentFilename ();
char * const FullPath = FindRealPath (Filename);
if (FullPath == NULL) {
AbEnd ("Failed to find the real path for the file %s", Filename);
}
SP_AddStr (PragmaOnceSeenFiles, FullPath);
free (FullPath);
}
static void DoPragma (void)
/* Handle a #pragma line by converting the #pragma preprocessor directive into
** the _Pragma() compiler operator.
*/
{
StrBuf* PragmaLine = OLine;
StrBuf* const PragmaLine = OLine;
PRECONDITION (PragmaLine != 0);
@ -2981,11 +2999,16 @@ static void DoPragma (void)
SB_Clear (MLine);
PreprocessDirective (Line, MLine, MSM_NONE);
/* Convert #pragma to _Pragma () */
SB_AppendStr (PragmaLine, "_Pragma (");
SB_Reset (MLine);
Stringize (MLine, PragmaLine);
SB_AppendChar (PragmaLine, ')');
if (SB_CompareStr(MLine, "once") == 0) {
DoPragmaOnce ();
}
else {
/* Convert #pragma to _Pragma () */
SB_AppendStr (PragmaLine, "_Pragma (");
SB_Reset (MLine);
Stringize (MLine, PragmaLine);
SB_AppendChar (PragmaLine, ')');
}
/* End this line */
SB_SetIndex (PragmaLine, SB_GetLen (PragmaLine));
@ -3157,6 +3180,7 @@ static int ParseDirectives (unsigned ModeFlags)
if (!PPSkip) {
if ((ModeFlags & MSM_IN_ARG_LIST) == 0) {
DoPragma ();
return Whitespace;
} else {
PPError ("Embedded #pragma directive within macro arguments is unsupported");
@ -3348,6 +3372,9 @@ void InitPreprocess (void)
/* Create the output buffers */
MLine = NewStrBuf ();
PLine = NewStrBuf ();
/* 64 is a sensible number of slots for the hash table */
PragmaOnceSeenFiles = NewStringPool(64);
}
@ -3358,6 +3385,7 @@ void DonePreprocess (void)
/* Done with the output buffers */
SB_Done (MLine);
SB_Done (PLine);
FreeStringPool(PragmaOnceSeenFiles);
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@ -110,6 +110,7 @@
<ClInclude Include="common\objdefs.h" />
<ClInclude Include="common\optdefs.h" />
<ClInclude Include="common\print.h" />
<ClInclude Include="common\pathutil.h" />
<ClInclude Include="common\scopedefs.h" />
<ClInclude Include="common\searchpath.h" />
<ClInclude Include="common\segdefs.h" />
@ -155,6 +156,7 @@
<ClCompile Include="common\matchpat.c" />
<ClCompile Include="common\mmodel.c" />
<ClCompile Include="common\print.c" />
<ClCompile Include="common\pathutil.c" />
<ClCompile Include="common\searchpath.c" />
<ClCompile Include="common\segnames.c" />
<ClCompile Include="common\shift.c" />
@ -171,4 +173,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

119
src/common/pathutil.c Normal file
View File

@ -0,0 +1,119 @@
/*****************************************************************************/
/* */
/* pathutil.c */
/* Path manipulation utilities */
/* */
/* */
/* */
/* (C) 2003-2008 Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#if defined(_WIN32)
#if !defined(_WIN32_WINNT) && !defined(NTDDI_VERSION)
/* Set minimum windows version for GetFinalPathNameByHandleA */
/* NTDDI_VISTA */
#define NTDDI_VERSION 0x06000000
/* _WIN32_WINNT_VISTA */
#define _WIN32_WINNT 0x600
#endif
#include "xmalloc.h"
#include <windows.h>
#include <fileapi.h>
#endif
#include <stdlib.h>
/*****************************************************************************/
/* code */
/*****************************************************************************/
#if defined(_WIN32)
char *FindRealPath (const char *Path)
/*
** Returns a malloced buffer containing the canonical path of the given path.
** If the path points to a non-existent file, or if any error occurs, NULL is returned.
** If the path points to a symlink, the resolved symlink path is returned.
** Note: The returned path's separator is system specific.
*/
{
HANDLE Handle = CreateFileA (Path,
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ |
FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (Handle == INVALID_HANDLE_VALUE) {
return NULL;
}
size_t BufferSize = MAX_PATH + 10;
char* Buffer = xmalloc (BufferSize);
DWORD Status = GetFinalPathNameByHandleA (Handle,
Buffer,
BufferSize,
FILE_NAME_NORMALIZED
| VOLUME_NAME_DOS);
if (Status == 0) {
xfree (Buffer);
CloseHandle (Handle);
return NULL;
}
CloseHandle (Handle);
return Buffer;
}
#else
char* FindRealPath (const char* path)
/*
** Returns a malloced buffer containing the canonical path of the given path.
** If the path points to a non-existent file, or if any error occurs, NULL is returned.
** If the path points to a symlink, the resolved symlink path is returned.
** Note: The returned path's separator is system specific.
*/
{
return realpath (path, NULL);
}
#endif

55
src/common/pathutil.h Normal file
View File

@ -0,0 +1,55 @@
/*****************************************************************************/
/* */
/* pathutil.h */
/* Path manipulation utilities */
/* */
/* */
/* */
/* (C) 2003-2008 Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#ifndef PATHUTIL_H
#define PATHUTIL_H
/*****************************************************************************/
/* Code */
/*****************************************************************************/
char *FindRealPath (const char *path);
/*
** Returns a malloced buffer containing the canonical path of the given path.
** If the path points to a non-existent file, or if any error occurs, NULL is returned.
** If the path points to a symlink, the resolved symlink path is returned.
** Note: The returned path's separator is system specific.
*/
/* End of pathutil.h */
#endif

View File

@ -377,6 +377,7 @@ char* SearchFile (const SearchPaths* P, const char* File)
SB_AppendStr (&PathName, File);
SB_Terminate (&PathName);
/* Check if this file exists */
if (access (SB_GetBuf (&PathName), 0) == 0) {
/* The file exists, we're done */

View File

@ -281,3 +281,22 @@ unsigned SP_GetCount (const StringPool* P)
{
return CollCount (&P->Entries);
}
unsigned SP_Lookup(StringPool *P, const StrBuf *S)
/*
** Determine whether the given string is in the pool.
** Returns 1 if the string is in the pool, 0 otherwise.
*/
{
return HT_Find (&P->Tab, S) != 0;
}
unsigned SP_LookupStr(StringPool *P, const char *S)
/*
** Determine whether the given string is in the pool.
** Returns 1 if the string is in the pool, 0 otherwise.
*/
{
StrBuf Buf;
return SP_Lookup (P, SB_InitFromString (&Buf, S));
}

View File

@ -84,7 +84,7 @@ const StrBuf* SP_Get (const StringPool* P, unsigned Index);
unsigned SP_Add (StringPool* P, const StrBuf* S);
/* Add a string buffer to the buffer and return the index. If the string does
** already exist in the pool, SP_AddBuf will just return the index of the
** already exist in the pool, SP_Add will just return the index of the
** existing string.
*/
@ -96,6 +96,18 @@ unsigned SP_AddStr (StringPool* P, const char* S);
unsigned SP_GetCount (const StringPool* P);
/* Return the number of strings in the pool */
unsigned SP_Lookup(StringPool *P, const StrBuf *S);
/*
** Determine whether the given string is in the pool.
** Returns 1 if the string is in the pool, 0 otherwise.
*/
unsigned SP_LookupStr(StringPool *P, const char *S);
/*
** Determine whether the given string is in the pool.
** Returns 1 if the string is in the pool, 0 otherwise.
*/
/* End of strpool.h */

View File

@ -0,0 +1,8 @@
/*
** !!DESCRIPTION!! Simple #pragma once directive tests
** !!ORIGIN!! cc65 regression tests
** !!LICENCE!! Public Domain
*/
#include "pragma-once-sample.h";

View File

@ -0,0 +1,21 @@
/*
** !!DESCRIPTION!! Simple #pragma once directive tests
** !!ORIGIN!! cc65 regression tests
** !!LICENCE!! Public Domain
*/
#ifdef FILE_INCLUDED
#error "This file should not have been included twice"
#define INCLUDED_TWICE
#else
#define FILE_INCLUDED
#endif
/* a pragma once directive should work regardless of where it is located in
the file, as long as it is seen by the preprocessor */
#pragma once

View File

@ -0,0 +1,27 @@
/*
** !!DESCRIPTION!! Simple #pragma once directive tests
** !!ORIGIN!! cc65 regression tests
** !!LICENCE!! Public Domain
*/
#pragma once
#include "pragma-once-sample-2.h"
#include "pragma-once-sample-2.h"
#include "pragma-once-sample.h"
#include "pragma-once-sample.h"
#include <stdio.h>
int main() {
#ifdef INCLUDED_TWICE
printf("pragma-once-sample.h included more than once\n");
return 1;
#else
printf("pragma-once-sample included once\n");
return 0;
#endif
}