summaryrefslogtreecommitdiffstats
path: root/sys/msdosfs/msdosfs_conv.c
diff options
context:
space:
mode:
authordfr <dfr@FreeBSD.org>1994-09-19 15:41:57 +0000
committerdfr <dfr@FreeBSD.org>1994-09-19 15:41:57 +0000
commit7efbb964fdff1348e01bdcd7d4bb59551c389bef (patch)
tree4ee90f451b7756bcd74bbc0c7e0ef0219507ff02 /sys/msdosfs/msdosfs_conv.c
parent7a224749be53b3a6bdb826ffbd93208f0b757586 (diff)
downloadFreeBSD-src-7efbb964fdff1348e01bdcd7d4bb59551c389bef.zip
FreeBSD-src-7efbb964fdff1348e01bdcd7d4bb59551c389bef.tar.gz
Added msdosfs.
Obtained from: NetBSD
Diffstat (limited to 'sys/msdosfs/msdosfs_conv.c')
-rw-r--r--sys/msdosfs/msdosfs_conv.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/sys/msdosfs/msdosfs_conv.c b/sys/msdosfs/msdosfs_conv.c
new file mode 100644
index 0000000..da8ce97
--- /dev/null
+++ b/sys/msdosfs/msdosfs_conv.c
@@ -0,0 +1,358 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_conv.c,v 1.6.2.1 1994/08/30 02:27:57 cgd Exp $ */
+
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+/*
+ * System include files.
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h> /* defines tz */
+
+/*
+ * MSDOSFS include files.
+ */
+#include <msdosfs/direntry.h>
+
+/*
+ * Days in each month in a regular year.
+ */
+u_short regyear[] = {
+ 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * Days in each month in a leap year.
+ */
+u_short leapyear[] = {
+ 31, 29, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * Variables used to remember parts of the last time conversion. Maybe we
+ * can avoid a full conversion.
+ */
+u_long lasttime;
+u_long lastday;
+u_short lastddate;
+u_short lastdtime;
+
+/*
+ * Convert the unix version of time to dos's idea of time to be used in
+ * file timestamps. The passed in unix time is assumed to be in GMT.
+ */
+void
+unix2dostime(tsp, ddp, dtp)
+ struct timespec *tsp;
+ u_short *ddp;
+ u_short *dtp;
+{
+ u_long t;
+ u_long days;
+ u_long inc;
+ u_long year;
+ u_long month;
+ u_short *months;
+
+ /*
+ * If the time from the last conversion is the same as now, then
+ * skip the computations and use the saved result.
+ */
+ t = tsp->ts_sec - (tz.tz_minuteswest * 60)
+ /* +- daylight savings time correction */ ;
+ if (lasttime != t) {
+ lasttime = t;
+ lastdtime = (((t % 60) >> 1) << DT_2SECONDS_SHIFT)
+ + (((t / 60) % 60) << DT_MINUTES_SHIFT)
+ + (((t / 3600) % 24) << DT_HOURS_SHIFT);
+
+ /*
+ * If the number of days since 1970 is the same as the last
+ * time we did the computation then skip all this leap year
+ * and month stuff.
+ */
+ days = t / (24 * 60 * 60);
+ if (days != lastday) {
+ lastday = days;
+ for (year = 1970;; year++) {
+ inc = year & 0x03 ? 365 : 366;
+ if (days < inc)
+ break;
+ days -= inc;
+ }
+ months = year & 0x03 ? regyear : leapyear;
+ for (month = 0; month < 12; month++) {
+ if (days < months[month])
+ break;
+ days -= months[month];
+ }
+ lastddate = ((days + 1) << DD_DAY_SHIFT)
+ + ((month + 1) << DD_MONTH_SHIFT);
+ /*
+ * Remember dos's idea of time is relative to 1980.
+ * unix's is relative to 1970. If somehow we get a
+ * time before 1980 then don't give totally crazy
+ * results.
+ */
+ if (year > 1980)
+ lastddate += (year - 1980) << DD_YEAR_SHIFT;
+ }
+ }
+ *dtp = lastdtime;
+ *ddp = lastddate;
+}
+
+/*
+ * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
+ * interval there were 8 regular years and 2 leap years.
+ */
+#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
+
+u_short lastdosdate;
+u_long lastseconds;
+
+/*
+ * Convert from dos' idea of time to unix'. This will probably only be
+ * called from the stat(), and fstat() system calls and so probably need
+ * not be too efficient.
+ */
+void
+dos2unixtime(dd, dt, tsp)
+ u_short dd;
+ u_short dt;
+ struct timespec *tsp;
+{
+ u_long seconds;
+ u_long m, month;
+ u_long y, year;
+ u_long days;
+ u_short *months;
+
+ seconds = ((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT)
+ + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
+ + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600;
+ /*
+ * If the year, month, and day from the last conversion are the
+ * same then use the saved value.
+ */
+ if (lastdosdate != dd) {
+ lastdosdate = dd;
+ days = 0;
+ year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
+ for (y = 0; y < year; y++) {
+ days += y & 0x03 ? 365 : 366;
+ }
+ months = year & 0x03 ? regyear : leapyear;
+ /*
+ * Prevent going from 0 to 0xffffffff in the following
+ * loop.
+ */
+ month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
+ if (month == 0) {
+ printf("dos2unixtime(): month value out of range (%d)\n",
+ month);
+ month = 1;
+ }
+ for (m = 0; m < month - 1; m++) {
+ days += months[m];
+ }
+ days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
+ lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
+ }
+ tsp->ts_sec = seconds + lastseconds + (tz.tz_minuteswest * 60)
+ /* -+ daylight savings time correction */ ;
+ tsp->ts_nsec = 0;
+}
+
+/*
+ * Cheezy macros to do case detection and conversion for the ascii
+ * character set. DOESN'T work for ebcdic.
+ */
+#define isupper(c) (c >= 'A' && c <= 'Z')
+#define islower(c) (c >= 'a' && c <= 'z')
+#define toupper(c) (c & ~' ')
+#define tolower(c) (c | ' ')
+
+/*
+ * DOS filenames are made of 2 parts, the name part and the extension part.
+ * The name part is 8 characters long and the extension part is 3
+ * characters long. They may contain trailing blanks if the name or
+ * extension are not long enough to fill their respective fields.
+ */
+
+/*
+ * Convert a DOS filename to a unix filename. And, return the number of
+ * characters in the resulting unix filename excluding the terminating
+ * null.
+ */
+int
+dos2unixfn(dn, un)
+ u_char dn[11];
+ u_char *un;
+{
+ int i;
+ int ni;
+ int ei;
+ int thislong = 0;
+ u_char c;
+ u_char *origun = un;
+
+ /*
+ * Find the last character in the name portion of the dos filename.
+ */
+ for (ni = 7; ni >= 0; ni--)
+ if (dn[ni] != ' ')
+ break;
+
+ /*
+ * Find the last character in the extension portion of the
+ * filename.
+ */
+ for (ei = 10; ei >= 8; ei--)
+ if (dn[ei] != ' ')
+ break;
+
+ /*
+ * Copy the name portion into the unix filename string. NOTE: DOS
+ * filenames are usually kept in upper case. To make it more unixy
+ * we convert all DOS filenames to lower case. Some may like this,
+ * some may not.
+ */
+ for (i = 0; i <= ni; i++) {
+ c = dn[i];
+ *un++ = isupper(c) ? tolower(c) : c;
+ thislong++;
+ }
+
+ /*
+ * Now, if there is an extension then put in a period and copy in
+ * the extension.
+ */
+ if (ei >= 8) {
+ *un++ = '.';
+ thislong++;
+ for (i = 8; i <= ei; i++) {
+ c = dn[i];
+ *un++ = isupper(c) ? tolower(c) : c;
+ thislong++;
+ }
+ }
+ *un++ = 0;
+
+ /*
+ * If first char of the filename is SLOT_E5 (0x05), then the real
+ * first char of the filename should be 0xe5. But, they couldn't
+ * just have a 0xe5 mean 0xe5 because that is used to mean a freed
+ * directory slot. Another dos quirk.
+ */
+ if (*origun == SLOT_E5)
+ *origun = 0xe5;
+
+ return thislong;
+}
+
+/*
+ * Convert a unix filename to a DOS filename. This function does not ensure
+ * that valid characters for a dos filename are supplied.
+ */
+void
+unix2dosfn(un, dn, unlen)
+ u_char *un;
+ u_char dn[11];
+ int unlen;
+{
+ int i;
+ u_char c;
+
+ /*
+ * Fill the dos filename string with blanks. These are DOS's pad
+ * characters.
+ */
+ for (i = 0; i <= 10; i++)
+ dn[i] = ' ';
+
+ /*
+ * The filenames "." and ".." are handled specially, since they
+ * don't follow dos filename rules.
+ */
+ if (un[0] == '.' && unlen == 1) {
+ dn[0] = '.';
+ return;
+ }
+ if (un[0] == '.' && un[1] == '.' && unlen == 2) {
+ dn[0] = '.';
+ dn[1] = '.';
+ return;
+ }
+
+ /*
+ * Copy the unix filename into the dos filename string upto the end
+ * of string, a '.', or 8 characters. Whichever happens first stops
+ * us. This forms the name portion of the dos filename. Fold to
+ * upper case.
+ */
+ for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) {
+ dn[i] = islower(c) ? toupper(c) : c;
+ un++;
+ unlen--;
+ }
+
+ /*
+ * If the first char of the filename is 0xe5, then translate it to
+ * 0x05. This is because 0xe5 is the marker for a deleted
+ * directory slot. I guess this means you can't have filenames
+ * that start with 0x05. I suppose we should check for this and
+ * doing something about it.
+ */
+ if (dn[0] == SLOT_DELETED)
+ dn[0] = SLOT_E5;
+
+ /*
+ * Strip any further characters up to a '.' or the end of the
+ * string.
+ */
+ while (unlen && (c = *un)) {
+ un++;
+ unlen--;
+ /* Make sure we've skipped over the dot before stopping. */
+ if (c == '.')
+ break;
+ }
+
+ /*
+ * Copy in the extension part of the name, if any. Force to upper
+ * case. Note that the extension is allowed to contain '.'s.
+ * Filenames in this form are probably inaccessable under dos.
+ */
+ for (i = 8; i <= 10 && unlen && (c = *un); i++) {
+ dn[i] = islower(c) ? toupper(c) : c;
+ un++;
+ unlen--;
+ }
+}
+
+/*
+ * Get rid of these macros before someone discovers we are using such
+ * hideous things.
+ */
+#undef isupper
+#undef islower
+#undef toupper
+#undef tolower
OpenPOWER on IntegriCloud