-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathATTRIB.C
344 lines (262 loc) · 9.4 KB
/
ATTRIB.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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
/* Program: ATTRIB
Written by: Phil Brutsche
Copyright: 1998 by Phil Brutsche, under the terms of the GNU GPL 2.
Maintained by: Brian E. Reifsnyder
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <dos.h>
#include <stdio.h>
#include <string.h>
#include "types.h"
#define VERSION "2.1"
#define EXIT_OK 0
#define E_ACCESS 1
#define E_TARGET 2
#define E_LIST 3
#define E_OPTION 4
#define ALL_FILE (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH)
#define ALL_ATTR (ALL_FILE | _A_SUBDIR)
#define DIR_ATTR ALL_ATTR
/*======================================================================*/
typedef byte ATTR;
/* do_path() requires less than 64 bytes of stack and path length is */
/* less than 80 characters (ie. there less than 40 nested directories */
/* possible), consequently deepest recursion requires 64*40=2.5K. With */
/* path equal to 256 characters deepest recursion requires 64*128=8K. */
extern unsigned _Cdecl _stklen = 9*1024u; /* TC/BC specific */
LOCAL byte recurse = 0;
LOCAL ATTR useattr = ALL_FILE;
LOCAL ATTR attr_keep = ~0u, attr_set = 0;
LOCAL ATTR findattr;
LOCAL char path [PATHLEN+4], /* 4=strlen("\\*.*") */
mask [PATHLEN];
LOCAL char info [] = "[-----] -> [-----] ";
/* _^______^___^ */
#define OLD_ATTR (info+1)
#define NEW_PART (info+8)
#define NEW_ATTR (info+12)
LOCAL unsigned found;
LOCAL byte retcode = EXIT_OK;
/*----------------------------------------------------------------------*/
LOCAL char *PROC adds (char dst[], const char src[]);
LOCAL ATTR PROC attr2str(char str[], ATTR attr);
LOCAL void PROC do_mask (char *pathend);
LOCAL void PROC do_path (char *pathend);
LOCAL char *PROC trimsq (char *p, char *pend);
LOCAL char *PROC nsplit (char *pathend);
LOCAL void PROC process (const char target[]);
LOCAL void PROC list (const char name[]);
LOCAL void PROC _s_ (const char s[]);
LOCAL void PROC say (const char s1[], const char s2[]);
LOCAL void PROC error (byte errcode, const char arg[], const char err[]);
#define sayerror(code,err,arg) error (code,arg,err)
/*======================================================================*/
LOCAL char *PROC adds (char dst[], const char src[]) {
return stpcpy (dst, src);
}
LOCAL ATTR PROC attr2str (char attr_str[], ATTR attr) {
char ch;
ch = '-'; if (attr & _A_SUBDIR) ch = 'D'; attr_str [0] = ch;
ch = '-'; if (attr & _A_HIDDEN) ch = 'H'; attr_str [1] = ch;
ch = '-'; if (attr & _A_SYSTEM) ch = 'S'; attr_str [2] = ch;
ch = '-'; if (attr & _A_RDONLY) ch = 'R'; attr_str [3] = ch;
ch = '-'; if (attr & _A_ARCH) ch = 'A'; attr_str [4] = ch;
return attr;
}
/*----------------------------------------------------------------------*/
LOCAL void PROC do_mask (char *pathend) {
static struct find_t fi;
adds (pathend, mask);
if (_dos_findfirst (path, findattr, &fi))
return;
do { const char *p;
p = fi.name-1; do p++; while (*p == '.');
if (*p == '\0') continue; /* name == dots */
adds (pathend, fi.name); found++;
{ ATTR attr = attr2str (OLD_ATTR, fi.attrib);
if (*NEW_PART != '\0') {
attr = (attr & attr_keep) | attr_set;
if (_dos_setfileattr (path,
attr2str (NEW_ATTR, attr)
& (ATTR)~_A_SUBDIR)) {
sayerror (E_ACCESS, "access denied", path);
continue;
} } }
say (info, path);
} while (_dos_findnext (&fi) == 0);
}
LOCAL void PROC do_path (char *pathend) {
struct find_t fi;
do_mask (pathend); if (recurse == 0) return;
adds (pathend, "*.*");
if (_dos_findfirst (path, DIR_ATTR, &fi))
return;
do { const char *p;
if ((fi.attrib & _A_SUBDIR) == 0) continue;
p = fi.name-1; do p++; while (*p == '.');
if (*p == '\0') continue; /* name == dots */
do_path (adds (adds (pathend, fi.name), "\\"));
} while (_dos_findnext (&fi) == 0);
}
/*----------------------------------------------------------------------*/
LOCAL char *PROC trimsq (char *p, char *pend) {
do { if (pend == p) { *pend = '\0'; return p; }
/*!*/ pend--;
} while (*pend == ' ' || *pend == '\t');/* trailing spaces, */
while (*p == ' ' || *p == '\t') p++; /* ...leading spaces */
if (p < pend && *p == '"' && *pend == '"')
p++, pend--; /* ...and quotes */
pend++, *pend = '\0';
return p;
}
LOCAL char *PROC nsplit (char *p) {
char *pend = p; ATTR fattr = ALL_ATTR;
/*--- scan trailing name */
for (;;) {
if (p == path) break;
/*!*/ p--;
if (*p == '.') continue; /* skip trailing dots */
if (*p != '\\' && *p != '/' && *p != ':') {
pend = NULL; /* this is filename */
if (*p == '*' || *p == '?') /* check wildcards */
fattr = useattr;
} else {
p++; break;
} }
/*--- extract name, if present */
{ char *q = p;
if (pend) { /* path="[[<something>]\]{.}" */
if (p < pend) p = pend, *p++ = '\\';
fattr = useattr, q = "*.*"; /* default mask value */
}
findattr = fattr, adds (mask, q);
return p;
} }
/*----------------------------------------------------------------------*/
LOCAL void PROC process (const char target[]) {
/* if (strlen(target) > sizeof(path)-5) {
* sayerror (E_TARGET, "too long", target);
* return;
* }
*/ found = 0;
do_path (nsplit (adds (path, target)));
if (found == 0) sayerror (E_TARGET, "no targets", target);
}
LOCAL void PROC list (const char name[]) {
FILE *fp = stdin;
if (name[0] && (fp = fopen (name, "rt")) == NULL) {
sayerror (E_LIST, "error open file", name);
return;
}
{ static char line [PATHLEN]; register int eof;
do { register char *p = line; int ch;
do { ch = fgetc (fp);
if (ch == EOF || ch == '\n') break;
*p++ = ch;
} while (p < &line [sizeof (line)-1]);
eof = ch, p = trimsq (line, p);
if (*p) process (strupr (p));
/*!*/ } while (eof -= EOF);
}
if (fp != stdin) fclose (fp);
}
/*----------------------------------------------------------------------*/
LOCAL FILE *_s_out;
LOCAL void PROC _s_ (const char s[]) { fputs (s, _s_out); }
LOCAL void PROC say (const char s1[], const char _s2[]) {
/*!*/ register const char *const s2 = _s2;
_s_out = stdout;
_s_ (s1); _s_ (s2); _s_ ("\n");
}
LOCAL void PROC error (byte errcode, const char arg[], const char err[]) {
if (errcode > retcode) retcode = errcode;
_s_out = stderr;
_s_ ("ATTRIB: "); _s_ (err); _s_ (": "); _s_ (arg); _s_ ("\n");
}
/*======================================================================*/
#define HELP() \
say ("ATTRIB v" VERSION " - Displays or changes file attributes.\n" \
"Copyright (c) 1998-2003, licensed under GPL2.\n\n" \
"Syntax: ATTRIB { options | [path\][file] | /@[list] }\n\n" \
"Options:\n\n" \
" +H Sets the Hidden attribute. -H Clears the Hidden attribute.\n" \
" +S Sets the System attribute. -S Clears the System attribute.\n" \
" +R Sets the Read-only attribute. -R Clears the Read-only attribute.\n" \
" +A Sets the Archive attribute. -A Clears the Archive attribute.\n\n" \
" /S Process files in all directories in the specified path(es).\n" \
" /D Process directory names for arguments with wildcards.\n" \
" /@ Process files, listed in the specified file [or in stdin].\n\n" \
"Examples:\n\n" \
" attrib file -rhs\n" \
" attrib +a -r dir1\ dir2\*.dat /s\n" \
" attrib -hs/sd /@list.txt *.*", "")
LOCAL byte iscomma = 0;
int _Cdecl main (int argc, char *argv[]) {
char **p = argv, **q = p;
while (--argc) {
char *arg; p++, arg = strupr (*p);
/* Comma is an `undocumented feature` from MS's ATTRIB. It */
/* basically unsets all the attributes of all the files in */
/* a directory */
if (*arg == ',') iscomma = 1, arg++;
if (*arg == '+' || *arg == '-') { /* +-HSRA */
register char ch0 = *arg; arg++;
do { ATTR mask = 0;
if (*arg == 'H') mask = _A_HIDDEN;
if (*arg == 'S') mask = _A_SYSTEM;
if (*arg == 'R') mask = _A_RDONLY;
if (*arg == 'A') mask = _A_ARCH;
if (mask == 0) {
sayerror (E_OPTION, "invalid attribute", arg);
return retcode;
}
if (ch0 == '+') attr_set |= mask;
mask = ~mask, attr_keep &= mask;
arg++;
} while (*arg && *arg != '/');
}
if (*arg == '/') { /* /?@SD */
do { if (*arg == '/') arg++;
if (*arg == '?') { HELP(); return retcode; }
if (*arg == '@') { *arg = '/'; break; }
if (*arg == 'S') { recurse = 1; continue; }
if (*arg != 'D') {
sayerror (E_OPTION, "invalid option", arg);
return retcode;
}
/*if (*arg == 'D')*/ useattr = ALL_ATTR;
} while (arg++, *arg);
}
if (*arg) *q++ = arg; /* file /@ */
} /* while */
if (attr_keep == (ATTR)~0u) {
if (iscomma)
attr_keep = ~ALL_FILE /*, attr_set = 0*/;
else *NEW_PART = '\0';
}
p = argv;
/*if (p == q)*/ *q = "*.*";
do {
if (**p == '/') list (&(*p) [1]);
else process (*p);
p++;
} while (p < q);
return retcode;
}
/*======================================================================*/
#ifdef __BORLANDC__
# include "setupio.inc"
# include "setvbuf.inc"
# include "malloc.inc"
# include "stdio.inc"
#endif