-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathfilename.c
268 lines (244 loc) · 8.48 KB
/
filename.c
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <alloca.h>
#include <curses.h>
#include <string.h>
#include "loterrs.h"
#include "lottypes.h"
#include "lotdefs.h"
#include "lotfuncs.h"
// This breaks the specified pathname into the PATHNAME structure, which
// contains offsets of drive, directory, filename, extension, and so on.
//
// This routine handles LMBCS encoded filesnames in DOS mode only.
// In DOS mode, '\\' or '/' are valid directory seperators, and will be
// converted as requested.
//
// Originally, this routine required filenames on UNIX to be under 14
// characters, but this rewrite removes that limitation, see #82.
//
// - base is an optional base directory to assume is prepended to pathname.
// - extension is a file extension to append.
// - resource..
// - If set, expandpath will expand globs.
// - If set, checklen will validate all components are within limits.
uint16_t file_name_split(struct PATHNAME *pstruct,
char *pathname,
char *root,
char *extension,
int16_t *resource,
int16_t expandpath,
int16_t checklen)
{
char *p;
unsigned int mbcs;
char *prepended_root_after_drv;
char *dirsep;
char *extptr;
int extlen;
int pathend;
uint16_t *fname_content_xlt_tbl;
int16_t pathtype;
uint16_t diroff_1;
uint16_t diroff;
uint16_t offsetofext;
uint16_t result;
char *prepended_root;
char *path_after_drive;
char *lastspace;
char *lastdirptr;
char *filenamestart;
char *fileext;
char *resourcematch;
char reducedpath[PATH_MAX];
result = 0;
// The translation table for lmbcs encoded filenames.
fname_content_xlt_tbl = get_fname_content_xlt_tbl(0, 2);
// If caller requested, validate lengths.
if (checklen && (result = check_lengths(pathname))) {
return result;
}
// Reset the cracked filename structure.
file_name_clear(pstruct);
// Validate parameters.
if (!pathname ||!*pathname )
return 0;
// Skip past any leading blanks.
while (*pathname == ' ')
++pathname;
p = pathname;
fileext = NULL;
lastdirptr = NULL;
lastspace = NULL;
path_after_drive = NULL;
resourcematch = NULL;
pathtype = 0; // pathtype 2 = resource?
// If in DOS mode, check if this looks like a drive.
if (pathname[1] == ':' && file_mode != FILE_MODE_UNIX) {
p = pathname + 2;
path_after_drive = pathname + 2;
}
// This translates lmbcs codepoints
if (!file_name_reduce(reducedpath, p))
return LOTERR_FILENAME_INVALID;
// Now check through the whole path for any special charaters.
while (*p) {
switch (*p) {
case ' ':
resourcematch = p;
lastspace = p;
if (!resource_substr_match(0xC28u, &resourcematch))
pathtype = 2;
break;
case '.':
// There can't be two extensions in DOS mode.
if (file_mode != FILE_MODE_UNIX && checklen && fileext)
return LOTERR_FILENAME_INVALID;
// Otherwise, the last . begins the extension.
fileext = p;
break;
case '/':
// The last directory.
lastdirptr = p;
break;
case '<':
case '>':
// These are not permitted in filenames because it's part of the
// linked file range syntax, e.g. <<foo.wk3>>A1..A5
return LOTERR_FILENAME_INVALID;
case '\\':
// In DOS mode, this is an acceptable dirseperator, otherwise
// it's just a normal char on UNIX.
if (file_mode != FILE_MODE_UNIX)
lastdirptr = p;
break;
default:
// For all other characters, maybe do lmbcs translation.
if (file_mode != FILE_MODE_UNIX) {
mbcs = peek_next_mbcs(p);
if ( mbcs > 0xFF || mbcs <= 0x1F || !fname_content_xlt_tbl[mbcs - 32] )
return LOTERR_FILENAME_INVALID;
}
break;
}
// If a resource request
if (pathtype == 2)
break;
// Skip over this char (possibly mb char)
p += char_size(*p);
}
// Now we've found the extension, filename, and dirname
if (lastspace) {
resourcematch = lastspace;
if (!resource_substr_match(0xC2Bu, &resourcematch) && !peek_next_mbcs(resourcematch)) {
pathtype = 1;
p = lastspace;
}
}
if (resource)
*resource = pathtype;
// Is the file extension part of the filename?
if (lastdirptr && fileext && lastdirptr > fileext)
fileext = 0;
prepended_root_after_drv = 0;
prepended_root = NULL;
if (file_mode != FILE_MODE_UNIX) {
// This is an optional "root" to prepend
if (root) {
// Check if it's a drive
prepended_root = root;
if (root[1] == ':') {
prepended_root = root + 2;
prepended_root_after_drv = root + 2;
}
}
if (path_after_drive) {
pstruct->diroff = path_after_drive - pathname;
memcpy(pstruct->str, pathname, (path_after_drive - pathname));
pathname = path_after_drive;
} else if (prepended_root_after_drv) {
pstruct->diroff = prepended_root_after_drv - root;
memcpy(pstruct->str, root, pstruct->diroff);
}
}
else {
prepended_root = root;
pstruct->diroff = 0;
path_after_drive = 0;
}
pstruct->start = 0;
diroff_1 = pstruct->diroff;
if (lastdirptr) {
filenamestart = lastdirptr + 1;
pstruct->filenameoff = filenamestart - pathname;
memcpy(&pstruct->str[diroff_1], pathname, (filenamestart - pathname));
pathname = filenamestart;
}
else if (root) {
if (!path_after_drive) {
pstruct->filenameoff = strlen(prepended_root);
memcpy(&pstruct->str[diroff_1], prepended_root, pstruct->filenameoff);
if (pstruct->filenameoff) {
dirsep = &pstruct->str[pstruct->filenameoff + diroff_1 - 1];
if (dirsep[0] != '/' && dirsep[0] != '\\') {
dirsep[1] = '/';
++pstruct->filenameoff;
}
}
}
}
slash_convert(&pstruct->str[diroff_1], pstruct->filenameoff);
pstruct->diroffpreroot = diroff_1;
diroff = pstruct->filenameoff + diroff_1;
pstruct->dirlen = diroff;
pstruct->namelen = p - pathname;
if (p != pathname) {
if (fileext) {
pstruct->extlen = p - fileext;
pstruct->namelen -= p - fileext;
}
if ( pstruct->extlen + pstruct->namelen + pstruct->dirlen > PATH_MAX) {
return LOTERR_FILENAME_TOO_LONG;
}
memcpy(&pstruct->str[diroff], pathname, pstruct->namelen);
}
offsetofext = pstruct->namelen + diroff;
if (pstruct->extlen) {
memcpy(&pstruct->str[offsetofext], fileext, pstruct->extlen);
} else if (extension) {
pstruct->extlen = strlen(extension) + 1;
if (pstruct->extlen + pstruct->namelen + pstruct->dirlen > PATH_MAX) {
return LOTERR_FILENAME_TOO_LONG;
}
pstruct->str[offsetofext] = '.';
extptr = &pstruct->str[offsetofext + 1];
strcpy(extptr, extension);
if (checklen)
fallback_and_coerce_case(extptr);
}
pstruct->offext = offsetofext;
// Extension cannot exceed 3 chars (+.) in DOS mode.
if (file_mode != FILE_MODE_UNIX && checklen) {
if (pstruct->extlen > 4)
extlen = 4;
else
extlen = pstruct->extlen;
pathend = extlen + pstruct->offext;
} else {
pathend = pstruct->extlen + pstruct->offext;
}
pstruct->str[pathend] = 0;
if (expandpath)
result = file_path_expand(pstruct);
if (!result && checklen)
file_name_case_check(pstruct->str);
if (pstruct->extlen && !pstruct->namelen) {
if (pstruct->filenameoff || pstruct->diroff || pstruct->extlen != 1)
return LOTERR_WORKSHEET_UNNAMED;
pstruct->namelen = 1;
pstruct->extlen = 0;
pstruct->offext = 1;
}
return result;
}