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

4.104.114.95
Last change on this file since 7c007cf was 8c14432, checked in by Ralf Corsepius <ralf.corsepius@…>, on 10/26/04 at 09:48:53

2004-10-26 Ralf Corsepius <ralf_corsepius@…>

  • libfs/src/dosfs/msdos_conv.c: Replace BSD fixed-size types with POSIX fixed-size types. Replace SECONDSTO1980 with DAYSTO1980. Make macros 16bit clean.
  • Property mode set to 100644
File size: 7.8 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#if HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include <rtems.h>
26#include "msdos.h"
27
28/* #define SECONDSPERDAY (24 * 60 * 60) */
29#define SECONDSPERDAY ((uint32_t) 86400)
30
31/*
32 * Days in each month in a regular year.
33 */
34static uint16_t regyear[] = {
35        31, 28, 31, 30, 31, 30,
36        31, 31, 30, 31, 30, 31
37};
38
39/*
40 * Days in each month in a leap year.
41 */
42static uint16_t leapyear[] = {
43        31, 29, 31, 30, 31, 30,
44        31, 31, 30, 31, 30, 31
45};
46
47/*
48 * Variables used to remember parts of the last time conversion.  Maybe we
49 * can avoid a full conversion.
50 */
51static uint32_t lasttime;
52static uint32_t lastday;
53static uint16_t lastddate;
54static uint16_t lastdtime;
55
56/*
57 * Convert the unix version of time to dos's idea of time to be used in
58 * file timestamps. The passed in unix time is assumed to be in GMT.
59 */
60void
61msdos_date_unix2dos(unsigned int t, unsigned short *ddp,
62                    unsigned short *dtp)
63{
64        uint32_t days;
65        uint32_t inc;
66        uint32_t year;
67        uint32_t month;
68        uint16_t *months;
69
70        /*
71         * If the time from the last conversion is the same as now, then
72         * skip the computations and use the saved result.
73         */
74        if (lasttime != t) {
75                lasttime = t;
76                lastdtime = (((t % 60) >> 1) << MSDOS_DT_2SECONDS_SHIFT)
77                    + (((t / 60) % 60) << MSDOS_DT_MINUTES_SHIFT)
78                    + (((t / 3600) % 24) << MSDOS_DT_HOURS_SHIFT);
79
80                /*
81                 * If the number of days since 1970 is the same as the last
82                 * time we did the computation then skip all this leap year
83                 * and month stuff.
84                 */
85                days = t / (SECONDSPERDAY);
86                if (days != lastday) {
87                        lastday = days;
88                        for (year = 1970;; year++) {
89                                inc = year & 0x03 ? 365 : 366;
90                                if (days < inc)
91                                        break;
92                                days -= inc;
93                        }
94                        months = year & 0x03 ? regyear : leapyear;
95                        for (month = 0; month < 12; month++) {
96                                if (days < months[month])
97                                        break;
98                                days -= months[month];
99                        }
100                        lastddate = ((days + 1) << MSDOS_DD_DAY_SHIFT)
101                            + ((month + 1) << MSDOS_DD_MONTH_SHIFT);
102                        /*
103                         * Remember dos's idea of time is relative to 1980.
104                         * unix's is relative to 1970.  If somehow we get a
105                         * time before 1980 then don't give totally crazy
106                         * results.
107                         */
108                        if (year > 1980)
109                                lastddate += (year - 1980) <<
110                                             MSDOS_DD_YEAR_SHIFT;
111                }
112        }
113        *dtp = lastdtime;
114        *ddp = lastddate;
115}
116
117/*
118 * The number of days between Jan 1, 1970 and Jan 1, 1980. In that
119 * interval there were 8 regular years and 2 leap years.
120 */
121/* #define      DAYSTO1980      ((8 * 365) + (2 * 366)) */
122#define DAYSTO1980   ((uint32_t) 3652)
123
124static uint16_t lastdosdate;
125static uint32_t lastseconds;
126
127/*
128 * Convert from dos' idea of time to unix'. This will probably only be
129 * called from the stat(), and fstat() system calls and so probably need
130 * not be too efficient.
131 */
132unsigned int
133msdos_date_dos2unix(unsigned int dd, unsigned int dt)
134{
135        uint32_t seconds;
136        uint32_t m, month;
137        uint32_t y, year;
138        uint32_t days;
139        uint16_t *months;
140
141        seconds = ((dt & MSDOS_DT_2SECONDS_MASK) >> MSDOS_DT_2SECONDS_SHIFT)
142            + ((dt & MSDOS_DT_MINUTES_MASK) >> MSDOS_DT_MINUTES_SHIFT) * 60
143            + ((dt & MSDOS_DT_HOURS_MASK) >> MSDOS_DT_HOURS_SHIFT) * 3600;
144        /*
145         * If the year, month, and day from the last conversion are the
146         * same then use the saved value.
147         */
148        if (lastdosdate != dd) {
149                lastdosdate = dd;
150                days = 0;
151                year = (dd & MSDOS_DD_YEAR_MASK) >> MSDOS_DD_YEAR_SHIFT;
152                for (y = 0; y < year; y++)
153                        days += y & 0x03 ? 365 : 366;
154                months = year & 0x03 ? regyear : leapyear;
155                /*
156                 * Prevent going from 0 to 0xffffffff in the following
157                 * loop.
158                 */
159                month = (dd & MSDOS_DD_MONTH_MASK) >> MSDOS_DD_MONTH_SHIFT;
160                if (month == 0) {
161                        month = 1;
162                }
163                for (m = 0; m < month - 1; m++)
164                        days += months[m];
165                days += ((dd & MSDOS_DD_DAY_MASK) >> MSDOS_DD_DAY_SHIFT) - 1;
166                lastseconds = (days + DAYSTO1980) * SECONDSPERDAY;
167        }
168        return seconds + lastseconds;
169}
170
171static const uint8_t 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        uint8_t 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.