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

4.104.114.84.95
Last change on this file since ffc144e was ffc144e, checked in by Ralf Corsepius <ralf.corsepius@…>, on 07/08/03 at 08:58:21

2003-07-08 Ralf Corsepius <corsepiu@…>

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