1 /*
2  * enigma/misc.c - provide miscellaneous utility routines.
3  *
4  * Copyright 2000 Simon Tatham. All rights reserved.
5  *
6  * Enigma is licensed under the MIT licence. See the file LICENCE for
7  * details.
8  *
9  * - we are all amf -
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <time.h>
16 #ifndef _WIN32
17 #  include <unistd.h>
18 #  include <pwd.h>
19 #endif
20 #include <sys/types.h>
21 
22 #include "enigma.h"
23 
24 char *fatal_error_string;
25 jmp_buf fatal_error_jmp_buf;
26 
fatal(char * string)27 void fatal(char *string) {
28     fatal_error_string = malloc(1+strlen(string));
29     if (fatal_error_string)
30 	strcpy(fatal_error_string, string);
31     longjmp(fatal_error_jmp_buf, 1);
32 }
33 
34 /*
35  * See if a line begins with a given header text.
36  */
ishdr(char * line,char * header)37 int ishdr(char *line, char *header) {
38     return !strncmp(line, header, strlen(header));
39 }
40 
41 /*
42  * Determine a username to use for saving and loading progress and
43  * game positions.
44  */
get_user(char * buf,int buflen)45 void get_user(char *buf, int buflen) {
46 #ifdef _WIN32
47     if (!GetUserName(buf, &buflen))
48 	strncpy(buf, "unknown", buflen);
49 #else
50     struct passwd *p;
51     uid_t uid = getuid();
52     char *user;
53 
54     /*
55      * First, find who we think we are using getlogin. If this
56      * agrees with our uid, we'll go along with it. This should
57      * allow sharing of uids between several login names whilst
58      * coping correctly with people who have su'ed.
59      */
60     user = getlogin();
61     setpwent();
62     if (user)
63 	p = getpwnam(user);
64     else
65 	p = NULL;
66     if (p && p->pw_uid == uid) {
67 	/*
68 	 * The result of getlogin() really does correspond to our
69 	 * uid. Fine.
70 	 */
71 	strncpy(buf, user, buflen);
72 	buf[buflen-1] = '\0';
73     } else {
74 	/*
75 	 * If that didn't work, for whatever reason, we'll do the
76 	 * simpler version: look up our uid in the password file
77 	 * and map it straight to a name.
78 	 */
79 	p = getpwuid(uid);
80 	strncpy(buf, p->pw_name, buflen);
81 	buf[buflen-1] = '\0';
82     }
83     endpwent();
84 #endif
85 }
86 
87 /*
88  * Routines to get around the fact that C's time handling is awful.
89  * With no direct inverse to gmtime(), and no standards-compliant
90  * way to read and write a time_t directly, what standard format
91  * can there possibly be for times that ports across time zones?
92  *
93  * Well, it can just about be done. Here's how.
94  */
95 
96 /* ----------------------------------------------------------------------
97  * This completely ANSI-compliant function determines the timezone shift
98  * in seconds. (E.g. if local time were 1 hour ahead of GMT, this routine
99  * would return 3600.) This timezone shift is to normal local time, not to
100  * DST local time.
101  */
tzshift(void)102 static long tzshift(void) {
103     time_t t1, t2;
104     struct tm tm;
105     t1 = time(NULL);
106     tm = *gmtime(&t1);
107     tm.tm_isdst = 0;		       /* should already be; let's make sure */
108     t2 = mktime(&tm);
109     /*
110      * So tm is t1 formatted as GMT, and is also t2 formatted as
111      * local time. Hence difftime(t1,t2) gives the number of
112      * seconds by which local time is ahead of GMT. We'll assume
113      * here that the number of seconds will fit in a long, since
114      * for it not to do so would have to imply a time zone 68
115      * _years_ different from GMT.
116      */
117     return (long)difftime(t1,t2);
118 }
119 
120 /* ----------------------------------------------------------------------
121  * This completely ANSI-compliant function adjusts a `struct tm' by
122  * a given number of seconds.
123  */
adjust_tm(struct tm * tm,long seconds)124 static void adjust_tm(struct tm *tm, long seconds) {
125     int sign = seconds < 0 ? -1 : +1;
126     seconds = labs(seconds);
127     mktime(tm);			       /* normalise the tm structure */
128     tm->tm_mday += sign * (seconds / 86400);
129     tm->tm_hour += sign * (seconds / 3600) % 24;
130     tm->tm_sec += sign * (seconds % 3600);
131     mktime(tm);			       /* normalise the tm structure again */
132 }
133 
134 /* ----------------------------------------------------------------------
135  * With the aid of the above two functions, _this_ completely ANSI-
136  * compliant function is the equivalent of mktime() using GMT. In
137  * other words, it's the inverse of gmtime().
138  */
mktimegm(struct tm * tm)139 static time_t mktimegm(struct tm *tm) {
140     tm->tm_isdst = 0;		       /* GMT is never daylight-saving */
141     /*
142      * If local time is an hour ahead of GMT, then calling mktime
143      * on tm will give us a result one hour _earlier_ than what we
144      * want, so we would have to add an hour to tm before calling
145      * mktime.
146      */
147     adjust_tm(tm, tzshift());
148     return mktime(tm);
149 }
150 
151 /*
152  * Progress files store dates in GMT, in the format YYYY/MM/DD hh:mm:ss.
153  */
154 
155 /*
156  * Parse a date in progress-file format.
157  */
parse_date(char * buf)158 time_t parse_date(char *buf) {
159     struct tm tm;
160     int year, month;
161 
162     sscanf(buf, "%d/%d/%d %d:%d:%d", &year, &month, &tm.tm_mday,
163 	   &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
164     tm.tm_year = year - 1900;
165     tm.tm_mon = month - 1;
166     return mktimegm(&tm);
167 }
168 
169 /*
170  * Format a date in progress-file format.
171  */
fmt_date(char * buf,time_t t)172 void fmt_date(char *buf, time_t t) {
173     struct tm tm;
174 
175     tm = *gmtime(&t);
176 
177     sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d",
178 	    1900+tm.tm_year, 1+tm.tm_mon, tm.tm_mday,
179 	    tm.tm_hour, tm.tm_min, tm.tm_sec);
180 }
181