1 #include <stdlib.h>
2 #include <string.h>
3 
4 #include "util.h"
5 
6 #include "filename.h"
7 
8 #ifdef _WIN32
9 #define PATHSEP '\\'
10 #else
11 #define PATHSEP '/'
12 #endif
13 
14 struct Filename
15 {
16     char *full;
17     char *dir;
18     char *base;
19     char *ext;
20 };
21 
Filename_create(void)22 SOLOCAL Filename *Filename_create(void)
23 {
24     Filename *self = xmalloc(sizeof *self);
25     memset(self, 0, sizeof *self);
26     return self;
27 }
28 
Filename_clone(const Filename * self)29 SOLOCAL Filename *Filename_clone(const Filename *self)
30 {
31     Filename *clone = Filename_create();
32     if (self->full) clone->full = copystr(self->full);
33     if (self->dir) clone->dir = copystr(self->dir);
34     if (self->base) clone->base = copystr(self->base);
35     if (self->ext) clone->ext = copystr(self->ext);
36     return clone;
37 }
38 
Filename_full(const Filename * self)39 SOLOCAL const char *Filename_full(const Filename *self)
40 {
41     return self->full;
42 }
43 
Filename_dir(const Filename * self)44 SOLOCAL const char *Filename_dir(const Filename *self)
45 {
46     return self->dir;
47 }
48 
Filename_base(const Filename * self)49 SOLOCAL const char *Filename_base(const Filename *self)
50 {
51     return self->base;
52 }
53 
Filename_ext(const Filename * self)54 SOLOCAL const char *Filename_ext(const Filename *self)
55 {
56     return self->ext;
57 }
58 
Filename_setFull(Filename * self,const char * full)59 SOLOCAL void Filename_setFull(Filename *self, const char *full)
60 {
61     free(self->full);
62     free(self->dir);
63     free(self->base);
64     free(self->ext);
65     memset(self, 0, sizeof *self);
66     if (!full) return;
67     self->full = copystr(full);
68 #ifdef _WIN32
69     for (char *c = self->full; *c; ++c)
70     {
71         if (*c == '/') *c = PATHSEP;
72     }
73 #endif
74     const char *pathsep = strrchr(self->full, PATHSEP);
75     const char *base;
76     if (pathsep)
77     {
78 	size_t dirlen = pathsep - self->full;
79 	self->dir = xmalloc(dirlen + 1);
80 	strncpy(self->dir, self->full, dirlen);
81 	self->dir[dirlen] = 0;
82 	base = pathsep+1;
83     }
84     else
85     {
86 	base = self->full;
87     }
88 
89     const char *extsep = *base ? strrchr(base, '.') : 0;
90     if (extsep)
91     {
92 	self->ext = copystr(extsep+1);
93 	size_t baselen = extsep - base;
94 	self->base = xmalloc(baselen + 1);
95 	strncpy(self->base, base, baselen);
96 	self->base[baselen] = 0;
97     }
98     else
99     {
100 	self->base = *base ? copystr(base) : 0;
101     }
102 }
103 
Filename_updateFull(Filename * self)104 static void Filename_updateFull(Filename *self)
105 {
106     free(self->full);
107     if (!self->base)
108     {
109 	self->full = 0;
110 	return;
111     }
112     size_t fulllen = strlen(self->base);
113     if (self->dir) fulllen += strlen(self->dir) + 1;
114     if (self->ext) fulllen += strlen(self->ext) + 1;
115     self->full = xmalloc(fulllen+1);
116     char *wrptr = self->full;
117     if (self->dir)
118     {
119 	strcpy(wrptr, self->dir);
120 	wrptr += strlen(self->dir);
121 	*wrptr++ = PATHSEP;
122     }
123     strcpy(wrptr, self->base);
124     wrptr += strlen(self->base);
125     if (self->ext)
126     {
127 	*wrptr++ = '.';
128 	strcpy(wrptr, self->ext);
129     }
130 }
131 
Filename_setDir(Filename * self,const char * dir)132 SOLOCAL void Filename_setDir(Filename *self, const char *dir)
133 {
134     free(self->dir);
135     self->dir = copystr(dir);
136     Filename_updateFull(self);
137 }
138 
Filename_setBase(Filename * self,const char * base)139 SOLOCAL void Filename_setBase(Filename *self, const char *base)
140 {
141     free(self->base);
142     self->base = copystr(base);
143     Filename_updateFull(self);
144 }
145 
Filename_setExt(Filename * self,const char * ext)146 SOLOCAL void Filename_setExt(Filename *self, const char *ext)
147 {
148     free(self->ext);
149     self->ext = copystr(ext);
150     Filename_updateFull(self);
151 }
152 
Filename_destroy(Filename * self)153 SOLOCAL void Filename_destroy(Filename *self)
154 {
155     if (!self) return;
156     free(self->full);
157     free(self->dir);
158     free(self->base);
159     free(self->ext);
160     free(self);
161 }
162 
163