X-Recipient: archive-cygwin AT delorie DOT com X-SWARE-Spam-Status: No, hits=-1.0 required=5.0 tests=AWL,BAYES_00,SPF_NEUTRAL,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Message-ID: <4CCB4676.8080609@cornell.edu> Date: Fri, 29 Oct 2010 18:11:02 -0400 From: Ken Brown User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.11) Gecko/20101013 Thunderbird/3.1.5 MIME-Version: 1.0 To: cygwin AT cygwin DOT com Subject: Re: localtime and TZ References: <4CCB4025 DOT 6070405 AT cornell DOT edu> <4CCB42AF DOT 2090907 AT redhat DOT com> <4CCB439B DOT 7060602 AT redhat DOT com> In-Reply-To: <4CCB439B.7060602@redhat.com> Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-IsSubscribed: yes Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com On 10/29/2010 5:58 PM, Eric Blake wrote: > On 10/29/2010 03:54 PM, Eric Blake wrote: >> On 10/29/2010 03:44 PM, Ken Brown wrote: >>> While trying to debug a timezone problem in the Cygwin build of emacs, I've come across a difference between Cygwin and Linux in the behavior of localtime with respect to TZ. Suppose I set TZ, call localtime, unset TZ, and call localtime again. On Cygwin, the second call to localtime re-uses the previous value of TZ. On Linux, localtime reverts to giving local information, just as if TZ had never been set. Here's a Simple Test Case: >>> >>> #include >>> #include >>> >>> extern char **environ; > > Also, you should be including for the declaration of environ, > rather than declaring it yourself. > >>> >>> void >>> unset_TZ (void) >>> { >>> char **from, **to; >>> for (to = from = environ; (*to = *from); from++) >>> if (! (to[0][0] == 'T'&& to[0][1] == 'Z'&& to[0][2] == '=')) >>> to++; >>> } >> >> Messing directly with environ is your problem. POSIX says that it is >> only portable to traverse (but not modify) environ's contents, or to >> completely assign a new array to environ. By going behind cygwin's >> back, and not using unsetenv(), you have violated POSIX and can't expect >> sane results. > > In particular: > > http://austingroupbugs.net/view.php?id=167 > > If the application modifies the pointers to which environ > points, the behavior of all interfaces described in the System > Interfaces volume of POSIX.1-2008 is undefined. > > > Conforming applications are required not to directly modify the > pointers to which environ points, but to use only the setenv(), > unsetenv() and putenv() functions, or assignment to environ > itself, to manipulate the process environment. This constraint > allows the implementation to properly manage the memory it > allocates. This enables the implementation to free any space it > has allocated to strings (and perhaps the pointers to them) > stored in environ when unsetenv() is called. A C runtime start-up > procedure (that which invokes main() and perhaps initializes > environ) can also initialize a flag indicating that none of the > environment has yet been copied to allocated storage, or that the > separate table has not yet been initialized. If the application > switches to a complete new environment by assigning a new value > to environ, this can be detected by getenv(), setenv(), unsetenv() > or putenv() and the implementation can at that point reinitialize > based on the new environment. (This may include copying the > environment strings into a new array and assigning environ to > point to it.) > > In fact, for higher performance of getenv(), implementations > that do not provide putenv() could also maintain a separate copy > of the environment in a data structure that could be searched > much more quickly (such as an indexed hash table, or a binary > tree), and update both it and the linear list at environ when > setenv() or unsetenv() is invoked. On implementations that do > provide putenv(), such a copy might still be worthwhile but > would need to allow for the fact that applications can directly > modify the content of environment strings added with putenv(). > For example, if an environment string found by searching the > copy is one that was added using putenv(), the implementation > would need to check that the string in environ still has the > same name (and value, if the copy includes values), and whenever > searching the copy produces no match the implementation would > then need to search each environment string in environ that > was added using putenv() in case any of them have changed their > names and now match. Thus each use of putenv() to add to the > environment would reduce the speed advantage of having the copy. > > After page 772 line 25712 section exec, add two new paragraphs: > > Applications can change the entire environment in a single > operation by assigning the environ variable to point to an array > of character pointers to the new environment strings. > After assigning a new value to environ, applications should > not rely on the new environment strings remaining part of the > environment, as a call to getenv(), [XSI]putenv(),[/XSI] > setenv(), unsetenv() or any function that is dependent on an > environment variable may, on noticing that environ has changed, > copy the environment strings to a new array and assign environ > to point to it. > > Any application that directly modifies the pointers to which the > environ variable points has undefined behavior. > Thanks, Eric. I didn't know about any of this. (I was using a modification of a configure test from the emacs sources.) But I get the same behavior with the following revised test case: #include #include int main (void) { time_t now = time ((time_t *) 0); printf ("TZ is initially unset; hour = %d\n", localtime (&now)->tm_hour); putenv ("TZ=GMT0"); printf ("TZ=GMT0; hour = %d\n", localtime (&now)->tm_hour); unsetenv("TZ"); printf ("TZ unset; hour = %d\n", localtime (&now)->tm_hour); putenv ("TZ=PST8"); printf ("TZ=PST8; hour = %d\n", localtime (&now)->tm_hour); unsetenv("TZ"); printf ("TZ unset again; hour = %d\n", localtime (&now)->tm_hour); } So the question remains whether this difference between Cygwin and Linux is a bug or by design. Ken -- Problem reports: http://cygwin.com/problems.html FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple