1 /*
2  * stat.c - stat builtin interface to system call
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1996-1997 Peter Stephenson
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Peter Stephenson or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Peter Stephenson and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Peter Stephenson and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Peter Stephenson and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "stat.mdh"
31 #include "stat.pro"
32 
33 enum statnum { ST_DEV, ST_INO, ST_MODE, ST_NLINK, ST_UID, ST_GID,
34 		   ST_RDEV, ST_SIZE, ST_ATIM, ST_MTIM, ST_CTIM,
35 		   ST_BLKSIZE, ST_BLOCKS, ST_READLINK, ST_COUNT };
36 enum statflags { STF_NAME = 1,  STF_FILE = 2, STF_STRING = 4, STF_RAW = 8,
37 		     STF_PICK = 16, STF_ARRAY = 32, STF_GMT = 64,
38 		     STF_HASH = 128, STF_OCTAL = 256 };
39 static char *statelts[] = { "device", "inode", "mode", "nlink",
40 				"uid", "gid", "rdev", "size", "atime",
41 				"mtime", "ctime", "blksize", "blocks",
42 				"link", NULL };
43 #define HNAMEKEY "name"
44 
45 /**/
46 static void
statmodeprint(mode_t mode,char * outbuf,int flags)47 statmodeprint(mode_t mode, char *outbuf, int flags)
48 {
49     if (flags & STF_RAW) {
50 	sprintf(outbuf, (flags & STF_OCTAL) ? "0%lo" : "%lu",
51 		(unsigned long)mode);
52 	if (flags & STF_STRING)
53 	    strcat(outbuf, " (");
54     }
55     if (flags & STF_STRING) {
56 	static const char *modes = "?rwxrwxrwx";
57 #ifdef __CYGWIN__
58 	static mode_t mflags[9] = { 0 };
59 #else
60 	static const mode_t mflags[9] = {
61 	    S_IRUSR, S_IWUSR, S_IXUSR,
62 	    S_IRGRP, S_IWGRP, S_IXGRP,
63 	    S_IROTH, S_IWOTH, S_IXOTH
64 	};
65 #endif
66 	const mode_t *mfp = mflags;
67 	char pm[11];
68 	int i;
69 
70 #ifdef __CYGWIN__
71 	if (mflags[0] == 0) {
72 	    mflags[0] = S_IRUSR;
73 	    mflags[1] = S_IWUSR;
74 	    mflags[2] = S_IXUSR;
75 	    mflags[3] = S_IRGRP;
76 	    mflags[4] = S_IWGRP;
77 	    mflags[5] = S_IXGRP;
78 	    mflags[6] = S_IROTH;
79 	    mflags[7] = S_IWOTH;
80 	    mflags[8] = S_IXOTH;
81 	}
82 #endif
83 
84 	if (S_ISBLK(mode))
85 	    *pm = 'b';
86 	else if (S_ISCHR(mode))
87 	    *pm = 'c';
88 	else if (S_ISDIR(mode))
89 	    *pm = 'd';
90 	else if (S_ISDOOR(mode))
91 	    *pm = 'D';
92 	else if (S_ISFIFO(mode))
93 	    *pm = 'p';
94 	else if (S_ISLNK(mode))
95 	    *pm = 'l';
96 	else if (S_ISMPC(mode))
97 	    *pm = 'm';
98 	else if (S_ISNWK(mode))
99 	    *pm = 'n';
100 	else if (S_ISOFD(mode))
101 	    *pm = 'M';
102 	else if (S_ISOFL(mode))
103 	    *pm = 'M';
104 	else if (S_ISREG(mode))
105 	    *pm = '-';
106 	else if (S_ISSOCK(mode))
107 	    *pm = 's';
108 	else
109 	    *pm = '?';
110 
111 	for (i = 1; i <= 9; i++)
112 	    pm[i] = (mode & *mfp++) ? modes[i] : '-';
113 	pm[10] = '\0';
114 
115 	if (mode & S_ISUID)
116 	    pm[3] = (mode & S_IXUSR) ? 's' : 'S';
117 	if (mode & S_ISGID)
118 	    pm[6] = (mode & S_IXGRP) ? 's' : 'S';
119 	if (mode & S_ISVTX)
120 	    pm[9] = (mode & S_IXOTH) ? 't' : 'T';
121 
122 	pm[10] = 0;
123 	strcat(outbuf, pm);
124 	if (flags & STF_RAW)
125 	    strcat(outbuf, ")");
126     }
127 }
128 
129 
130 /**/
131 static void
statuidprint(uid_t uid,char * outbuf,int flags)132 statuidprint(uid_t uid, char *outbuf, int flags)
133 {
134     if (flags & STF_RAW) {
135 	sprintf(outbuf, "%lu", (unsigned long)uid);
136 	if (flags & STF_STRING)
137 	    strcat(outbuf, " (");
138     }
139     if (flags & STF_STRING) {
140 #ifdef HAVE_GETPWUID
141 	struct passwd *pwd;
142 	pwd = getpwuid(uid);
143 	if (pwd)
144 	    strcat(outbuf, pwd->pw_name);
145 	else
146 #endif /* !HAVE_GETPWUID */
147 	{
148 	    char *optr;
149 	    for (optr = outbuf; *optr; optr++)
150 		;
151 	    sprintf(optr, "%lu", (unsigned long)uid);
152 	}
153 	if (flags & STF_RAW)
154 	    strcat(outbuf, ")");
155     }
156 }
157 
158 
159 /**/
160 static void
statgidprint(gid_t gid,char * outbuf,int flags)161 statgidprint(gid_t gid, char *outbuf, int flags)
162 {
163     if (flags & STF_RAW) {
164 	sprintf(outbuf, "%lu", (unsigned long)gid);
165 	if (flags & STF_STRING)
166 	    strcat(outbuf, " (");
167     }
168     if (flags & STF_STRING) {
169 #ifdef USE_GETGRGID
170 	struct group *gr;
171 	gr = getgrgid(gid);
172 	if (gr)
173 	    strcat(outbuf, gr->gr_name);
174 	else
175 #endif /* !USE_GETGRGID */
176 	{
177 	    char *optr;
178 	    for (optr = outbuf; *optr; optr++)
179 		;
180 	    sprintf(optr, "%lu", (unsigned long)gid);
181 	}
182 	if (flags & STF_RAW)
183 	    strcat(outbuf, ")");
184     }
185 }
186 
187 static char *timefmt;
188 
189 /**/
190 static void
stattimeprint(time_t tim,long nsecs,char * outbuf,int flags)191 stattimeprint(time_t tim, long nsecs, char *outbuf, int flags)
192 {
193     if (flags & STF_RAW) {
194 	sprintf(outbuf, "%ld", (unsigned long)tim);
195 	if (flags & STF_STRING)
196 	    strcat(outbuf, " (");
197     }
198     if (flags & STF_STRING) {
199 	char *oend = outbuf + strlen(outbuf);
200 	/* Where the heck does "40" come from? */
201 	ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
202 			   localtime(&tim), nsecs);
203 	if (flags & STF_RAW)
204 	    strcat(oend, ")");
205     }
206 }
207 
208 
209 /**/
210 static void
statulprint(unsigned long num,char * outbuf)211 statulprint(unsigned long num, char *outbuf)
212 {
213     sprintf(outbuf, "%lu", num);
214 }
215 
216 
217 /**/
218 static void
statlinkprint(struct stat * sbuf,char * outbuf,char * fname)219 statlinkprint(struct stat *sbuf, char *outbuf, char *fname)
220 {
221     int num;
222 
223     /* fname is NULL if we are looking at an fd */
224     if (fname && S_ISLNK(sbuf->st_mode) &&
225  	(num = readlink(fname, outbuf, PATH_MAX)) > 0) {
226 	/* readlink doesn't terminate the buffer itself */
227 	outbuf[num] = '\0';
228     }
229 }
230 
231 
232 /**/
233 static void
statprint(struct stat * sbuf,char * outbuf,char * fname,int iwhich,int flags)234 statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags)
235 {
236     char *optr = outbuf;
237 
238     if (flags & STF_NAME) {
239 	sprintf(outbuf, (flags & (STF_PICK|STF_ARRAY)) ?
240 		"%s " : "%-8s", statelts[iwhich]);
241 	optr += strlen(outbuf);
242     }
243     *optr = '\0';
244 
245     /* cast values to unsigned long as safest bet */
246     switch (iwhich) {
247     case ST_DEV:
248 	statulprint((unsigned long)sbuf->st_dev, optr);
249 	break;
250 
251     case ST_INO:
252 #ifdef INO_T_IS_64_BIT
253 	convbase(optr, sbuf->st_ino, 0);
254 #else
255 	DPUTS(sizeof(sbuf->st_ino) > sizeof(unsigned long),
256 	      "Shell compiled with wrong ino_t size");
257 	statulprint((unsigned long)sbuf->st_ino, optr);
258 #endif
259 	break;
260 
261     case ST_MODE:
262 	statmodeprint(sbuf->st_mode, optr, flags);
263 	break;
264 
265     case ST_NLINK:
266 	statulprint((unsigned long)sbuf->st_nlink, optr);
267 	break;
268 
269     case ST_UID:
270 	statuidprint(sbuf->st_uid, optr, flags);
271 	break;
272 
273     case ST_GID:
274 	statgidprint(sbuf->st_gid, optr, flags);
275 	break;
276 
277     case ST_RDEV:
278 	statulprint((unsigned long)sbuf->st_rdev, optr);
279 	break;
280 
281     case ST_SIZE:
282 #ifdef OFF_T_IS_64_BIT
283 	convbase(optr, sbuf->st_size, 0);
284 #else
285 	DPUTS(sizeof(sbuf->st_size) > sizeof(unsigned long),
286 	      "Shell compiled with wrong off_t size");
287 	statulprint((unsigned long)sbuf->st_size, optr);
288 #endif
289 	break;
290 
291     case ST_ATIM:
292 #ifdef GET_ST_ATIME_NSEC
293 	stattimeprint(sbuf->st_atime, GET_ST_ATIME_NSEC(*sbuf), optr, flags);
294 #else
295 	stattimeprint(sbuf->st_atime, 0L, optr, flags);
296 #endif
297 	break;
298 
299     case ST_MTIM:
300 #ifdef GET_ST_MTIME_NSEC
301 	stattimeprint(sbuf->st_mtime, GET_ST_MTIME_NSEC(*sbuf), optr, flags);
302 #else
303 	stattimeprint(sbuf->st_mtime, 0L, optr, flags);
304 #endif
305 	break;
306 
307     case ST_CTIM:
308 #ifdef GET_ST_CTIME_NSEC
309 	stattimeprint(sbuf->st_ctime, GET_ST_CTIME_NSEC(*sbuf), optr, flags);
310 #else
311 	stattimeprint(sbuf->st_ctime, 0L, optr, flags);
312 #endif
313 	break;
314 
315     case ST_BLKSIZE:
316 	statulprint((unsigned long)sbuf->st_blksize, optr);
317 	break;
318 
319     case ST_BLOCKS:
320 	statulprint((unsigned long)sbuf->st_blocks, optr);
321 	break;
322 
323     case ST_READLINK:
324 	statlinkprint(sbuf, optr, fname);
325 	break;
326 
327     case ST_COUNT:			/* keep some compilers happy */
328 	break;
329     }
330 }
331 
332 
333 /*
334  *
335  * Options:
336  *  -f fd:   stat fd instead of file
337  *  -g:   use GMT rather than local time for time strings (forces -s on).
338  *  -n:   always print file name of file being statted
339  *  -N:   never print file name
340  *  -l:   list stat types
341  *  -L:   do lstat (else links are implicitly dereferenced by stat)
342  *  -t:   always print name of stat type
343  *  -T:   never print name of stat type
344  *  -r:   print raw alongside string data
345  *  -s:   string, print mode, times, uid, gid as appropriate strings:
346  *        harmless but unnecessary when combined with -r.
347  *  -A array:  assign results to given array, one stat result per element.
348  *        File names and type names are only added if explicitly requested:
349  *        file names are returned as a separate array element, type names as
350  *        prefix to element.  Note the formatting deliberately contains
351  *        fewer frills when -A is used.
352  *  -H hash:  as for -A array, but returns a hash with the keys being those
353  *        from stat -l
354  *  -F fmt: specify a $TIME-like format for printing times; the default
355  *        is the (CTIME-like) "%a %b %e %k:%M:%S %Z %Y".  This option implies
356  *        -s as it is not useful for numerical times.
357  *
358  *  +type selects just element type of stat buffer (-l gives list):
359  *        type can be shortened to unambiguous string.  only one type is
360  *        allowed.  The extra type, +link, reads the target of a symbolic
361  *        link; it is empty if the stat was not an lstat or if
362  *        a file descriptor was stat'd, if the stat'd file is
363  *        not a symbolic link, or if symbolic links are not
364  *        supported.  If +link is explicitly requested, the -L (lstat)
365  *        option is set automatically.
366  */
367 /**/
368 static int
bin_stat(char * name,char ** args,Options ops,UNUSED (int func))369 bin_stat(char *name, char **args, Options ops, UNUSED(int func))
370 {
371     char **aptr, *arrnam = NULL, **array = NULL, **arrptr = NULL;
372     char *hashnam = NULL, **hash = NULL, **hashptr = NULL;
373     int len, iwhich = -1, ret = 0, flags = 0, arrsize = 0, fd = 0;
374     struct stat statbuf;
375     int found = 0, nargs;
376 
377     timefmt = "%a %b %e %k:%M:%S %Z %Y";
378 
379     for (; *args && (**args == '+' || **args == '-'); args++) {
380 	char *arg = *args+1;
381 	if (!*arg || *arg == '-' || *arg == '+') {
382 	    args++;
383 	    break;
384 	}
385 
386 	if (**args == '+') {
387 	    if (found)
388 		break;
389 	    len = strlen(arg);
390 	    for (aptr = statelts; *aptr; aptr++)
391 		if (!strncmp(*aptr, arg, len)) {
392 		    found++;
393 		    iwhich = aptr - statelts;
394 		}
395 	    if (found > 1) {
396 		zwarnnam(name, "%s: ambiguous stat element", arg);
397 		return 1;
398 	    } else if (found == 0) {
399 		zwarnnam(name, "%s: no such stat element", arg);
400 		return 1;
401 	    }
402 	    /* if name of link requested, turn on lstat */
403 	    if (iwhich == ST_READLINK)
404 		ops->ind['L'] = 1;
405 	    flags |= STF_PICK;
406 	} else {
407 	    for (; *arg; arg++) {
408 		if (strchr("glLnNorstT", *arg))
409 		    ops->ind[STOUC(*arg)] = 1;
410 		else if (*arg == 'A') {
411 		    if (arg[1]) {
412 			arrnam = arg+1;
413 		    } else if (!(arrnam = *++args)) {
414 			zwarnnam(name, "missing parameter name");
415 			return 1;
416 		    }
417 		    flags |= STF_ARRAY;
418 		    break;
419 		} else if (*arg == 'H') {
420 		    if (arg[1]) {
421 			hashnam = arg+1;
422 		    } else if (!(hashnam = *++args)) {
423 			zwarnnam(name, "missing parameter name");
424 			return 1;
425 		    }
426 		    flags |= STF_HASH;
427 		    break;
428 		} else if (*arg == 'f') {
429 		    char *sfd;
430 		    ops->ind['f'] = 1;
431 		    if (arg[1]) {
432 			sfd = arg+1;
433 		    } else if (!(sfd = *++args)) {
434 			zwarnnam(name, "missing file descriptor");
435 			return 1;
436 		    }
437 		    fd = zstrtol(sfd, &sfd, 10);
438 		    if (*sfd) {
439 			zwarnnam(name, "bad file descriptor");
440 			return 1;
441 		    }
442 		    break;
443 		} else if (*arg == 'F') {
444 		    if (arg[1]) {
445 			timefmt = arg+1;
446 		    } else if (!(timefmt = *++args)) {
447 			zwarnnam(name, "missing time format");
448 			return 1;
449 		    }
450 		    /* force string format in order to use time format */
451 		    ops->ind['s'] = 1;
452 		    break;
453 		} else {
454 		    zwarnnam(name, "bad option: -%c", *arg);
455 		    return 1;
456 		}
457 	    }
458 	}
459     }
460 
461     if ((flags & STF_ARRAY) && (flags & STF_HASH)) {
462     	/* We don't implement setting multiple variables at once */
463 	zwarnnam(name, "both array and hash requested");
464 	return 1;
465 	/* Alternate method would be to make -H blank arrnam etc etc *
466 	 * and so get 'silent loss' of earlier choice, which would   *
467 	 * be similar to stat -A foo -A bar filename                 */
468     }
469 
470     if (OPT_ISSET(ops,'l')) {
471 	/* list types and return:  can also list to array */
472 	if (arrnam) {
473 	    arrptr = array = (char **)zalloc((ST_COUNT+1)*sizeof(char *));
474 	    array[ST_COUNT] = NULL;
475 	}
476 	for (aptr = statelts; *aptr; aptr++) {
477 	    if (arrnam) {
478 		*arrptr++ = ztrdup(*aptr);
479 	    } else {
480 		printf("%s", *aptr);
481 		if (aptr[1])
482 		    putchar(' ');
483 	    }
484 	}
485 	if (arrnam) {
486 	    setaparam(arrnam, array);
487 	    if (errflag)
488 		return 1;
489 	} else
490 	    putchar('\n');
491 	return 0;
492     }
493 
494     if (!*args && !OPT_ISSET(ops,'f')) {
495 	zwarnnam(name, "no files given");
496 	return 1;
497     } else if (*args && OPT_ISSET(ops,'f')) {
498 	zwarnnam(name, "no files allowed with -f");
499 	return 1;
500     }
501 
502     nargs = 0;
503     if (OPT_ISSET(ops,'f'))
504 	nargs = 1;
505     else
506 	for (aptr = args; *aptr; aptr++)
507 	    nargs++;
508 
509     if (OPT_ISSET(ops,'g')) {
510 	flags |= STF_GMT;
511 	ops->ind['s'] = 1;
512     }
513     if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'r'))
514 	flags |= STF_STRING;
515     if (OPT_ISSET(ops,'r') || !OPT_ISSET(ops,'s'))
516 	flags |= STF_RAW;
517     if (OPT_ISSET(ops,'n'))
518 	flags |= STF_FILE;
519     if (OPT_ISSET(ops,'o'))
520 	flags |= STF_OCTAL;
521     if (OPT_ISSET(ops,'t'))
522 	flags |= STF_NAME;
523 
524     if (!(arrnam || hashnam)) {
525 	if (nargs > 1)
526 	    flags |= STF_FILE;
527 	if (!(flags & STF_PICK))
528 	    flags |= STF_NAME;
529     }
530 
531     if (OPT_ISSET(ops,'N') || OPT_ISSET(ops,'f'))
532 	flags &= ~STF_FILE;
533     if (OPT_ISSET(ops,'T') || OPT_ISSET(ops,'H'))
534 	flags &= ~STF_NAME;
535 
536     if (hashnam) {
537     	if (nargs > 1) {
538 	    zwarnnam(name, "only one file allowed with -H");
539 	    return 1;
540 	}
541 	arrsize = (flags & STF_PICK) ? 1 : ST_COUNT;
542 	if (flags & STF_FILE)
543 	    arrsize++;
544 	hashptr = hash = (char **)zshcalloc((arrsize+1)*2*sizeof(char *));
545     }
546 
547     if (arrnam) {
548 	arrsize = (flags & STF_PICK) ? 1 : ST_COUNT;
549 	if (flags & STF_FILE)
550 	    arrsize++;
551 	arrsize *= nargs;
552 	arrptr = array = (char **)zshcalloc((arrsize+1)*sizeof(char *));
553     }
554 
555     for (; OPT_ISSET(ops,'f') || *args; args++) {
556 	char outbuf[PATH_MAX + 9]; /* "link   " + link name + NULL */
557 	int rval = OPT_ISSET(ops,'f') ? fstat(fd, &statbuf) :
558 	    OPT_ISSET(ops,'L') ? lstat(unmeta(*args), &statbuf) :
559 	    stat(unmeta(*args), &statbuf);
560 	if (rval) {
561 	    if (OPT_ISSET(ops,'f'))
562 		sprintf(outbuf, "%d", fd);
563 	    zwarnnam(name, "%s: %e", OPT_ISSET(ops,'f') ? outbuf : *args,
564 		     errno);
565 	    ret = 1;
566 	    if (OPT_ISSET(ops,'f') || arrnam)
567 		break;
568 	    else
569 		continue;
570 	}
571 
572 	if (flags & STF_FILE) {
573 	    if (arrnam)
574 		*arrptr++ = ztrdup(*args);
575 	    else if (hashnam) {
576 	    	*hashptr++ = ztrdup(HNAMEKEY);
577 		*hashptr++ = ztrdup(*args);
578 	    } else
579 		printf("%s%s", *args, (flags & STF_PICK) ? " " : ":\n");
580 	}
581 	if (iwhich > -1) {
582 	    statprint(&statbuf, outbuf, *args, iwhich, flags);
583 	    if (arrnam)
584 		*arrptr++ = metafy(outbuf, -1, META_DUP);
585 	    else if (hashnam) {
586 		/* STF_NAME explicitly turned off for ops.ind['H'] above */
587 	    	*hashptr++ = ztrdup(statelts[iwhich]);
588 		*hashptr++ = metafy(outbuf, -1, META_DUP);
589 	    } else
590 		printf("%s\n", outbuf);
591 	} else {
592 	    int i;
593 	    for (i = 0; i < ST_COUNT; i++) {
594 		statprint(&statbuf, outbuf, *args, i, flags);
595 		if (arrnam)
596 		    *arrptr++= metafy(outbuf, -1, META_DUP);
597 		else if (hashnam) {
598 		    /* STF_NAME explicitly turned off for ops.ind['H'] above */
599 		    *hashptr++ = ztrdup(statelts[i]);
600 		    *hashptr++ = metafy(outbuf, -1, META_DUP);
601 		} else
602 		    printf("%s\n", outbuf);
603 	    }
604 	}
605 	if (OPT_ISSET(ops,'f'))
606 	    break;
607 
608 	if (!arrnam && !hashnam && args[1] && !(flags & STF_PICK))
609 	    putchar('\n');
610     }
611 
612     if (arrnam) {
613 	if (ret)
614 	    freearray(array);
615 	else {
616 	    setaparam(arrnam, array);
617 	    if (errflag)
618 		return 1;
619 	}
620     }
621 
622     if (hashnam) {
623     	if (ret)
624 	    freearray(hash);
625 	else {
626 	    sethparam(hashnam, hash);
627 	    if (errflag)
628 		return 1;
629 	}
630     }
631 
632     return ret;
633 }
634 
635 static struct builtin bintab[] = {
636     BUILTIN("stat", 0, bin_stat, 0, -1, 0, NULL, NULL),
637     BUILTIN("zstat", 0, bin_stat, 0, -1, 0, NULL, NULL),
638 };
639 
640 static struct features module_features = {
641     bintab, sizeof(bintab)/sizeof(*bintab),
642     NULL, 0,
643     NULL, 0,
644     NULL, 0,
645     0
646 };
647 
648 /**/
649 int
setup_(UNUSED (Module m))650 setup_(UNUSED(Module m))
651 {
652     return 0;
653 }
654 
655 /**/
656 int
features_(Module m,char *** features)657 features_(Module m, char ***features)
658 {
659     *features = featuresarray(m, &module_features);
660     return 0;
661 }
662 
663 /**/
664 int
enables_(Module m,int ** enables)665 enables_(Module m, int **enables)
666 {
667     return handlefeatures(m, &module_features, enables);
668 }
669 
670 /**/
671 int
boot_(UNUSED (Module m))672 boot_(UNUSED(Module m))
673 {
674     return 0;
675 }
676 
677 /**/
678 int
cleanup_(Module m)679 cleanup_(Module m)
680 {
681     return setfeatureenables(m, &module_features, NULL);
682 }
683 
684 /**/
685 int
finish_(UNUSED (Module m))686 finish_(UNUSED(Module m))
687 {
688     return 0;
689 }
690