Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4432615
path.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
path.c
View Options
#include
<compat.h>
// IWYU pragma: keep
#include
<ctype.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
"ant.h"
#include
"errors.h"
#include
"internal.h"
#include
"modules/symbol.h"
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#ifdef _WIN32
#define PATH_SEP '\\'
#define PATH_SEP_STR "\\"
#define PATH_DELIMITER ';'
#else
#define PATH_SEP '/'
#define PATH_SEP_STR "/"
#define PATH_DELIMITER ':'
#endif
typedef
enum
{
PATH_STYLE_POSIX
=
1
,
PATH_STYLE_WIN32
=
2
,
}
path_style_t
;
static
path_style_t
path_host_style
(
void
)
{
#ifdef _WIN32
return
PATH_STYLE_WIN32
;
#else
return
PATH_STYLE_POSIX
;
#endif
}
static
path_style_t
path_current_style
(
ant_t
*
js
)
{
ant_value_t
data
=
js_get_slot
(
js
->
current_func
,
SLOT_DATA
);
if
(
vtype
(
data
)
==
T_NUM
)
{
int
style
=
(
int
)
js_getnum
(
data
);
if
(
style
==
PATH_STYLE_WIN32
)
return
PATH_STYLE_WIN32
;
if
(
style
==
PATH_STYLE_POSIX
)
return
PATH_STYLE_POSIX
;
}
return
path_host_style
();
}
static
bool
path_style_is_windows
(
path_style_t
style
)
{
return
style
==
PATH_STYLE_WIN32
;
}
static
bool
path_is_sep
(
path_style_t
style
,
char
ch
)
{
if
(
style
==
PATH_STYLE_WIN32
)
return
ch
==
'\\'
||
ch
==
'/'
;
return
ch
==
'/'
;
}
static
char
path_sep_char
(
path_style_t
style
)
{
return
style
==
PATH_STYLE_WIN32
?
'\\'
:
'/'
;
}
static
const
char
*
path_sep_str
(
path_style_t
style
)
{
return
style
==
PATH_STYLE_WIN32
?
"
\\
"
:
"/"
;
}
static
const
char
*
path_delimiter_str
(
path_style_t
style
)
{
return
style
==
PATH_STYLE_WIN32
?
";"
:
":"
;
}
static
bool
path_has_drive_letter
(
const
char
*
path
,
size_t
len
)
{
return
len
>=
2
&&
isalpha
((
unsigned
char
)
path
[
0
])
&&
path
[
1
]
==
':'
;
}
static
bool
path_is_absolute_style
(
path_style_t
style
,
const
char
*
path
,
size_t
len
)
{
if
(
!
path
||
len
==
0
)
return
false
;
if
(
style
==
PATH_STYLE_WIN32
)
{
if
(
path_is_sep
(
style
,
path
[
0
]))
return
true
;
return
len
>=
3
&&
path_has_drive_letter
(
path
,
len
)
&&
path_is_sep
(
style
,
path
[
2
]);
}
return
path
[
0
]
==
'/'
;
}
static
char
*
path_normalize_separators
(
path_style_t
style
,
const
char
*
path
,
size_t
len
)
{
char
*
result
=
malloc
(
len
+
1
);
if
(
!
result
)
return
NULL
;
for
(
size_t
i
=
0
;
i
<
len
;
i
++
)
{
if
(
style
==
PATH_STYLE_WIN32
&&
(
path
[
i
]
==
'\\'
||
path
[
i
]
==
'/'
))
result
[
i
]
=
'\\'
;
else
result
[
i
]
=
path
[
i
];
}
result
[
len
]
=
'\0'
;
return
result
;
}
static
size_t
path_root_length
(
path_style_t
style
,
const
char
*
path
,
size_t
len
)
{
if
(
!
path
||
len
==
0
)
return
0
;
if
(
style
!=
PATH_STYLE_WIN32
)
return
path
[
0
]
==
'/'
?
1
:
0
;
if
(
len
>=
2
&&
path_is_sep
(
style
,
path
[
0
])
&&
path_is_sep
(
style
,
path
[
1
]))
{
size_t
i
=
2
;
int
parts
=
0
;
while
(
i
<
len
)
{
while
(
i
<
len
&&
path_is_sep
(
style
,
path
[
i
]))
i
++
;
size_t
start
=
i
;
while
(
i
<
len
&&
!
path_is_sep
(
style
,
path
[
i
]))
i
++
;
if
(
i
==
start
)
continue
;
parts
++
;
if
(
parts
==
2
)
{
while
(
i
<
len
&&
path_is_sep
(
style
,
path
[
i
]))
i
++
;
return
i
;
}}
return
2
;
}
if
(
path_has_drive_letter
(
path
,
len
))
return
(
len
>=
3
&&
path_is_sep
(
style
,
path
[
2
]))
?
3
:
2
;
return
path_is_sep
(
style
,
path
[
0
])
?
1
:
0
;
}
static
bool
path_is_drive_relative
(
path_style_t
style
,
const
char
*
path
,
size_t
len
)
{
return
style
==
PATH_STYLE_WIN32
&&
path_has_drive_letter
(
path
,
len
)
&&
!
(
len
>=
3
&&
path_is_sep
(
style
,
path
[
2
]));
}
typedef
struct
{
char
*
from_norm
;
char
*
to_norm
;
char
**
from_segs
;
char
**
to_segs
;
size_t
*
from_lens
;
size_t
*
to_lens
;
int
from_count
;
int
to_count
;
size_t
from_root_len
;
size_t
to_root_len
;
int
common
;
char
*
result
;
size_t
result_cap
;
size_t
pos
;
}
path_relative_ctx_t
;
static
size_t
path_trimmed_end
(
path_style_t
style
,
const
char
*
path
,
size_t
len
)
{
size_t
root_len
=
path_root_length
(
style
,
path
,
len
);
while
(
len
>
root_len
&&
path_is_sep
(
style
,
path
[
len
-
1
]))
len
--
;
return
len
;
}
static
size_t
path_basename_start
(
path_style_t
style
,
const
char
*
path
,
size_t
len
)
{
size_t
root_len
=
path_root_length
(
style
,
path
,
len
);
size_t
end
=
path_trimmed_end
(
style
,
path
,
len
);
size_t
start
=
end
;
while
(
start
>
root_len
&&
!
path_is_sep
(
style
,
path
[
start
-
1
]))
start
--
;
return
start
;
}
static
ant_value_t
path_make_string
(
ant_t
*
js
,
const
char
*
src
,
size_t
len
)
{
return
js_mkstr
(
js
,
src
,
len
);
}
static
ant_value_t
builtin_path_basename
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"basename() requires a path argument"
);
if
(
vtype
(
args
[
0
])
!=
T_STR
)
return
js_mkerr
(
js
,
"basename() path must be a string"
);
size_t
path_len
;
char
*
path
=
js_getstr
(
js
,
args
[
0
],
&
path_len
);
if
(
!
path
||
path_len
==
0
)
return
js_mkstr
(
js
,
""
,
0
);
size_t
end
=
path_trimmed_end
(
style
,
path
,
path_len
);
size_t
start
=
path_basename_start
(
style
,
path
,
path_len
);
const
char
*
base
=
path
+
start
;
size_t
base_len
=
end
>
start
?
end
-
start
:
0
;
if
(
nargs
>=
2
&&
vtype
(
args
[
1
])
==
T_STR
)
{
size_t
ext_len
;
char
*
ext
=
js_getstr
(
js
,
args
[
1
],
&
ext_len
);
if
(
ext
&&
ext_len
>
0
&&
base_len
>=
ext_len
)
{
if
(
memcmp
(
base
+
base_len
-
ext_len
,
ext
,
ext_len
)
==
0
)
base_len
-=
ext_len
;
}
}
return
path_make_string
(
js
,
base
,
base_len
);
}
// path.dirname(path)
static
ant_value_t
builtin_path_dirname
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"dirname() requires a path argument"
);
if
(
vtype
(
args
[
0
])
!=
T_STR
)
return
js_mkerr
(
js
,
"dirname() path must be a string"
);
size_t
path_len
;
char
*
path
=
js_getstr
(
js
,
args
[
0
],
&
path_len
);
if
(
!
path
||
path_len
==
0
)
return
js_mkstr
(
js
,
"."
,
1
);
char
*
normalized
=
path_normalize_separators
(
style
,
path
,
path_len
);
size_t
root_len
=
0
;
size_t
end
=
0
;
size_t
cut
=
0
;
if
(
!
normalized
)
return
js_mkerr
(
js
,
"Out of memory"
);
root_len
=
path_root_length
(
style
,
normalized
,
path_len
);
end
=
path_trimmed_end
(
style
,
normalized
,
path_len
);
cut
=
end
;
while
(
cut
>
root_len
&&
!
path_is_sep
(
style
,
normalized
[
cut
-
1
]))
cut
--
;
while
(
cut
>
root_len
&&
path_is_sep
(
style
,
normalized
[
cut
-
1
]))
cut
--
;
if
(
cut
==
0
)
{
free
(
normalized
);
return
js_mkstr
(
js
,
"."
,
1
);
}
if
(
cut
<
root_len
)
cut
=
root_len
;
ant_value_t
result
=
path_make_string
(
js
,
normalized
,
cut
);
free
(
normalized
);
return
result
;
}
// path.extname(path)
static
ant_value_t
builtin_path_extname
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"extname() requires a path argument"
);
if
(
vtype
(
args
[
0
])
!=
T_STR
)
return
js_mkerr
(
js
,
"extname() path must be a string"
);
size_t
path_len
;
char
*
path
=
js_getstr
(
js
,
args
[
0
],
&
path_len
);
if
(
!
path
||
path_len
==
0
)
return
js_mkstr
(
js
,
""
,
0
);
size_t
start
=
path_basename_start
(
style
,
path
,
path_len
);
size_t
end
=
path_trimmed_end
(
style
,
path
,
path_len
);
const
char
*
base
=
path
+
start
;
size_t
base_len
=
end
>
start
?
end
-
start
:
0
;
for
(
size_t
i
=
base_len
;
i
>
0
;
i
--
)
{
if
(
base
[
i
-
1
]
!=
'.'
)
continue
;
if
(
i
-
1
==
0
)
break
;
return
path_make_string
(
js
,
base
+
i
-
1
,
base_len
-
(
i
-
1
));
}
return
js_mkstr
(
js
,
""
,
0
);
}
static
int
is_dotdot
(
const
char
*
seg
,
size_t
len
)
{
return
len
==
2
&&
seg
[
0
]
==
'.'
&&
seg
[
1
]
==
'.'
;
}
static
char
*
normalize_path_full
(
path_style_t
style
,
const
char
*
path
,
size_t
path_len
)
{
char
*
normalized
=
NULL
;
char
**
segments
=
NULL
;
size_t
*
seg_lens
=
NULL
;
char
*
result
=
NULL
;
char
sep
=
path_sep_char
(
style
);
size_t
root_len
=
0
;
bool
drive_relative
=
false
;
if
(
path_len
==
0
)
return
strdup
(
"."
);
normalized
=
path_normalize_separators
(
style
,
path
,
path_len
);
if
(
!
normalized
)
goto
fail
;
segments
=
malloc
(
path_len
*
sizeof
(
char
*
));
seg_lens
=
malloc
(
path_len
*
sizeof
(
size_t
));
if
(
!
segments
||
!
seg_lens
)
goto
fail
;
root_len
=
path_root_length
(
style
,
normalized
,
path_len
);
drive_relative
=
path_is_drive_relative
(
style
,
normalized
,
path_len
);
int
seg_count
=
0
;
char
*
start
=
normalized
+
root_len
;
char
*
seg_start
=
start
;
for
(
char
*
p
=
start
;
;
p
++
)
{
if
(
!
path_is_sep
(
style
,
*
p
)
&&
*
p
!=
'\0'
)
continue
;
size_t
len
=
p
-
seg_start
;
if
(
len
==
0
||
(
len
==
1
&&
seg_start
[
0
]
==
'.'
))
goto
next
;
if
(
is_dotdot
(
seg_start
,
len
))
{
if
(
seg_count
>
0
&&
!
is_dotdot
(
segments
[
seg_count
-
1
],
seg_lens
[
seg_count
-
1
]))
seg_count
--
;
else
if
(
root_len
==
0
||
drive_relative
)
goto
add_segment
;
goto
next
;
}
add_segment
:
segments
[
seg_count
]
=
seg_start
;
seg_lens
[
seg_count
]
=
len
;
seg_count
++
;
next
:
if
(
*
p
==
'\0'
)
break
;
seg_start
=
p
+
1
;
}
size_t
result_len
=
root_len
;
for
(
int
i
=
0
;
i
<
seg_count
;
i
++
)
{
result_len
+=
seg_lens
[
i
];
if
(
i
<
seg_count
-
1
)
result_len
++
;
}
if
(
result_len
==
0
)
result_len
=
1
;
result
=
malloc
(
result_len
+
1
);
if
(
!
result
)
goto
fail
;
size_t
pos
=
0
;
if
(
root_len
>
0
)
{
memcpy
(
result
+
pos
,
normalized
,
root_len
);
pos
+=
root_len
;
}
for
(
int
i
=
0
;
i
<
seg_count
;
i
++
)
{
if
(
pos
>
0
&&
result
[
pos
-
1
]
!=
sep
&&
!
(
drive_relative
&&
pos
==
root_len
))
result
[
pos
++
]
=
sep
;
memcpy
(
result
+
pos
,
segments
[
i
],
seg_lens
[
i
]);
pos
+=
seg_lens
[
i
];
}
if
(
pos
==
0
)
result
[
pos
++
]
=
'.'
;
if
(
path_len
>
root_len
&&
path_is_sep
(
style
,
path
[
path_len
-
1
])
&&
pos
>
0
&&
!
path_is_sep
(
style
,
result
[
pos
-
1
]))
{
result
=
realloc
(
result
,
pos
+
2
);
if
(
!
result
)
goto
fail
;
result
[
pos
++
]
=
sep
;
}
result
[
pos
]
=
'\0'
;
free
(
segments
);
free
(
seg_lens
);
free
(
normalized
);
return
result
;
fail
:
free
(
segments
);
free
(
seg_lens
);
free
(
normalized
);
return
NULL
;
}
// path.normalize(path)
static
ant_value_t
builtin_path_normalize
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"normalize() requires a path argument"
);
if
(
vtype
(
args
[
0
])
!=
T_STR
)
return
js_mkerr
(
js
,
"normalize() path must be a string"
);
size_t
path_len
;
char
*
path
=
js_getstr
(
js
,
args
[
0
],
&
path_len
);
if
(
!
path
||
path_len
==
0
)
return
js_mkstr
(
js
,
"."
,
1
);
char
*
result
=
normalize_path_full
(
style
,
path
,
path_len
);
if
(
!
result
)
return
js_mkerr
(
js
,
"Out of memory"
);
ant_value_t
ret
=
js_mkstr
(
js
,
result
,
strlen
(
result
));
free
(
result
);
return
ret
;
}
// path.join(...paths)
static
ant_value_t
builtin_path_join
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
char
sep
=
path_sep_char
(
style
);
if
(
nargs
<
1
)
return
js_mkstr
(
js
,
"."
,
1
);
size_t
total_len
=
0
;
char
**
segments
=
malloc
(
nargs
*
sizeof
(
char
*
));
size_t
*
lengths
=
malloc
(
nargs
*
sizeof
(
size_t
));
if
(
!
segments
||
!
lengths
)
{
free
(
segments
);
free
(
lengths
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
int
valid_segments
=
0
;
for
(
int
i
=
0
;
i
<
nargs
;
i
++
)
{
if
(
vtype
(
args
[
i
])
==
T_STR
)
{
segments
[
valid_segments
]
=
js_getstr
(
js
,
args
[
i
],
&
lengths
[
valid_segments
]);
if
(
segments
[
valid_segments
]
&&
lengths
[
valid_segments
]
>
0
)
{
total_len
+=
lengths
[
valid_segments
]
+
1
;
// +1 for separator
valid_segments
++
;
}}}
if
(
valid_segments
==
0
)
{
free
(
segments
);
free
(
lengths
);
return
js_mkstr
(
js
,
"."
,
1
);
}
char
*
result
=
malloc
(
total_len
+
1
);
if
(
!
result
)
{
free
(
segments
);
free
(
lengths
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
size_t
pos
=
0
;
for
(
int
i
=
0
;
i
<
valid_segments
;
i
++
)
{
if
(
i
>
0
&&
pos
>
0
&&
result
[
pos
-
1
]
!=
sep
)
{
result
[
pos
++
]
=
sep
;
}
size_t
start
=
0
;
if
(
i
>
0
&&
path_is_sep
(
style
,
segments
[
i
][
0
]))
start
=
1
;
size_t
seg_len
=
lengths
[
i
];
while
(
seg_len
>
start
+
1
&&
path_is_sep
(
style
,
segments
[
i
][
seg_len
-
1
]))
seg_len
--
;
if
(
seg_len
>
start
)
{
memcpy
(
result
+
pos
,
segments
[
i
]
+
start
,
seg_len
-
start
);
pos
+=
seg_len
-
start
;
}
}
if
(
valid_segments
>
0
&&
lengths
[
valid_segments
-
1
]
>
0
&&
path_is_sep
(
style
,
segments
[
valid_segments
-
1
][
lengths
[
valid_segments
-
1
]
-
1
])
&&
pos
>
0
&&
!
path_is_sep
(
style
,
result
[
pos
-
1
])
)
result
[
pos
++
]
=
sep
;
result
[
pos
]
=
'\0'
;
char
*
normalized
=
normalize_path_full
(
style
,
result
,
pos
);
free
(
result
);
free
(
segments
);
free
(
lengths
);
if
(
!
normalized
)
return
js_mkerr
(
js
,
"Out of memory"
);
ant_value_t
ret
=
js_mkstr
(
js
,
normalized
,
strlen
(
normalized
));
free
(
normalized
);
return
ret
;
}
// path.resolve(...paths)
static
ant_value_t
builtin_path_resolve
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
char
cwd
[
PATH_MAX
];
char
*
cwd_norm
=
NULL
;
char
*
joined
=
NULL
;
char
*
normalized
=
NULL
;
char
drive_prefix
[
3
]
=
{
0
};
bool
saw_absolute
=
false
;
char
sep
=
path_sep_char
(
style
);
if
(
getcwd
(
cwd
,
sizeof
(
cwd
))
==
NULL
)
return
js_mkerr
(
js
,
"Failed to get current working directory"
);
cwd_norm
=
path_normalize_separators
(
style
,
cwd
,
strlen
(
cwd
));
if
(
!
cwd_norm
)
return
js_mkerr
(
js
,
"Out of memory"
);
for
(
int
i
=
nargs
-
1
;
i
>=
0
;
i
--
)
{
char
*
next
=
NULL
;
size_t
seg_len
=
0
;
size_t
joined_len
=
0
;
size_t
next_len
=
0
;
char
*
segment
=
NULL
;
size_t
prefix_len
=
0
;
if
(
vtype
(
args
[
i
])
!=
T_STR
)
continue
;
segment
=
js_getstr
(
js
,
args
[
i
],
&
seg_len
);
if
(
!
segment
||
seg_len
==
0
)
continue
;
if
(
style
==
PATH_STYLE_WIN32
&&
path_is_drive_relative
(
style
,
segment
,
seg_len
))
{
if
(
drive_prefix
[
0
]
==
'\0'
)
{
drive_prefix
[
0
]
=
segment
[
0
];
drive_prefix
[
1
]
=
':'
;
drive_prefix
[
2
]
=
'\0'
;
}
prefix_len
=
2
;
}
if
(
!
joined
)
{
joined
=
strndup
(
segment
+
prefix_len
,
seg_len
-
prefix_len
);
if
(
!
joined
)
{
free
(
cwd_norm
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
}
else
{
joined_len
=
strlen
(
joined
);
next_len
=
(
seg_len
-
prefix_len
)
+
1
+
joined_len
+
1
;
next
=
malloc
(
next_len
);
if
(
!
next
)
{
free
(
cwd_norm
);
free
(
joined
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
snprintf
(
next
,
next_len
,
"%.*s%c%s"
,
(
int
)(
seg_len
-
prefix_len
),
segment
+
prefix_len
,
sep
,
joined
);
free
(
joined
);
joined
=
next
;
}
if
(
path_is_absolute_style
(
style
,
segment
,
seg_len
))
{
saw_absolute
=
true
;
break
;
}
}
if
(
!
joined
)
{
if
(
style
==
PATH_STYLE_WIN32
&&
drive_prefix
[
0
]
!=
'\0'
)
{
size_t
cwd_len
=
strlen
(
cwd_norm
);
joined
=
malloc
(
2
+
cwd_len
+
1
);
if
(
!
joined
)
{
free
(
cwd_norm
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
snprintf
(
joined
,
2
+
cwd_len
+
1
,
"%s%s"
,
drive_prefix
,
cwd_norm
);
}
else
{
joined
=
strdup
(
cwd_norm
);
if
(
!
joined
)
{
free
(
cwd_norm
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
}
}
else
if
(
!
saw_absolute
)
{
size_t
cwd_len
=
strlen
(
cwd_norm
);
size_t
joined_len
=
strlen
(
joined
);
size_t
prefix_len
=
(
style
==
PATH_STYLE_WIN32
&&
drive_prefix
[
0
]
!=
'\0'
)
?
2
:
0
;
char
*
next
=
malloc
(
prefix_len
+
cwd_len
+
1
+
joined_len
+
1
);
if
(
!
next
)
{
free
(
cwd_norm
);
free
(
joined
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
if
(
prefix_len
>
0
)
snprintf
(
next
,
prefix_len
+
cwd_len
+
1
+
joined_len
+
1
,
"%s%s%c%s"
,
drive_prefix
,
cwd_norm
,
sep
,
joined
);
else
snprintf
(
next
,
prefix_len
+
cwd_len
+
1
+
joined_len
+
1
,
"%s%c%s"
,
cwd_norm
,
sep
,
joined
);
free
(
joined
);
joined
=
next
;
}
normalized
=
normalize_path_full
(
style
,
joined
,
strlen
(
joined
));
free
(
cwd_norm
);
free
(
joined
);
if
(
!
normalized
)
return
js_mkerr
(
js
,
"Out of memory"
);
ant_value_t
ret
=
js_mkstr
(
js
,
normalized
,
strlen
(
normalized
));
free
(
normalized
);
return
ret
;
}
// path.relative(from, to)
static
ant_value_t
builtin_path_relative
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
path_relative_ctx_t
rel
=
{
0
};
char
sep
=
path_sep_char
(
style
);
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"relative() requires from and to arguments"
);
if
(
vtype
(
args
[
0
])
!=
T_STR
)
return
js_mkerr
(
js
,
"relative() from must be a string"
);
if
(
vtype
(
args
[
1
])
!=
T_STR
)
return
js_mkerr
(
js
,
"relative() to must be a string"
);
size_t
from_len
,
to_len
;
char
*
from
=
js_getstr
(
js
,
args
[
0
],
&
from_len
);
char
*
to
=
js_getstr
(
js
,
args
[
1
],
&
to_len
);
if
(
!
from
||
!
to
)
return
js_mkerr
(
js
,
"Failed to get arguments"
);
if
(
from_len
==
to_len
&&
strncmp
(
from
,
to
,
from_len
)
==
0
)
return
js_mkstr
(
js
,
""
,
0
);
rel
.
from_norm
=
normalize_path_full
(
style
,
from
,
from_len
);
rel
.
to_norm
=
normalize_path_full
(
style
,
to
,
to_len
);
if
(
!
rel
.
from_norm
||
!
rel
.
to_norm
)
goto
relative_fail
;
rel
.
from_root_len
=
path_root_length
(
style
,
rel
.
from_norm
,
strlen
(
rel
.
from_norm
));
rel
.
to_root_len
=
path_root_length
(
style
,
rel
.
to_norm
,
strlen
(
rel
.
to_norm
));
if
(
style
==
PATH_STYLE_WIN32
)
{
if
(
rel
.
from_root_len
!=
rel
.
to_root_len
||
strncasecmp
(
rel
.
from_norm
,
rel
.
to_norm
,
rel
.
from_root_len
)
!=
0
)
{
ant_value_t
out
=
js_mkstr
(
js
,
rel
.
to_norm
,
strlen
(
rel
.
to_norm
));
free
(
rel
.
from_norm
);
free
(
rel
.
to_norm
);
return
out
;
}
}
else
if
(
rel
.
from_root_len
!=
rel
.
to_root_len
||
strncmp
(
rel
.
from_norm
,
rel
.
to_norm
,
rel
.
from_root_len
)
!=
0
)
{
ant_value_t
out
=
js_mkstr
(
js
,
rel
.
to_norm
,
strlen
(
rel
.
to_norm
));
free
(
rel
.
from_norm
);
free
(
rel
.
to_norm
);
return
out
;
}
rel
.
from_segs
=
malloc
(
strlen
(
rel
.
from_norm
)
*
sizeof
(
char
*
));
rel
.
to_segs
=
malloc
(
strlen
(
rel
.
to_norm
)
*
sizeof
(
char
*
));
rel
.
from_lens
=
malloc
(
strlen
(
rel
.
from_norm
)
*
sizeof
(
size_t
));
rel
.
to_lens
=
malloc
(
strlen
(
rel
.
to_norm
)
*
sizeof
(
size_t
));
if
(
!
rel
.
from_segs
||
!
rel
.
to_segs
||
!
rel
.
from_lens
||
!
rel
.
to_lens
)
goto
relative_fail
;
for
(
size_t
i
=
rel
.
from_root_len
,
start
=
rel
.
from_root_len
;;
i
++
)
{
if
(
rel
.
from_norm
[
i
]
!=
'\0'
&&
!
path_is_sep
(
style
,
rel
.
from_norm
[
i
]))
continue
;
if
(
i
>
start
)
{
rel
.
from_segs
[
rel
.
from_count
]
=
rel
.
from_norm
+
start
;
rel
.
from_lens
[
rel
.
from_count
++
]
=
i
-
start
;
}
if
(
rel
.
from_norm
[
i
]
==
'\0'
)
break
;
start
=
i
+
1
;
}
for
(
size_t
i
=
rel
.
to_root_len
,
start
=
rel
.
to_root_len
;;
i
++
)
{
if
(
rel
.
to_norm
[
i
]
!=
'\0'
&&
!
path_is_sep
(
style
,
rel
.
to_norm
[
i
]))
continue
;
if
(
i
>
start
)
{
rel
.
to_segs
[
rel
.
to_count
]
=
rel
.
to_norm
+
start
;
rel
.
to_lens
[
rel
.
to_count
++
]
=
i
-
start
;
}
if
(
rel
.
to_norm
[
i
]
==
'\0'
)
break
;
start
=
i
+
1
;
}
while
(
rel
.
common
<
rel
.
from_count
&&
rel
.
common
<
rel
.
to_count
)
{
bool
equal
=
false
;
if
(
rel
.
from_lens
[
rel
.
common
]
==
rel
.
to_lens
[
rel
.
common
])
{
equal
=
style
==
PATH_STYLE_WIN32
?
strncasecmp
(
rel
.
from_segs
[
rel
.
common
],
rel
.
to_segs
[
rel
.
common
],
rel
.
from_lens
[
rel
.
common
])
==
0
:
strncmp
(
rel
.
from_segs
[
rel
.
common
],
rel
.
to_segs
[
rel
.
common
],
rel
.
from_lens
[
rel
.
common
])
==
0
;
}
if
(
!
equal
)
break
;
rel
.
common
++
;
}
rel
.
result_cap
=
strlen
(
rel
.
to_norm
)
+
(
size_t
)(
rel
.
from_count
-
rel
.
common
)
*
3
+
2
;
rel
.
result
=
malloc
(
rel
.
result_cap
);
if
(
!
rel
.
result
)
goto
relative_fail
;
for
(
int
i
=
rel
.
common
;
i
<
rel
.
from_count
;
i
++
)
{
if
(
rel
.
pos
>
0
)
rel
.
result
[
rel
.
pos
++
]
=
sep
;
rel
.
result
[
rel
.
pos
++
]
=
'.'
;
rel
.
result
[
rel
.
pos
++
]
=
'.'
;
}
for
(
int
i
=
rel
.
common
;
i
<
rel
.
to_count
;
i
++
)
{
if
(
rel
.
pos
>
0
)
rel
.
result
[
rel
.
pos
++
]
=
sep
;
memcpy
(
rel
.
result
+
rel
.
pos
,
rel
.
to_segs
[
i
],
rel
.
to_lens
[
i
]);
rel
.
pos
+=
rel
.
to_lens
[
i
];
}
if
(
rel
.
pos
==
0
)
rel
.
result
[
rel
.
pos
++
]
=
'.'
;
rel
.
result
[
rel
.
pos
]
=
'\0'
;
ant_value_t
out
=
js_mkstr
(
js
,
rel
.
result
,
rel
.
pos
);
free
(
rel
.
result
);
free
(
rel
.
from_norm
);
free
(
rel
.
to_norm
);
free
(
rel
.
from_segs
);
free
(
rel
.
to_segs
);
free
(
rel
.
from_lens
);
free
(
rel
.
to_lens
);
return
out
;
relative_fail
:
free
(
rel
.
result
);
free
(
rel
.
from_norm
);
free
(
rel
.
to_norm
);
free
(
rel
.
from_segs
);
free
(
rel
.
to_segs
);
free
(
rel
.
from_lens
);
free
(
rel
.
to_lens
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
// path.isAbsolute(path)
static
ant_value_t
builtin_path_isAbsolute
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"isAbsolute() requires a path argument"
);
if
(
vtype
(
args
[
0
])
!=
T_STR
)
return
js_mkerr
(
js
,
"isAbsolute() path must be a string"
);
size_t
path_len
;
char
*
path
=
js_getstr
(
js
,
args
[
0
],
&
path_len
);
if
(
!
path
||
path_len
==
0
)
return
js_false
;
return
js_bool
(
path_is_absolute_style
(
style
,
path
,
path_len
));
}
// path.parse(path)
static
ant_value_t
builtin_path_parse
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"parse() requires a path argument"
);
if
(
vtype
(
args
[
0
])
!=
T_STR
)
return
js_mkerr
(
js
,
"parse() path must be a string"
);
size_t
path_len
;
char
*
path
=
js_getstr
(
js
,
args
[
0
],
&
path_len
);
if
(
!
path
)
return
js_mkerr
(
js
,
"Failed to get path string"
);
ant_value_t
result
=
js_mkobj
(
js
);
char
*
normalized
=
path_normalize_separators
(
style
,
path
,
path_len
);
size_t
root_len
=
0
;
size_t
start
=
0
;
size_t
end
=
0
;
const
char
*
base
=
NULL
;
size_t
base_len
=
0
;
if
(
!
normalized
)
return
js_mkerr
(
js
,
"Out of memory"
);
root_len
=
path_root_length
(
style
,
normalized
,
path_len
);
js_set
(
js
,
result
,
"root"
,
path_make_string
(
js
,
normalized
,
root_len
));
end
=
path_trimmed_end
(
style
,
normalized
,
path_len
);
start
=
path_basename_start
(
style
,
normalized
,
path_len
);
base
=
normalized
+
start
;
base_len
=
end
>
start
?
end
-
start
:
0
;
if
(
start
==
0
)
js_set
(
js
,
result
,
"dir"
,
js_mkstr
(
js
,
"."
,
1
));
else
{
size_t
dir_len
=
start
;
while
(
dir_len
>
root_len
&&
path_is_sep
(
style
,
normalized
[
dir_len
-
1
]))
dir_len
--
;
js_set
(
js
,
result
,
"dir"
,
path_make_string
(
js
,
normalized
,
dir_len
));
}
js_set
(
js
,
result
,
"base"
,
path_make_string
(
js
,
base
,
base_len
));
for
(
size_t
i
=
base_len
;
i
>
0
;
i
--
)
{
if
(
base
[
i
-
1
]
!=
'.'
)
continue
;
if
(
i
-
1
==
0
)
break
;
js_set
(
js
,
result
,
"ext"
,
path_make_string
(
js
,
base
+
i
-
1
,
base_len
-
(
i
-
1
)));
js_set
(
js
,
result
,
"name"
,
path_make_string
(
js
,
base
,
i
-
1
));
free
(
normalized
);
return
result
;
}
js_set
(
js
,
result
,
"ext"
,
js_mkstr
(
js
,
""
,
0
));
js_set
(
js
,
result
,
"name"
,
path_make_string
(
js
,
base
,
base_len
));
free
(
normalized
);
return
result
;
}
// path.format(pathObject)
static
ant_value_t
builtin_path_format
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
path_style_t
style
=
path_current_style
(
js
);
char
sep
=
path_sep_char
(
style
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"format() requires a path object argument"
);
if
(
!
is_special_object
(
args
[
0
]))
return
js_mkerr
(
js
,
"format() argument must be an object"
);
ant_value_t
obj
=
args
[
0
];
ant_value_t
dir_val
=
js_get
(
js
,
obj
,
"dir"
);
ant_value_t
root_val
=
js_get
(
js
,
obj
,
"root"
);
ant_value_t
base_val
=
js_get
(
js
,
obj
,
"base"
);
ant_value_t
name_val
=
js_get
(
js
,
obj
,
"name"
);
ant_value_t
ext_val
=
js_get
(
js
,
obj
,
"ext"
);
char
result
[
PATH_MAX
]
=
{
0
};
size_t
pos
=
0
;
if
(
vtype
(
dir_val
)
==
T_STR
)
{
size_t
len
;
char
*
str
=
js_getstr
(
js
,
dir_val
,
&
len
);
if
(
str
&&
len
>
0
&&
pos
+
len
<
PATH_MAX
)
{
memcpy
(
result
+
pos
,
str
,
len
);
pos
+=
len
;
if
(
result
[
pos
-
1
]
!=
sep
&&
pos
<
PATH_MAX
-
1
)
{
result
[
pos
++
]
=
sep
;
}
}
}
else
if
(
vtype
(
root_val
)
==
T_STR
)
{
size_t
len
;
char
*
str
=
js_getstr
(
js
,
root_val
,
&
len
);
if
(
str
&&
len
>
0
&&
pos
+
len
<
PATH_MAX
)
{
memcpy
(
result
+
pos
,
str
,
len
);
pos
+=
len
;
}
}
if
(
vtype
(
base_val
)
==
T_STR
)
{
size_t
len
;
char
*
str
=
js_getstr
(
js
,
base_val
,
&
len
);
if
(
str
&&
len
>
0
&&
pos
+
len
<
PATH_MAX
)
{
memcpy
(
result
+
pos
,
str
,
len
);
pos
+=
len
;
}
}
else
{
if
(
vtype
(
name_val
)
==
T_STR
)
{
size_t
len
;
char
*
str
=
js_getstr
(
js
,
name_val
,
&
len
);
if
(
str
&&
len
>
0
&&
pos
+
len
<
PATH_MAX
)
{
memcpy
(
result
+
pos
,
str
,
len
);
pos
+=
len
;
}
}
if
(
vtype
(
ext_val
)
==
T_STR
)
{
size_t
len
;
char
*
str
=
js_getstr
(
js
,
ext_val
,
&
len
);
if
(
str
&&
len
>
0
&&
pos
+
len
<
PATH_MAX
)
{
memcpy
(
result
+
pos
,
str
,
len
);
pos
+=
len
;
}
}
}
return
js_mkstr
(
js
,
result
,
pos
);
}
typedef
struct
{
ant_value_t
basename
;
ant_value_t
dirname
;
ant_value_t
extname
;
ant_value_t
join
;
ant_value_t
normalize
;
ant_value_t
resolve
;
ant_value_t
relative
;
ant_value_t
isAbsolute
;
ant_value_t
parse
;
ant_value_t
format
;
}
path_api_t
;
static
path_api_t
path_build_api_for_style
(
ant_t
*
js
,
path_style_t
style
)
{
ant_value_t
style_value
=
js_mknum
((
double
)
style
);
return
(
path_api_t
){
.
basename
=
js_heavy_mkfun
(
js
,
builtin_path_basename
,
style_value
),
.
dirname
=
js_heavy_mkfun
(
js
,
builtin_path_dirname
,
style_value
),
.
extname
=
js_heavy_mkfun
(
js
,
builtin_path_extname
,
style_value
),
.
join
=
js_heavy_mkfun
(
js
,
builtin_path_join
,
style_value
),
.
normalize
=
js_heavy_mkfun
(
js
,
builtin_path_normalize
,
style_value
),
.
resolve
=
js_heavy_mkfun
(
js
,
builtin_path_resolve
,
style_value
),
.
relative
=
js_heavy_mkfun
(
js
,
builtin_path_relative
,
style_value
),
.
isAbsolute
=
js_heavy_mkfun
(
js
,
builtin_path_isAbsolute
,
style_value
),
.
parse
=
js_heavy_mkfun
(
js
,
builtin_path_parse
,
style_value
),
.
format
=
js_heavy_mkfun
(
js
,
builtin_path_format
,
style_value
)
};}
static
void
path_apply_api
(
ant_t
*
js
,
ant_value_t
target
,
const
path_api_t
*
api
)
{
js_set
(
js
,
target
,
"basename"
,
api
->
basename
);
js_set
(
js
,
target
,
"dirname"
,
api
->
dirname
);
js_set
(
js
,
target
,
"extname"
,
api
->
extname
);
js_set
(
js
,
target
,
"join"
,
api
->
join
);
js_set
(
js
,
target
,
"normalize"
,
api
->
normalize
);
js_set
(
js
,
target
,
"resolve"
,
api
->
resolve
);
js_set
(
js
,
target
,
"relative"
,
api
->
relative
);
js_set
(
js
,
target
,
"isAbsolute"
,
api
->
isAbsolute
);
js_set
(
js
,
target
,
"parse"
,
api
->
parse
);
js_set
(
js
,
target
,
"format"
,
api
->
format
);
}
static
ant_value_t
path_make_variant
(
ant_t
*
js
,
const
path_api_t
*
api
,
path_style_t
style
)
{
ant_value_t
variant
=
js_mkobj
(
js
);
path_apply_api
(
js
,
variant
,
api
);
js_set
(
js
,
variant
,
"sep"
,
js_mkstr
(
js
,
path_sep_str
(
style
),
1
));
js_set
(
js
,
variant
,
"delimiter"
,
js_mkstr
(
js
,
path_delimiter_str
(
style
),
1
));
js_set
(
js
,
variant
,
"default"
,
variant
);
return
variant
;
}
ant_value_t
path_library
(
ant_t
*
js
)
{
path_api_t
api
=
path_build_api_for_style
(
js
,
path_host_style
());
path_api_t
posix_api
=
path_build_api_for_style
(
js
,
PATH_STYLE_POSIX
);
path_api_t
win32_api
=
path_build_api_for_style
(
js
,
PATH_STYLE_WIN32
);
ant_value_t
lib
=
path_make_variant
(
js
,
&
api
,
path_host_style
());
ant_value_t
posix
=
path_make_variant
(
js
,
&
posix_api
,
PATH_STYLE_POSIX
);
ant_value_t
win32
=
path_make_variant
(
js
,
&
win32_api
,
PATH_STYLE_WIN32
);
js_set
(
js
,
lib
,
"posix"
,
posix
);
js_set
(
js
,
lib
,
"win32"
,
win32
);
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"path"
,
4
));
js_set
(
js
,
lib
,
"default"
,
lib
);
return
lib
;
}
ant_value_t
path_posix_library
(
ant_t
*
js
)
{
path_api_t
api
=
path_build_api_for_style
(
js
,
PATH_STYLE_POSIX
);
ant_value_t
lib
=
path_make_variant
(
js
,
&
api
,
PATH_STYLE_POSIX
);
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"path"
,
4
));
return
lib
;
}
ant_value_t
path_win32_library
(
ant_t
*
js
)
{
path_api_t
api
=
path_build_api_for_style
(
js
,
PATH_STYLE_WIN32
);
ant_value_t
lib
=
path_make_variant
(
js
,
&
api
,
PATH_STYLE_WIN32
);
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"path"
,
4
));
return
lib
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 5:13 AM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
541696
Default Alt Text
path.c (27 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment