Hello!
I have noticed that the lcc-win32 linker cannot link resources under Wine.
It creates a temporary name using tmpnam() from msvcrt.dll, adds "obj" to
it and converts the resource file to it. Then it opens the file and
cannot determine it's type, i.e. whether it's an object file or something
else. It happens because Wine tmpnam() returns a name ending with
".tmp", so the resulting file has extension ".tmpobj" instead of ".tmp".
In fact, the native tmpnam() returns the name ending with "." only when
it's called the first time for the given process. I'd rather not rely on
this fact if I wrote lcclnk, but Wine can and should emulate tmpnam()
close enough to satisfy its callers.
I tested the native tmpnam() and found that it creates files in the
following format:
\sPPP.UUU
where PPP is base32 representation of the process id and UUU is base32
representation of the unique per-process counter. The file is guaranteed
not to exist in the root directory of the current drive. If the file
exists, next unique number is used. GetFileAttributesA() is used to check
if the file exists.
Base32 uses digits from 0 to 9 and letters from "a" to "v". It strips all
leading zeroes, even for 0, so the first file indeed ends with a dot.
Also, tmpnam() uses and returns its argument is it's not NULL and only
falls back to the static string for the NULL argument. This is documented
on MSDN.
MSDN says that TMP_MAX should be defined in stdio.h to 32767. I put it
there using the same precautions that are used for other potentially
conflicting symbols, e.g. FILENAME_MAX. If USE_MSVCRT_PREFIX is defined,
TMP_MAX is called MSVCRT_TMP_MAX.
A positive side effect of the change in tmpnam() is that tmpfile() creates
files in the root directory now (not in the temporary directory), as
documented on MSDN.
ChangeLog:
* dlls/msvcrt/file.c:
msvcrt_int_to_base32() - internal function to create base32
strings for temporary files.
MSVCRT_tmpnam() - complete rewrite. Use the same names as
the native version. Use the caller-supplied buffer if possible.
* include/msvcrt/stdio.h:
Define TMP_MAX and MSVCRT_TMP_MAX.
--
Regards,
Pavel Roskin
--- dlls/msvcrt/file.c
+++ dlls/msvcrt/file.c
@@ -262,6 +262,29 @@ static void msvcrt_alloc_buffer(MSVCRT_F
file->_cnt = 0;
}
+/* INTERNAL: Convert integer to base32 string (0-9a-v), 0 becomes "" */
+static void msvcrt_int_to_base32(int num, char *str)
+{
+ char *p;
+ int n = num;
+ int digits = 0;
+
+ while (n != 0)
+ {
+ n >>= 5;
+ digits++;
+ }
+ p = str + digits;
+ *p = 0;
+ while (--p >= str)
+ {
+ *p = (num & 31) + '0';
+ if (*p > '9')
+ *p += ('a' - '0' - 10);
+ num >>= 5;
+ }
+}
+
/*********************************************************************
* __p__iob(MSVCRT.@)
*/
@@ -2397,16 +2420,22 @@ void MSVCRT_setbuf(MSVCRT_FILE* file, ch
*/
char *MSVCRT_tmpnam(char *s)
{
- char tmpbuf[MAX_PATH];
- const char* prefix = "TMP";
- if (!GetTempPathA(MAX_PATH,tmpbuf) ||
- !GetTempFileNameA(tmpbuf,prefix,0,MSVCRT_tmpname))
- {
- TRACE(":failed-last error (%ld)\n",GetLastError());
- return NULL;
+ static int unique;
+ char tmpstr[16];
+ char *p;
+ int count;
+ if (s == 0)
+ s = MSVCRT_tmpname;
+ msvcrt_int_to_base32(GetCurrentProcessId(), tmpstr);
+ p = s + sprintf(s, "\\s%s.", tmpstr);
+ for (count = 0; count < MSVCRT_TMP_MAX; count++)
+ {
+ msvcrt_int_to_base32(unique++, tmpstr);
+ strcpy(p, tmpstr);
+ if (GetFileAttributesA(s) == INVALID_FILE_ATTRIBUTES &&
+ GetLastError() == ERROR_FILE_NOT_FOUND)
+ break;
}
- TRACE(":got tmpnam %s\n",MSVCRT_tmpname);
- s = MSVCRT_tmpname;
return s;
}
--- include/msvcrt/stdio.h
+++ include/msvcrt/stdio.h
@@ -65,6 +65,7 @@
#define EOF (-1)
#define FILENAME_MAX 260
+#define TMP_MAX 0x7fff
#define FOPEN_MAX 20
#define L_tmpnam 260
@@ -84,6 +85,7 @@
#define MSVCRT__IOLBF 0x0040
#define MSVCRT_FILENAME_MAX 260
+#define MSVCRT_TMP_MAX 0x7fff
#define MSVCRT_EOF (-1)