source: rtems/cpukit/libfs/src/dosfs/msdos_conv.c @ c125425

4.104.114.84.95
Last change on this file since c125425 was c125425, checked in by Chris Johns <chrisj@…>, on Jul 2, 2003 at 2:09:15 PM

Patch from Victor V. Vengerov <vvv@…> to remove Linux code.

  • Property mode set to 100644
File size: 7.9 KB
Line 
1/*
2 * Adaptation of NetBSD code for RTEMS by Victor V. Vengerov <vvv@oktet.ru>
3 */
4/*      $NetBSD: msdosfs_conv.c,v 1.10 1994/12/27 18:36:24 mycroft Exp $        */
5/*
6 * Written by Paul Popelka (paulp@uts.amdahl.com)
7 *
8 * You can do anything you want with this software, just don't say you wrote
9 * it, and don't remove this notice.
10 *
11 * This software is provided "as is".
12 *
13 * The author supplies this software to be publicly redistributed on the
14 * understanding that the author is not responsible for the correct
15 * functioning of this software in any circumstances and is not liable for
16 * any damages caused by this software.
17 *
18 * October 1992
19 */
20
21#include <rtems.h>
22#include "msdos.h"
23/*
24 * Days in each month in a regular year.
25 */
26static u_short regyear[] = {
27        31, 28, 31, 30, 31, 30,
28        31, 31, 30, 31, 30, 31
29};
30
31/*
32 * Days in each month in a leap year.
33 */
34static u_short leapyear[] = {
35        31, 29, 31, 30, 31, 30,
36        31, 31, 30, 31, 30, 31
37};
38
39/*
40 * Variables used to remember parts of the last time conversion.  Maybe we
41 * can avoid a full conversion.
42 */
43static u_long lasttime;
44static u_long lastday;
45static u_short lastddate;
46static u_short lastdtime;
47
48/*
49 * Convert the unix version of time to dos's idea of time to be used in
50 * file timestamps. The passed in unix time is assumed to be in GMT.
51 */
52void
53msdos_date_unix2dos(unsigned int t, unsigned short *ddp, 
54                    unsigned short *dtp)
55{
56        u_long days;
57        u_long inc;
58        u_long year;
59        u_long month;
60        u_short *months;
61
62        /*
63         * If the time from the last conversion is the same as now, then
64         * skip the computations and use the saved result.
65         */
66        if (lasttime != t) {
67                lasttime = t;
68                lastdtime = (((t % 60) >> 1) << MSDOS_DT_2SECONDS_SHIFT)
69                    + (((t / 60) % 60) << MSDOS_DT_MINUTES_SHIFT)
70                    + (((t / 3600) % 24) << MSDOS_DT_HOURS_SHIFT);
71
72                /*
73                 * If the number of days since 1970 is the same as the last
74                 * time we did the computation then skip all this leap year
75                 * and month stuff.
76                 */
77                days = t / (24 * 60 * 60);
78                if (days != lastday) {
79                        lastday = days;
80                        for (year = 1970;; year++) {
81                                inc = year & 0x03 ? 365 : 366;
82                                if (days < inc)
83                                        break;
84                                days -= inc;
85                        }
86                        months = year & 0x03 ? regyear : leapyear;
87                        for (month = 0; month < 12; month++) {
88                                if (days < months[month])
89                                        break;
90                                days -= months[month];
91                        }
92                        lastddate = ((days + 1) << MSDOS_DD_DAY_SHIFT)
93                            + ((month + 1) << MSDOS_DD_MONTH_SHIFT);
94                        /*
95                         * Remember dos's idea of time is relative to 1980.
96                         * unix's is relative to 1970.  If somehow we get a
97                         * time before 1980 then don't give totally crazy
98                         * results.
99                         */
100                        if (year > 1980)
101                                lastddate += (year - 1980) << 
102                                             MSDOS_DD_YEAR_SHIFT;
103                }
104        }
105        *dtp = lastdtime;
106        *ddp = lastddate;
107}
108
109/*
110 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
111 * interval there were 8 regular years and 2 leap years.
112 */
113#define SECONDSTO1980   (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
114
115static u_short lastdosdate;
116static u_long lastseconds;
117
118/*
119 * Convert from dos' idea of time to unix'. This will probably only be
120 * called from the stat(), and fstat() system calls and so probably need
121 * not be too efficient.
122 */
123unsigned int
124msdos_date_dos2unix(unsigned int dd, unsigned int dt)
125{
126        u_long seconds;
127        u_long m, month;
128        u_long y, year;
129        u_long days;
130        u_short *months;
131
132        seconds = ((dt & MSDOS_DT_2SECONDS_MASK) >> MSDOS_DT_2SECONDS_SHIFT)
133            + ((dt & MSDOS_DT_MINUTES_MASK) >> MSDOS_DT_MINUTES_SHIFT) * 60
134            + ((dt & MSDOS_DT_HOURS_MASK) >> MSDOS_DT_HOURS_SHIFT) * 3600;
135        /*
136         * If the year, month, and day from the last conversion are the
137         * same then use the saved value.
138         */
139        if (lastdosdate != dd) {
140                lastdosdate = dd;
141                days = 0;
142                year = (dd & MSDOS_DD_YEAR_MASK) >> MSDOS_DD_YEAR_SHIFT;
143                for (y = 0; y < year; y++)
144                        days += y & 0x03 ? 365 : 366;
145                months = year & 0x03 ? regyear : leapyear;
146                /*
147                 * Prevent going from 0 to 0xffffffff in the following
148                 * loop.
149                 */
150                month = (dd & MSDOS_DD_MONTH_MASK) >> MSDOS_DD_MONTH_SHIFT;
151                if (month == 0) {
152                        month = 1;
153                }
154                for (m = 0; m < month - 1; m++)
155                        days += months[m];
156                days += ((dd & MSDOS_DD_DAY_MASK) >> MSDOS_DD_DAY_SHIFT) - 1;
157                lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
158        }
159        return seconds + lastseconds;
160}
161
162/*
163 * Cheezy macros to do case detection and conversion for the ascii
164 * character set.  DOESN'T work for ebcdic.
165 */
166#define isupper(c)      (c >= 'A'  &&  c <= 'Z')
167#define islower(c)      (c >= 'a'  &&  c <= 'z')
168#define toupper(c)      (c & ~' ')
169#define tolower(c)      (c | ' ')
170
171static const u_char msdos_map[] = {
172/* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173/* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174/* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175/* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176/* 20 */ 0x00, 0x21, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, /*  !"#$%&' */
177/* 28 */ 0x28, 0x29, 0x00, 0x00, 0x00, 0x2D, 0x2E, 0x00, /* ()*+,-./ */
178/* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 01234567 */
179/* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */
180/* 40 */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */
181/* 48 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* HIJKLMNO */
182/* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* PQRSTUVW */
183/* 58 */ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x00, 0x5E, 0x5F, /* XYZ[\]^_ */
184/* 60 */ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */
185/* 68 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* hijklmno */
186/* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* pqrstuvw */
187/* 78 */ 0x58, 0x59, 0x5A, 0x5B, 0x7C, 0x00, 0x7E, 0x00, /* xyz{|}~  */
188/* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189/* 88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
190/* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191/* 98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
192/* A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
193/* A8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194/* B0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195/* B8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
196/* C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
197/* C8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
198/* D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199/* D8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200/* E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201/* E8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202/* F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
203/* F8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
204};
205/*
206 * Convert a unix filename to a DOS filename. Return -1 if wrong name is
207 * supplied.
208 */
209int
210msdos_filename_unix2dos(char *un, int unlen, char *dn)
211{
212        int i;
213        u_char c;
214
215        /*
216         * Fill the dos filename string with blanks. These are DOS's pad
217         * characters.
218         */
219        for (i = 0; i <= 10; i++)
220                dn[i] = ' ';
221
222        /*
223         * The filenames "." and ".." are handled specially, since they
224         * don't follow dos filename rules.
225         */
226        if (un[0] == '.' && unlen == 1) {
227                dn[0] = '.';
228                return 0;
229        }
230        if (un[0] == '.' && un[1] == '.' && unlen == 2) {
231                dn[0] = '.';
232                dn[1] = '.';
233                return 0;
234        }
235
236        /*
237         * Copy the unix filename into the dos filename string upto the end
238         * of string, a '.', or 8 characters. Whichever happens first stops
239         * us. This forms the name portion of the dos filename. Fold to
240         * upper case.
241         */
242        for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) {
243                if ((dn[i] = msdos_map[c]) == 0)
244                        return -1;
245                un++;
246                unlen--;
247        }
248
249        /*
250         * Strip any further characters up to a '.' or the end of the
251         * string.
252         */
253        while (unlen && (c = *un)) {
254                un++;
255                unlen--;
256                /* Make sure we've skipped over the dot before stopping. */
257                if (c == '.')
258                        break;
259        }
260
261        /*
262         * Copy in the extension part of the name, if any. Force to upper
263         * case. Note that the extension is allowed to contain '.'s.
264         * Filenames in this form are probably inaccessable under dos.
265         */
266        for (i = 8; i <= 10 && unlen && (c = *un); i++) {
267                if ((dn[i] = msdos_map[c]) == 0)
268                        return -1;
269                un++;
270                unlen--;
271        }
272        return 0;
273}
Note: See TracBrowser for help on using the repository browser.