/* file.c - Additional file attributes Copyright (C) 1993 Werner Almesberger Copyright (C) 1998 Roman Hodek Copyright (C) 2008-2014 Daniel Baumann Copyright (C) 2020 Pali Rohár 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, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #include #include #include #include #include #include "common.h" #include "file.h" #include "msdos_fs.h" #include "charconv.h" FDSC *fp_root = NULL; static void put_char(char **p, unsigned char c, unsigned int out_size) { if (dos_char_to_printable(p, c, out_size)) return; if (out_size >= 1 && c >= ' ' && c < 0x7f) *(*p)++ = c; else if (out_size >= 4) { *(*p)++ = '\\'; *(*p)++ = '0' + (c >> 6); *(*p)++ = '0' + ((c >> 3) & 7); *(*p)++ = '0' + (c & 7); } } /** * Construct the "pretty-printed" representation of the name in a short directory entry. * * @param[in] fixed Pointer to name[0] of a DIR_ENT * * @return Pointer to static string containing pretty "8.3" equivalent of the * name in the directory entry. */ char *file_name(unsigned char *fixed) { static char path[256]; char *p; int i, j; p = path; i = j = 0; if (fixed[0] == 0x05) { put_char(&p, 0xe5, path + sizeof(path) - 1 - p); ++i; ++j; } for (; i < 8; i++) if (fixed[i] != ' ') { while (j++ < i) *p++ = ' '; put_char(&p, fixed[i], path + sizeof(path) - 1 - p); } if (strncmp((const char *)(fixed + 8), " ", 3)) { *p++ = '.'; for (i = j = 0; i < 3; i++) if (fixed[i + 8] != ' ') { while (j++ < i) *p++ = ' '; put_char(&p, fixed[i + 8], path + sizeof(path) - 1 - p); } } *p = 0; return path; } int file_cvt(unsigned char *name, unsigned char *fixed) { unsigned char c; int size, ext, cnt; size = 8; ext = 0; while (*name) { c = *name; if (c < ' ' || c > 0x7e || strchr("*?<>|\"/", c)) { printf("Invalid character in name. Use \\ooo for special " "characters.\n"); return 0; } if (c == '.') { if (ext) { printf("Duplicate dots in name.\n"); return 0; } while (size--) *fixed++ = ' '; size = 3; ext = 1; name++; continue; } if (c == '\\') { c = 0; name++; for (cnt = 3; cnt; cnt--) { if (*name < '0' || *name > '7') { printf("Expected three octal digits.\n"); return 0; } c = c * 8 + *name++ - '0'; } name--; } if (islower(c)) c = toupper(c); if (size) { if (size == 8 && c == 0xE5) *fixed++ = 0x05; else *fixed++ = c; size--; } name++; } if (*name || size == 8) return 0; if (!ext) { while (size--) *fixed++ = ' '; size = 3; } while (size--) *fixed++ = ' '; return 1; } void file_add(char *path, FD_TYPE type) { FDSC **current, *walk; char name[MSDOS_NAME]; char *here; current = &fp_root; if (*path != '/') die("%s: Absolute path required.", path); path++; while (1) { if ((here = strchr(path, '/'))) *here = 0; if (!file_cvt((unsigned char *)path, (unsigned char *)name)) exit(2); for (walk = *current; walk; walk = walk->next) if (!here && (!strncmp(name, walk->name, MSDOS_NAME) || (type == fdt_undelete && !strncmp (name + 1, walk->name + 1, MSDOS_NAME - 1)))) die("Ambiguous name: \"%s\"", path); else if (here && !strncmp(name, walk->name, MSDOS_NAME)) break; if (!walk) { walk = alloc(sizeof(FDSC)); strncpy(walk->name, name, MSDOS_NAME); walk->type = here ? fdt_none : type; walk->first = NULL; walk->next = *current; *current = walk; } current = &walk->first; if (!here) break; *here = '/'; path = here + 1; } } FDSC **file_cd(FDSC ** curr, char *fixed) { FDSC **walk; if (!curr || !*curr) return NULL; for (walk = curr; *walk; walk = &(*walk)->next) if (!strncmp((*walk)->name, fixed, MSDOS_NAME) && (*walk)->first) return &(*walk)->first; return NULL; } static FDSC **file_find(FDSC ** dir, char *fixed) { if (!dir || !*dir) return NULL; if (*(unsigned char *)fixed == DELETED_FLAG) { while (*dir) { if (!strncmp((*dir)->name + 1, fixed + 1, MSDOS_NAME - 1) && !(*dir)->first) return dir; dir = &(*dir)->next; } return NULL; } while (*dir) { if (!strncmp((*dir)->name, fixed, MSDOS_NAME) && !(*dir)->first) return dir; dir = &(*dir)->next; } return NULL; } /* Returns the attribute of the file FIXED in directory CURR or FDT_NONE if no such file exists or if CURR is NULL. */ FD_TYPE file_type(FDSC ** curr, char *fixed) { FDSC **this; if ((this = file_find(curr, fixed))) return (*this)->type; return fdt_none; } void file_modify(FDSC ** curr, char *fixed) { FDSC **this, *next; if (!(this = file_find(curr, fixed))) die("Internal error: file_find failed"); switch ((*this)->type) { case fdt_drop: printf("Dropping %s\n", file_name((unsigned char *)fixed)); *(unsigned char *)fixed = DELETED_FLAG; break; case fdt_undelete: *fixed = *(*this)->name; printf("Undeleting %s\n", file_name((unsigned char *)fixed)); break; default: die("Internal error: file_modify"); } next = (*this)->next; free(*this); *this = next; } static void report_unused(FDSC * this) { FDSC *next; while (this) { next = this->next; if (this->first) report_unused(this->first); else if (this->type != fdt_none) printf("Warning: did not %s file %s\n", this->type == fdt_drop ? "drop" : "undelete", file_name((unsigned char *)this->name)); free(this); this = next; } } void file_unused(void) { report_unused(fp_root); }