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