Imported Upstream version 4.2
[platform/upstream/dosfstools.git] / src / file.c
1 /* file.c - Additional file attributes
2
3    Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
4    Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5    Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
6    Copyright (C) 2020 Pali Rohár <pali.rohar@gmail.com>
7
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21    The complete text of the GNU General Public License
22    can be found in /usr/share/common-licenses/GPL-3 file.
23 */
24
25 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
26  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <unistd.h>
33
34 #include "common.h"
35 #include "file.h"
36 #include "msdos_fs.h"
37 #include "charconv.h"
38
39 FDSC *fp_root = NULL;
40
41 static void put_char(char **p, unsigned char c, unsigned int out_size)
42 {
43     if (dos_char_to_printable(p, c, out_size))
44         return;
45     if (out_size >= 1 && c >= ' ' && c < 0x7f)
46         *(*p)++ = c;
47     else if (out_size >= 4) {
48         *(*p)++ = '\\';
49         *(*p)++ = '0' + (c >> 6);
50         *(*p)++ = '0' + ((c >> 3) & 7);
51         *(*p)++ = '0' + (c & 7);
52     }
53 }
54
55 /**
56  * Construct the "pretty-printed" representation of the name in a short directory entry.
57  *
58  * @param[in]    fixed  Pointer to name[0] of a DIR_ENT
59  *
60  * @return  Pointer to static string containing pretty "8.3" equivalent of the
61  *          name in the directory entry.
62  */
63 char *file_name(unsigned char *fixed)
64 {
65     static char path[256];
66     char *p;
67     int i, j;
68
69     p = path;
70     i = j = 0;
71     if (fixed[0] == 0x05) {
72         put_char(&p, 0xe5, path + sizeof(path) - 1 - p);
73         ++i;
74         ++j;
75     }
76     for (; i < 8; i++)
77         if (fixed[i] != ' ') {
78             while (j++ < i)
79                 *p++ = ' ';
80             put_char(&p, fixed[i], path + sizeof(path) - 1 - p);
81         }
82     if (strncmp((const char *)(fixed + 8), "   ", 3)) {
83         *p++ = '.';
84         for (i = j = 0; i < 3; i++)
85             if (fixed[i + 8] != ' ') {
86                 while (j++ < i)
87                     *p++ = ' ';
88                 put_char(&p, fixed[i + 8], path + sizeof(path) - 1 - p);
89             }
90     }
91     *p = 0;
92     return path;
93 }
94
95 int file_cvt(unsigned char *name, unsigned char *fixed)
96 {
97     unsigned char c;
98     int size, ext, cnt;
99
100     size = 8;
101     ext = 0;
102     while (*name) {
103         c = *name;
104         if (c < ' ' || c > 0x7e || strchr("*?<>|\"/", c)) {
105             printf("Invalid character in name. Use \\ooo for special "
106                    "characters.\n");
107             return 0;
108         }
109         if (c == '.') {
110             if (ext) {
111                 printf("Duplicate dots in name.\n");
112                 return 0;
113             }
114             while (size--)
115                 *fixed++ = ' ';
116             size = 3;
117             ext = 1;
118             name++;
119             continue;
120         }
121         if (c == '\\') {
122             c = 0;
123             name++;
124             for (cnt = 3; cnt; cnt--) {
125                 if (*name < '0' || *name > '7') {
126                     printf("Expected three octal digits.\n");
127                     return 0;
128                 }
129                 c = c * 8 + *name++ - '0';
130             }
131             name--;
132         }
133         if (islower(c))
134             c = toupper(c);
135         if (size) {
136             if (size == 8 && c == 0xE5)
137                 *fixed++ = 0x05;
138             else
139                 *fixed++ = c;
140             size--;
141         }
142         name++;
143     }
144     if (*name || size == 8)
145         return 0;
146     if (!ext) {
147         while (size--)
148             *fixed++ = ' ';
149         size = 3;
150     }
151     while (size--)
152         *fixed++ = ' ';
153     return 1;
154 }
155
156 void file_add(char *path, FD_TYPE type)
157 {
158     FDSC **current, *walk;
159     char name[MSDOS_NAME];
160     char *here;
161
162     current = &fp_root;
163     if (*path != '/')
164         die("%s: Absolute path required.", path);
165     path++;
166     while (1) {
167         if ((here = strchr(path, '/')))
168             *here = 0;
169         if (!file_cvt((unsigned char *)path, (unsigned char *)name))
170             exit(2);
171         for (walk = *current; walk; walk = walk->next)
172             if (!here && (!strncmp(name, walk->name, MSDOS_NAME) || (type ==
173                                                                      fdt_undelete
174                                                                      &&
175                                                                      !strncmp
176                                                                      (name + 1,
177                                                                       walk->name
178                                                                       + 1,
179                                                                       MSDOS_NAME
180                                                                       - 1))))
181                 die("Ambiguous name: \"%s\"", path);
182             else if (here && !strncmp(name, walk->name, MSDOS_NAME))
183                 break;
184         if (!walk) {
185             walk = alloc(sizeof(FDSC));
186             strncpy(walk->name, name, MSDOS_NAME);
187             walk->type = here ? fdt_none : type;
188             walk->first = NULL;
189             walk->next = *current;
190             *current = walk;
191         }
192         current = &walk->first;
193         if (!here)
194             break;
195         *here = '/';
196         path = here + 1;
197     }
198 }
199
200 FDSC **file_cd(FDSC ** curr, char *fixed)
201 {
202     FDSC **walk;
203
204     if (!curr || !*curr)
205         return NULL;
206     for (walk = curr; *walk; walk = &(*walk)->next)
207         if (!strncmp((*walk)->name, fixed, MSDOS_NAME) && (*walk)->first)
208             return &(*walk)->first;
209     return NULL;
210 }
211
212 static FDSC **file_find(FDSC ** dir, char *fixed)
213 {
214     if (!dir || !*dir)
215         return NULL;
216     if (*(unsigned char *)fixed == DELETED_FLAG) {
217         while (*dir) {
218             if (!strncmp((*dir)->name + 1, fixed + 1, MSDOS_NAME - 1)
219                 && !(*dir)->first)
220                 return dir;
221             dir = &(*dir)->next;
222         }
223         return NULL;
224     }
225     while (*dir) {
226         if (!strncmp((*dir)->name, fixed, MSDOS_NAME) && !(*dir)->first)
227             return dir;
228         dir = &(*dir)->next;
229     }
230     return NULL;
231 }
232
233 /* Returns the attribute of the file FIXED in directory CURR or FDT_NONE if no
234    such file exists or if CURR is NULL. */
235 FD_TYPE file_type(FDSC ** curr, char *fixed)
236 {
237     FDSC **this;
238
239     if ((this = file_find(curr, fixed)))
240         return (*this)->type;
241     return fdt_none;
242 }
243
244 void file_modify(FDSC ** curr, char *fixed)
245 {
246     FDSC **this, *next;
247
248     if (!(this = file_find(curr, fixed)))
249         die("Internal error: file_find failed");
250     switch ((*this)->type) {
251     case fdt_drop:
252         printf("Dropping %s\n", file_name((unsigned char *)fixed));
253         *(unsigned char *)fixed = DELETED_FLAG;
254         break;
255     case fdt_undelete:
256         *fixed = *(*this)->name;
257         printf("Undeleting %s\n", file_name((unsigned char *)fixed));
258         break;
259     default:
260         die("Internal error: file_modify");
261     }
262     next = (*this)->next;
263     free(*this);
264     *this = next;
265 }
266
267 static void report_unused(FDSC * this)
268 {
269     FDSC *next;
270
271     while (this) {
272         next = this->next;
273         if (this->first)
274             report_unused(this->first);
275         else if (this->type != fdt_none)
276             printf("Warning: did not %s file %s\n", this->type == fdt_drop ?
277                    "drop" : "undelete", file_name((unsigned char *)this->name));
278         free(this);
279         this = next;
280     }
281 }
282
283 void file_unused(void)
284 {
285     report_unused(fp_root);
286 }