• R/O
  • HTTP
  • SSH
  • HTTPS

mingw-org-wsl: Commit

The MinGW.org Windows System Libraries


Commit MetaInfo

Revision2c98d6af72c886e5821b3717bfa25efea5147e39 (tree)
Time2018-12-05 04:00:29
AuthorKeith Marshall <keith@user...>
CommiterKeith Marshall

Log Message

Reimplement Win9x specific fseek()/fwrite() redirector.

Change Summary

Incremental Difference

--- a/mingwrt/ChangeLog
+++ b/mingwrt/ChangeLog
@@ -1,5 +1,28 @@
11 2018-12-04 Keith Marshall <keith@users.osdn.me>
22
3+ Reimplement Win9x specific fseek()/fwrite() redirector.
4+
5+ * include/stdio.h [__USE_MINGW_FSEEK]: Deprecate support for direct
6+ user definition; deduce it, as an internal private definition, from...
7+ [_WIN32_WINDOWS]: ...this, when appropriately defined for Win9x.
8+ [__USE_MINGW_FSEEK] (__mingw_fseek): Declare new prototype; map it as
9+ multiple __CRT_ALIAS inline function overrides for each of...
10+ (fseek, _fseeki64, fseeko64): ...these.
11+ [__USE_MINGW_FSEEK] (__mingw_fwrite): Likewise, overriding...
12+ (fwrite): ...this.
13+ (__mingw_fseeko64): Delete all references; it is no longer provided,
14+ nor required.
15+
16+ * mingwex/stdio/fwrite.c: New file; it reimplements the Win9x
17+ fseek()/fwrite() function redirector interface, and thus replaces...
18+ * mingex/ming-fseek.c: ...this; delete it.
19+
20+ * Makefile.in (libmingwex.a): Remove dependency on...
21+ (mingw-fseek.$OBJEXT): ...this object module; replace it with...
22+ (fwrite.$OBJEXT): ...this alternative dependency.
23+
24+2018-12-04 Keith Marshall <keith@users.osdn.me>
25+
326 Implement POSIX.1-1996 linked-list queue management API.
427
528 * mingwex/insque.c mingwex/remque.c: New files; they implement the
--- a/mingwrt/Makefile.in
+++ b/mingwrt/Makefile.in
@@ -446,7 +446,7 @@ $(addsuffix fmt.$(OBJEXT),varo crto geto seto crtn getn setn): %.$(OBJEXT): ofmt
446446
447447 # Some additional miscellaneous functions, in libmingwex.a
448448 #
449-libmingwex.a: $(addsuffix .$(OBJEXT), mingw-aligned-malloc mingw-fseek)
449+libmingwex.a: $(addsuffix .$(OBJEXT), mingw-aligned-malloc fwrite)
450450 libmingwex.a: $(addsuffix .$(OBJEXT), glob getopt basename dirname nsleep)
451451 libmingwex.a: $(addsuffix .$(OBJEXT), clockapi clockres clockset clocktime)
452452 libmingwex.a: $(addsuffix .$(OBJEXT), insque remque tdelete tfind tsearch twalk)
--- a/mingwrt/include/stdio.h
+++ b/mingwrt/include/stdio.h
@@ -742,14 +742,37 @@ _CRTIMP __cdecl __MINGW_NOTHROW void rewind (FILE *);
742742
743743 #ifdef __USE_MINGW_FSEEK
744744 /* Workaround for a limitation on Win9x where a file is not zero padded
745- * on write, following a seek beyond the original end of file; these are
746- * implemented in libmingwex.a
745+ * on write, following a seek beyond the original end of file; supporting
746+ * redirector functions are implemented in libmingwex.a
747+ *
748+ * Note: this is improper usage. __USE_MINGW_FSEEK exhibits the form of a
749+ * private (system reserved) feature test macro; as such, users should not
750+ * define it directly, and thus, it really should not have been defined at
751+ * this point; discourage this practice.
747752 */
748-__cdecl __MINGW_NOTHROW int __mingw_fseek (FILE *, long, int);
749-__cdecl __MINGW_NOTHROW size_t __mingw_fwrite (const void *, size_t, size_t, FILE *);
753+#warning "The __USE_MINGW_FSEEK feature test is deprecated"
754+#pragma info "Define _WIN32_WINDOWS, instead of __USE_MINGW_FSEEK"
755+
756+#elif _WIN32_WINDOWS >= _WIN32_WINDOWS_95 && _WIN32_WINDOWS < _WIN32_WINNT_WIN2K
757+/* This is correct usage; the private __USE_MINGW_FSEEK feature affects only
758+ * Win9x, so enable it implicitly when the _WIN32_WINDOWS feature is specified,
759+ * thus indicating the user's intent to target a Win9x platform.
760+ */
761+#define __USE_MINGW_FSEEK
762+#endif
750763
751-#define fwrite(buffer, size, count, fp) __mingw_fwrite(buffer, size, count, fp)
752-#define fseek(fp, offset, whence) __mingw_fseek(fp, offset, whence)
764+#ifdef __USE_MINGW_FSEEK
765+/* Regardless of how it may have become defined, when __USE_MINGW_FSEEK has
766+ * been defined, we must redirect calls to fseek() and fwrite(), so that the
767+ * Win9x zero padding limitation can be mitigated.
768+ */
769+__cdecl __MINGW_NOTHROW int __mingw_fseek (FILE *, __off64_t, int);
770+__CRT_ALIAS int fseek( FILE *__fp, long __offset, int __whence )
771+{ return __mingw_fseek( __fp, (__off64_t)(__offset), __whence ); }
772+
773+__cdecl __MINGW_NOTHROW size_t __mingw_fwrite (const void *, size_t, size_t, FILE *);
774+__CRT_ALIAS size_t fwrite( const void *__buf, size_t __len, size_t __cnt, FILE *__fp )
775+{ return __mingw_fwrite( __buf, __len, __cnt, __fp ); }
753776 #endif /* __USE_MINGW_FSEEK */
754777
755778 /* An opaque data type used for storing file positions... The contents
@@ -965,12 +988,11 @@ int __cdecl __MINGW_NOTHROW fseeko64 (FILE *, __off64_t, int);
965988 * argument, (and both types are effectively 64-bit signed ints anyway),
966989 * the same wrapper will suffice for both.
967990 */
968-int __cdecl __MINGW_NOTHROW __mingw_fseeko64 (FILE *, __off64_t, int);
969-__CRT_ALIAS int __cdecl __MINGW_NOTHROW fseeko64 (FILE *__f, __off64_t __o, int __w)
970-{ return __mingw_fseeko64 (__f, __o, __w); }
991+__CRT_ALIAS int _fseeki64( FILE *__fp, __int64 __offset, int __whence )
992+{ return __mingw_fseek( __fp, (__off64_t)(__offset), __whence ); }
971993
972-__CRT_ALIAS int __cdecl __MINGW_NOTHROW _fseeki64 (FILE *__f, __off64_t __o, int __w)
973-{ return __mingw_fseeko64 (__f, (__off64_t)(__o), __w); }
994+__CRT_ALIAS int fseeko64( FILE *__fp, __off64_t __offset, int __whence )
995+{ return __mingw_fseek( __fp, __offset, __whence ); }
974996 #endif
975997
976998 __off64_t __cdecl __MINGW_NOTHROW ftello64 (FILE *);
--- a/mingwrt/mingwex/mingw-fseek.c
+++ /dev/null
@@ -1,138 +0,0 @@
1-/*
2- * mingw-fseek.c
3- *
4- * Workaround for limitations on Win9x where extended file content
5- * is not zeroed out if you seek past the end and then write.
6- *
7- * $Id$
8- *
9- * Written by Mumit Khan <khan@xraylith.wisc.edu>
10- * Copyright (C) 1999, 2002-2005, 2011, 2015, MinGW.org Project.
11- *
12- * Abstracted from MinGW local patch to binutils/bfd/libbfd.c
13- *
14- *
15- * Permission is hereby granted, free of charge, to any person obtaining a
16- * copy of this software and associated documentation files (the "Software"),
17- * to deal in the Software without restriction, including without limitation
18- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19- * and/or sell copies of the Software, and to permit persons to whom the
20- * Software is furnished to do so, subject to the following conditions:
21- *
22- * The above copyright notice, this permission notice, and the following
23- * disclaimer shall be included in all copies or substantial portions of
24- * the Software.
25- *
26- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
32- * DEALINGS IN THE SOFTWARE.
33- *
34- */
35-#define WIN32_LEAN_AND_MEAN
36-
37-#include <windows.h>
38-#include <stdio.h>
39-#include <io.h>
40-#include <stdlib.h>
41-
42-#define ZEROBLOCKSIZE 512
43-static int __mingw_fseek_called;
44-
45-/* The fseek in Win9x runtime does not zero out the file if seeking past
46- * the end; if you don't want random stuff from your disk included in your
47- * output DLL/executable, use this version instead. On WinNT/Win2k, it
48- * just calls runtime fseek().
49- *
50- * CHECK/FIXME: Does this work for both text and binary modes??
51- */
52-int
53-__mingw_fseek (FILE *fp, long offset, int whence)
54-{
55-# undef fseek
56- __mingw_fseek_called = 1;
57- return fseek (fp, offset, whence);
58-}
59-
60-int
61-__mingw_fseeko64 (FILE *fp, __off64_t offset, int whence)
62-{
63-# undef fseeko64
64- __mingw_fseek_called = 1;
65- return fseeko64 (fp, offset, whence);
66-}
67-
68-size_t
69-__mingw_fwrite (const void *buffer, size_t size, size_t count, FILE *fp)
70-{
71-# undef fwrite
72- if ((_osver & 0x8000) && __mingw_fseek_called)
73- {
74- ULARGE_INTEGER actual_length;
75- LARGE_INTEGER current_position = {{0LL}};
76- __mingw_fseek_called = 0;
77- fflush (fp);
78- actual_length.u.LowPart = GetFileSize
79- ( (HANDLE) _get_osfhandle (fileno (fp)), &actual_length.u.HighPart
80- );
81- if (actual_length.u.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR )
82- return -1;
83- current_position.u.LowPart = SetFilePointer
84- ( (HANDLE) _get_osfhandle (fileno (fp)), current_position.u.LowPart,
85- &current_position.u.HighPart, FILE_CURRENT
86- );
87- if (current_position.u.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR )
88- return -1;
89-
90-# ifdef DEBUG
91- printf( "__mingw_fwrite: current %I64u, actual %I64u\n",
92- current_position.QuadPart, actual_length.QuadPart
93- );
94-# endif /* DEBUG */
95- if( current_position.QuadPart > actual_length.QuadPart )
96- {
97- static char __mingw_zeros[ZEROBLOCKSIZE];
98- long long numleft;
99-
100- SetFilePointer( (HANDLE) _get_osfhandle (fileno (fp)), 0, 0, FILE_END );
101- numleft = current_position.QuadPart - actual_length.QuadPart;
102-
103-# ifdef DEBUG
104- printf( "__mingw_fwrite: Seeking %I64d bytes past end\n", numleft );
105-# endif /* DEBUG */
106- while( numleft > 0LL )
107- {
108- DWORD nzeros = (numleft > ZEROBLOCKSIZE)
109- ? ZEROBLOCKSIZE
110- : numleft;
111- DWORD written;
112- if( ! WriteFile ((HANDLE) _get_osfhandle (fileno (fp)),
113- __mingw_zeros, nzeros, &written, NULL)
114- )
115- { /* Best we can hope for, or at least DJ says so.
116- */
117- SetFilePointer( (HANDLE) _get_osfhandle (fileno (fp)),
118- 0, 0, FILE_BEGIN
119- );
120- return -1;
121- }
122- if( written < nzeros )
123- {
124- /* Likewise. */
125- SetFilePointer( (HANDLE) _get_osfhandle (fileno (fp)),
126- 0, 0, FILE_BEGIN
127- );
128- return -1;
129- }
130- numleft -= written;
131- }
132- FlushFileBuffers ((HANDLE) _get_osfhandle (fileno (fp)));
133- }
134- }
135- return (fwrite)(buffer, size, count, fp);
136-}
137-
138-/* $RCSfile$: end of file */
--- /dev/null
+++ b/mingwrt/mingwex/stdio/fwrite.c
@@ -0,0 +1,275 @@
1+/*
2+ * fwrite.c
3+ *
4+ * Workaround for limitations on Win9x where extended file content
5+ * is not zeroed out if you seek past the end and then write.
6+ *
7+ *
8+ * $Id$
9+ *
10+ * Written by Keith Marshall <keith@users.osdn.me>
11+ * Copyright (C) 2018, MinGW.org Project.
12+ *
13+ *
14+ * Replaces mingw-fseek.c implementation
15+ * Written by Mumit Khan <khan@xraylith.wisc.edu>
16+ * Copyright (C) 1999, 2002-2005, 2011, 2015, MinGW.org Project.
17+ *
18+ * Originally abstracted from MinGW local patch to binutils/bfd/libbfd.c
19+ *
20+ *
21+ * Permission is hereby granted, free of charge, to any person obtaining a
22+ * copy of this software and associated documentation files (the "Software"),
23+ * to deal in the Software without restriction, including without limitation
24+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
25+ * and/or sell copies of the Software, and to permit persons to whom the
26+ * Software is furnished to do so, subject to the following conditions:
27+ *
28+ * The above copyright notice, this permission notice, and the following
29+ * disclaimer shall be included in all copies or substantial portions of
30+ * the Software.
31+ *
32+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
33+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
35+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
37+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
38+ * DEALINGS IN THE SOFTWARE.
39+ *
40+ *
41+ * Although specifically providing support for Win9x, we must compile this
42+ * with the associated feature test disabled, so that the wrappers provided
43+ * herein can access the underlying functions which they wrap.
44+ *
45+ */
46+#undef _WIN32_WINDOWS
47+#undef __USE_MINGW_FSEEK
48+
49+#include <io.h>
50+#include <search.h>
51+#include <stdlib.h>
52+#include <stdio.h>
53+
54+/* The fseek() handler control structures.
55+ */
56+static void fseek_handler_init( FILE * );
57+struct fseek_pending { struct fseek_pending *fwd, *bkwd; FILE *fp; };
58+static struct { struct fseek_pending *avail, *active; void (*action)(FILE *); }
59+fseek_handler = { NULL, NULL, fseek_handler_init };
60+
61+/* The fseek() handler function redirector implementation.
62+ */
63+static __attribute__((__noinline__))
64+void *fseek_handler_trap_pending( FILE *fp )
65+{ /* Local helper, to check for any pending fseek trap associated
66+ * with "fp"; used from multiple locations, so we prefer to deny
67+ * GCC any choice to in-line it.
68+ *
69+ * Note that, if "fseek_handler.active" is NULL, there are no
70+ * pending stream traps, so there is nothing to check...
71+ */
72+ if( fseek_handler.active != NULL )
73+ { /* ...but when there is at least one active trap...
74+ */
75+ struct fseek_pending *trap = fseek_handler.active;
76+ do { /* ...we walk the queue of active traps, until we find
77+ * (and immediately return) one associated with "fp", or
78+ * we have examined all entries in the circular queue.
79+ */
80+ if( trap->fp == fp ) return trap;
81+ } while( (trap = trap->fwd) != fseek_handler.active );
82+ }
83+ /* If we get to here, there is no trap associated with "fp".
84+ */
85+ return NULL;
86+}
87+
88+/* On WinNT, the fseek() handler is required to take no action; by
89+ * installing the following "no-op" handler, on first-time call, we
90+ * reduce the fseek() and fwrite() wrappers to operate as simple
91+ * pass-through filters...
92+ */
93+static void fseek_handler_nop( FILE *fp __attribute__((__unused__)) ){}
94+
95+/* ...whereas, on Win9x, we install this active fseek() handler.
96+ */
97+static void fseek_handler_set_trap( FILE *fp )
98+{ /* This records fseek() requests, on a per-stream basis, such that
99+ * any subsequent fwrite() request can apply corrective action, to
100+ * ensure that any "holes" in the file stream are properly filled
101+ * with zeros, in the event that the fseek() has moved the file
102+ * pointer beyond EOF; however, we never assign more than one
103+ * active trap per stream.
104+ */
105+ if( fseek_handler_trap_pending( fp ) == NULL )
106+ { /* There is no active trap currently associated with "fp"; take
107+ * an unused trap record from the "avail" queue...
108+ */
109+ struct fseek_pending *avail;
110+ if( (avail = fseek_handler.avail) == NULL )
111+ { /* ...creating a new block of eight such records, if none are
112+ * currently available...
113+ */
114+ int avail_index = 8;
115+ avail = malloc( 8 * sizeof (struct fseek_pending) );
116+
117+ /* ...and linking them into a linear queue.
118+ */
119+ avail->fwd = avail->bkwd = NULL;
120+ while( --avail_index > 0 ) insque( avail + avail_index, avail );
121+ }
122+ /* The taken record is popped from the front of the queue; thus
123+ * we must follow its forward link, to update the front-of-queue
124+ * pointer to the next available unused trap record.
125+ */
126+ remque( avail ); fseek_handler.avail = avail->fwd;
127+
128+ /* Now, we must insert the "avail" record we've just acquired
129+ * into the "active" queue; this is managed as a circular queue,
130+ * so, if it is currently empty...
131+ */
132+ if( fseek_handler.active == NULL )
133+ /* ...then we must assign this new record as its sole entry,
134+ * with both links referring to itself...
135+ */
136+ fseek_handler.active = avail->fwd = avail->bkwd = avail;
137+
138+ /* ...otherwise, the POSIX.1 insque() API will take care of it.
139+ */
140+ else insque( avail, fseek_handler.active );
141+
142+ /* Finally, we must associate this new trap record with "fp".
143+ */
144+ avail->fp = fp;
145+ }
146+}
147+
148+static void fseek_handler_init( FILE *fp )
149+{ /* fseek() handler initialization routine; invoked only the first time
150+ * that the fseek() handler itself is invoked, it checks whether we are
151+ * running on Win9x, and if not...
152+ */
153+ if( (_osver & 0x8000) == 0 )
154+ /* ...it installs a no-op handler for future calls, (since WinNT
155+ * doesn't require any further handling...
156+ */
157+ fseek_handler.action = fseek_handler_nop;
158+
159+ else
160+ { /* ...otherwise, it installs the Win9x specific handler, which
161+ * traps seek requests to enable corrective action, which may be
162+ * required in a subsequent fwrite() call...
163+ */
164+ fseek_handler.action = fseek_handler_set_trap;
165+
166+ /* ...and immediately sets a trap for this first-time call.
167+ */
168+ fseek_handler_set_trap( fp );
169+ }
170+}
171+
172+/* Public API entry to the Win9x function redirector for the system
173+ * fseek() APIs; implemented in terms of fseeko64(), it is suitable as
174+ * a transparent wrapper for any of the fseek()-alike functions.
175+ */
176+int __mingw_fseek( FILE *fp, __off64_t offset, int whence )
177+{ fseek_handler.action( fp ); return fseeko64( fp, offset, whence ); }
178+
179+static __off64_t fseek_handler_reset( struct fseek_pending *trap )
180+{ /* Bridging function, called exclusively by __mingw_fwrite(), to disarm
181+ * any fseek trap which it has identified as being active and associated
182+ * with its target stream; since it is called only from one location, we
183+ * may safely allow GCC to in-line it.
184+ */
185+ if( trap == fseek_handler.active )
186+ { /* The active trap is currently the lead entry, in the active trap
187+ * queue, then we must move the lead entry onward; if it continues
188+ * to refer to the same trap, then we must clear the queue...
189+ */
190+ if( fseek_handler.active->fwd == trap ) fseek_handler.active = NULL;
191+
192+ /* ...otherwise, we simply remove the trap entry from the queue...
193+ */
194+ else remque( trap );
195+ }
196+ /* ...and likewise, if the trap entry is not the queue's lead entry.
197+ */
198+ else remque( trap );
199+
200+ /* Having removed the trap from the "active" queue, we must return it
201+ * to the "avail" queue...
202+ */
203+ insque( trap, fseek_handler.avail );
204+
205+ /* ...and, if that queue was previously empty, update its lead entry
206+ * reference pointer to match.
207+ */
208+ if( fseek_handler.avail == NULL ) fseek_handler.avail = trap;
209+
210+ /* Finally, tell fwrite() where it must begin data transfer, after it
211+ * has completed any Win9x corrective action which may be required.
212+ */
213+ return __mingw_ftelli64( trap->fp );
214+}
215+
216+size_t __mingw_fwrite( const void *buffer, size_t size, size_t count, FILE *fp )
217+{ /* A wrapper around the system fwrite() API; it ensures that padding
218+ * zero bytes are inserted, following EOF, when fwrite() is called on
219+ * Win9x, after any seek request which moves the file pointer to any
220+ * position which lies beyond the existing EOF.
221+ */
222+ struct fseek_pending *trap;
223+ if( (trap = fseek_handler_trap_pending( fp )) != NULL )
224+ { /* The fseek handler has determined that we are running on Win9x,
225+ * and that this fwrite operation was preceded by a seek; we don't
226+ * yet know if that seek has moved the position beyond the current
227+ * end of file, in which case Win9x may leave random garbage after
228+ * EOF, in the intervening space, (where ISO-C requires the effect
229+ * of zero bytes); check for this anomaly now.
230+ */
231+ __off64_t eof_pos, fwrite_pos = fseek_handler_reset( trap );
232+ if( fwrite_pos > (eof_pos = _lseeki64( fileno( fp ), 0LL, SEEK_END )) )
233+ { /* The original seek request HAD moved the fwrite position to
234+ * some point beyond EOF! We've now moved it back to EOF, so
235+ * prepare to fill with zeros, to pad out the file until we
236+ * return to the original seek position.
237+ */
238+ char zero_bytes[BUFSIZ] = {'\0'};
239+ __int64 fill_len = fwrite_pos - eof_pos;
240+
241+ /* Emit the requisite number of padding zeros, in blocks of
242+ * no more than BUFSIZ bytes...
243+ */
244+ while( fill_len > 0LL )
245+ { size_t len = (fill_len > BUFSIZ) ? BUFSIZ : fill_len;
246+ if( fwrite( zero_bytes, 1, len, fp ) != len )
247+ { /* ...but, if any block falls short of its expected size,
248+ * then an error has occurred; attempt to restore to the
249+ * original seek position, and abort the fwrite request,
250+ * having written NONE of its requested data.
251+ */
252+ __mingw_fseeki64( fp, fwrite_pos, SEEK_SET );
253+ return 0;
254+ }
255+ /* A padding block has been successfully written; adjust
256+ * the residual padding length, to account for it.
257+ */
258+ fill_len -= len;
259+ }
260+ }
261+ else
262+ /* The preceding seek was not beyond end of file, so there is no
263+ * danger of leaving random garbage, but our check has moved the
264+ * fwrite position to end of file; move it back to the position
265+ * set by the original seek request.
266+ */
267+ __mingw_fseeki64( fp, fwrite_pos, SEEK_SET );
268+ }
269+ /* Ultimately, complete the original fwrite request, at the expected
270+ * position within the output file.
271+ */
272+ return fwrite( buffer, size, count, fp );
273+}
274+
275+/* $RCSfile$: end of file */
Show on old repository browser