summaryrefslogtreecommitdiffstats
path: root/vfat.c
blob: 45d468aead11457743a9f04b0bd99bf9c70db1b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#ident "$Id$"
/* ----------------------------------------------------------------------- *
 *   
 *   Copyright 2003 H. Peter Anvin - All Rights Reserved
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
 *   Bostom MA 02111-1307, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/*
 * vfat.h
 *
 * Construct VFAT long filename descriptors
 */

#include <iconv.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include "fat.h"
#include "minmax.h"

extern const char *program;

static iconv_t iconv_utf16;

void vfat_setup(void)
{
  iconv_utf16 = iconv_open("UTF-16LE", "");
  if ( iconv_utf16 == (iconv_t)-1 ) {
    fprintf("%s: failed to iconv_open UTF-16LE: %s\n",
	    program, strerror(errno));
    exit(1);
  }
}

int vfat_make_long_name(struct fat_vfat_slot *slots,
			const char *str,
			uint8_t csum)
{
  uint16_t buffer[VFAT_MAX_LEN];
  uint16_t *xb;
  char *inb, *outb;
  int inbl, outbl, xl, nx;
  int seq = 1;
  int i;
  uint16_t cv;
  static const char *vfat_bad_long =
    "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
    "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
    "\"*/:<>?\\|\177";
  
  inb = str;
  inbl = strlen(inb);
  outb = (char *)buffer;
  outbl = VFAT_MAX_LEN << 1; 
  
  iconv(iconv_utf16, NULL, NULL, NULL, NULL); /* Reset input state */
  iconv(iconv_utf16, &inb, &inbl, &outb, &outbl);
  
  xl = VFAT_MAX_LEN - (outbl >> 1);
  xb = buffer;

  /* The VFAT spec says trailing periods and leading and trailing spaces
     are ignored.  However, it doesn't specify if those should actually
     be stripped.  Ignore that for now and let the filesystem worry about it. */

  for ( i = 0 ; i < xl ; i++ ) {
    cv = read16((le16_t *)&buffer[i]);
    if ( cv <= 127 && strchr(vfat_bad_long, cv) )
      write16((le16_t *)&buffer[i], '_'); /* Bad character, replace with _ */
  }

  while ( xl ) {
    memset(slots, 0, sizeof(*slots));
    slots->id = seq++;
    slots->attribute = 0x0f;	/* Part of a long name */
    slots->alias_csum = csum;
    
    nx = min(xl,5);
    memcpy(slots->name0, xb, nx<<1);
    xl -= nx;

    nx = min(xl,6);
    memcpy(slots->name5, xb, nx<<1);
    xl -= nx;

    nx = min(xl,2);
    memcpy(slots->name11, xb, nx<<1);
    xl -= nx;

    if ( !xl )
      slots->id |= 0x40;	/* Last slot */

    slots++;
  }

  return seq-1;
}