168349Sobrien/* 2133359Sobrien * Copyright (c) Ian F. Darwin 1986-1995. 3133359Sobrien * Software written by Ian F. Darwin and others; 4133359Sobrien * maintained 1995-present by Christos Zoulas and others. 5191736Sobrien * 6133359Sobrien * Redistribution and use in source and binary forms, with or without 7133359Sobrien * modification, are permitted provided that the following conditions 8133359Sobrien * are met: 9133359Sobrien * 1. Redistributions of source code must retain the above copyright 10133359Sobrien * notice immediately at the beginning of the file, without modification, 11133359Sobrien * this list of conditions, and the following disclaimer. 12133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright 13133359Sobrien * notice, this list of conditions and the following disclaimer in the 14133359Sobrien * documentation and/or other materials provided with the distribution. 15191736Sobrien * 16133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26133359Sobrien * SUCH DAMAGE. 27133359Sobrien */ 28133359Sobrien/* 2968349Sobrien * file - find type of a file or files - main program. 3068349Sobrien */ 31103373Sobrien 32103373Sobrien#include "file.h" 33191736Sobrien 34191736Sobrien#ifndef lint 35284193SdelphijFILE_RCSID("@(#)$File: file.c,v 1.160 2014/12/16 23:18:40 christos Exp $") 36191736Sobrien#endif /* lint */ 37191736Sobrien 38133359Sobrien#include "magic.h" 39133359Sobrien 4068349Sobrien#include <stdlib.h> 4169216Sobrien#include <unistd.h> 4268349Sobrien#include <string.h> 4368349Sobrien#ifdef RESTORE_TIME 4468349Sobrien# if (__COHERENT__ >= 0x420) 4568349Sobrien# include <sys/utime.h> 4668349Sobrien# else 4768349Sobrien# ifdef USE_UTIMES 4868349Sobrien# include <sys/time.h> 4968349Sobrien# else 5068349Sobrien# include <utime.h> 5168349Sobrien# endif 5268349Sobrien# endif 5368349Sobrien#endif 5468349Sobrien#ifdef HAVE_UNISTD_H 5568349Sobrien#include <unistd.h> /* for read() */ 5668349Sobrien#endif 57133359Sobrien#ifdef HAVE_WCHAR_H 58133359Sobrien#include <wchar.h> 59133359Sobrien#endif 6068349Sobrien 61192348Sdelphij#if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION) 62186690Sobrien#include <getopt.h> 63226048Sobrien#ifndef HAVE_GETOPT_LONG 64226048Sobrienint getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex); 65226048Sobrien#endif 66133359Sobrien#else 67186690Sobrien#include "mygetopt.h" 68103373Sobrien#endif 69103373Sobrien 7068349Sobrien#ifdef S_IFLNK 71267843Sdelphij#define FILE_FLAGS "-bcEhikLlNnprsvz0" 7268349Sobrien#else 73267843Sdelphij#define FILE_FLAGS "-bcEiklNnprsvz0" 7468349Sobrien#endif 7568349Sobrien 76226048Sobrien# define USAGE \ 77226048Sobrien "Usage: %s [" FILE_FLAGS \ 78226048Sobrien "] [--apple] [--mime-encoding] [--mime-type]\n" \ 79226048Sobrien " [-e testname] [-F separator] [-f namefile] [-m magicfiles] " \ 80226048Sobrien "file ...\n" \ 81226048Sobrien " %s -C [-m magicfiles]\n" \ 82226048Sobrien " %s [--help]\n" 83103373Sobrien 84133359Sobrienprivate int /* Global command-line options */ 8568349Sobrien bflag = 0, /* brief output format */ 86110949Sobrien nopad = 0, /* Don't pad output */ 87169942Sobrien nobuffer = 0, /* Do not buffer stdout */ 88169942Sobrien nulsep = 0; /* Append '\0' to the separator */ 8968349Sobrien 90159764Sobrienprivate const char *separator = ":"; /* Default field separator */ 91191736Sobrienprivate const struct option long_options[] = { 92191736Sobrien#define OPT(shortname, longname, opt, doc) \ 93191736Sobrien {longname, opt, NULL, shortname}, 94191736Sobrien#define OPT_LONGONLY(longname, opt, doc) \ 95191736Sobrien {longname, opt, NULL, 0}, 96191736Sobrien#include "file_opts.h" 97191736Sobrien#undef OPT 98191736Sobrien#undef OPT_LONGONLY 99191736Sobrien {0, 0, NULL, 0} 100191736Sobrien}; 101284193Sdelphij#define OPTSTRING "bcCde:Ef:F:hiklLm:nNpP:rsvz0" 10268349Sobrien 103191736Sobrienprivate const struct { 104191736Sobrien const char *name; 105191736Sobrien int value; 106191736Sobrien} nv[] = { 107191736Sobrien { "apptype", MAGIC_NO_CHECK_APPTYPE }, 108191736Sobrien { "ascii", MAGIC_NO_CHECK_ASCII }, 109191736Sobrien { "cdf", MAGIC_NO_CHECK_CDF }, 110191736Sobrien { "compress", MAGIC_NO_CHECK_COMPRESS }, 111191736Sobrien { "elf", MAGIC_NO_CHECK_ELF }, 112191736Sobrien { "encoding", MAGIC_NO_CHECK_ENCODING }, 113191736Sobrien { "soft", MAGIC_NO_CHECK_SOFT }, 114191736Sobrien { "tar", MAGIC_NO_CHECK_TAR }, 115226048Sobrien { "text", MAGIC_NO_CHECK_TEXT }, /* synonym for ascii */ 116234250Sobrien { "tokens", MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */ 117191736Sobrien}; 118191736Sobrien 119284193Sdelphijprivate struct { 120284193Sdelphij const char *name; 121284193Sdelphij int tag; 122284193Sdelphij size_t value; 123284193Sdelphij} pm[] = { 124284193Sdelphij { "indir", MAGIC_PARAM_INDIR_MAX, 0 }, 125284193Sdelphij { "name", MAGIC_PARAM_NAME_MAX, 0 }, 126284193Sdelphij { "elf_phnum", MAGIC_PARAM_ELF_PHNUM_MAX, 0 }, 127284193Sdelphij { "elf_shnum", MAGIC_PARAM_ELF_SHNUM_MAX, 0 }, 128284193Sdelphij { "elf_notes", MAGIC_PARAM_ELF_NOTES_MAX, 0 }, 129284193Sdelphij}; 130284193Sdelphij 131133359Sobrienprivate char *progname; /* used throughout */ 13268349Sobrien 133133359Sobrienprivate void usage(void); 134267843Sdelphijprivate void docprint(const char *); 135133359Sobrienprivate void help(void); 13668349Sobrien 137191736Sobrienprivate int unwrap(struct magic_set *, const char *); 138191736Sobrienprivate int process(struct magic_set *ms, const char *, int); 139191736Sobrienprivate struct magic_set *load(const char *, int); 140284193Sdelphijprivate void setparam(const char *); 141284193Sdelphijprivate void applyparam(magic_t); 142133359Sobrien 143191736Sobrien 14468349Sobrien/* 14568349Sobrien * main - parse arguments and handle options 14668349Sobrien */ 14768349Sobrienint 148133359Sobrienmain(int argc, char *argv[]) 14968349Sobrien{ 150175296Sobrien int c; 151175296Sobrien size_t i; 152133359Sobrien int action = 0, didsomefiles = 0, errflg = 0; 153191736Sobrien int flags = 0, e = 0; 154191736Sobrien struct magic_set *magic = NULL; 155103373Sobrien int longindex; 156226048Sobrien const char *magicfile = NULL; /* where the magic is */ 15768349Sobrien 158159764Sobrien /* makes islower etc work for other langs */ 159284193Sdelphij#ifdef HAVE_SETLOCALE 160159764Sobrien (void)setlocale(LC_CTYPE, ""); 161284193Sdelphij#endif 16268349Sobrien 163103373Sobrien#ifdef __EMX__ 164103373Sobrien /* sh-like wildcard expansion! Shouldn't hurt at least ... */ 165103373Sobrien _wildcard(&argc, &argv); 166103373Sobrien#endif 167103373Sobrien 16868349Sobrien if ((progname = strrchr(argv[0], '/')) != NULL) 16968349Sobrien progname++; 17068349Sobrien else 17168349Sobrien progname = argv[0]; 17268349Sobrien 173159764Sobrien#ifdef S_IFLNK 174159764Sobrien flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0; 175159764Sobrien#endif 176103373Sobrien while ((c = getopt_long(argc, argv, OPTSTRING, long_options, 177103373Sobrien &longindex)) != -1) 17868349Sobrien switch (c) { 179103373Sobrien case 0 : 180175296Sobrien switch (longindex) { 181175296Sobrien case 0: 182103373Sobrien help(); 183175296Sobrien break; 184175296Sobrien case 10: 185191736Sobrien flags |= MAGIC_APPLE; 186191736Sobrien break; 187191736Sobrien case 11: 188175296Sobrien flags |= MAGIC_MIME_TYPE; 189175296Sobrien break; 190191736Sobrien case 12: 191175296Sobrien flags |= MAGIC_MIME_ENCODING; 192175296Sobrien break; 193175296Sobrien } 194103373Sobrien break; 195169942Sobrien case '0': 196169942Sobrien nulsep = 1; 197169942Sobrien break; 19868349Sobrien case 'b': 199175296Sobrien bflag++; 20068349Sobrien break; 20168349Sobrien case 'c': 202133359Sobrien action = FILE_CHECK; 20368349Sobrien break; 20474784Sobrien case 'C': 205133359Sobrien action = FILE_COMPILE; 20674784Sobrien break; 20768349Sobrien case 'd': 208133359Sobrien flags |= MAGIC_DEBUG|MAGIC_CHECK; 20968349Sobrien break; 210267843Sdelphij case 'E': 211267843Sdelphij flags |= MAGIC_ERROR; 212267843Sdelphij break; 213169962Sobrien case 'e': 214169962Sobrien for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) 215169962Sobrien if (strcmp(nv[i].name, optarg) == 0) 216169962Sobrien break; 217169962Sobrien 218169962Sobrien if (i == sizeof(nv) / sizeof(nv[0])) 219169962Sobrien errflg++; 220169962Sobrien else 221169962Sobrien flags |= nv[i].value; 222169962Sobrien break; 223191736Sobrien 22468349Sobrien case 'f': 225133359Sobrien if(action) 226133359Sobrien usage(); 227191736Sobrien if (magic == NULL) 228191736Sobrien if ((magic = load(magicfile, flags)) == NULL) 229191736Sobrien return 1; 230191736Sobrien e |= unwrap(magic, optarg); 23168349Sobrien ++didsomefiles; 23268349Sobrien break; 233110949Sobrien case 'F': 234133359Sobrien separator = optarg; 235110949Sobrien break; 23668349Sobrien case 'i': 237133359Sobrien flags |= MAGIC_MIME; 23868349Sobrien break; 23968349Sobrien case 'k': 240133359Sobrien flags |= MAGIC_CONTINUE; 24168349Sobrien break; 242226048Sobrien case 'l': 243226048Sobrien action = FILE_LIST; 244226048Sobrien break; 24568349Sobrien case 'm': 24668349Sobrien magicfile = optarg; 24768349Sobrien break; 24868349Sobrien case 'n': 24968349Sobrien ++nobuffer; 25068349Sobrien break; 251110949Sobrien case 'N': 252110949Sobrien ++nopad; 253110949Sobrien break; 254133359Sobrien#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) 255133359Sobrien case 'p': 256133359Sobrien flags |= MAGIC_PRESERVE_ATIME; 257133359Sobrien break; 258133359Sobrien#endif 259284193Sdelphij case 'P': 260284193Sdelphij setparam(optarg); 261284193Sdelphij break; 262133359Sobrien case 'r': 263133359Sobrien flags |= MAGIC_RAW; 264133359Sobrien break; 265284193Sdelphij break; 26668349Sobrien case 's': 267133359Sobrien flags |= MAGIC_DEVICES; 26868349Sobrien break; 26968349Sobrien case 'v': 270226048Sobrien if (magicfile == NULL) 271226048Sobrien magicfile = magic_getpath(magicfile, action); 272226048Sobrien (void)fprintf(stdout, "%s-%s\n", progname, VERSION); 273226048Sobrien (void)fprintf(stdout, "magic file from %s\n", 27468349Sobrien magicfile); 275267843Sdelphij return 0; 27668349Sobrien case 'z': 277133359Sobrien flags |= MAGIC_COMPRESS; 27868349Sobrien break; 27968349Sobrien#ifdef S_IFLNK 28068349Sobrien case 'L': 281133359Sobrien flags |= MAGIC_SYMLINK; 28268349Sobrien break; 283159764Sobrien case 'h': 284159764Sobrien flags &= ~MAGIC_SYMLINK; 285159764Sobrien break; 28668349Sobrien#endif 28768349Sobrien case '?': 28868349Sobrien default: 28968349Sobrien errflg++; 29068349Sobrien break; 29168349Sobrien } 29268349Sobrien 29368349Sobrien if (errflg) { 29474784Sobrien usage(); 29568349Sobrien } 296191736Sobrien if (e) 297191736Sobrien return e; 29868349Sobrien 299267843Sdelphij if (MAGIC_VERSION != magic_version()) 300267843Sdelphij (void)fprintf(stderr, "%s: compiled magic version [%d] " 301267843Sdelphij "does not match with shared library magic version [%d]\n", 302267843Sdelphij progname, MAGIC_VERSION, magic_version()); 303267843Sdelphij 304133359Sobrien switch(action) { 305133359Sobrien case FILE_CHECK: 306133359Sobrien case FILE_COMPILE: 307226048Sobrien case FILE_LIST: 308191736Sobrien /* 309191736Sobrien * Don't try to check/compile ~/.magic unless we explicitly 310191736Sobrien * ask for it. 311191736Sobrien */ 312133359Sobrien magic = magic_open(flags|MAGIC_CHECK); 313133359Sobrien if (magic == NULL) { 314133359Sobrien (void)fprintf(stderr, "%s: %s\n", progname, 315133359Sobrien strerror(errno)); 316133359Sobrien return 1; 317133359Sobrien } 318284193Sdelphij 319284193Sdelphij 320226048Sobrien switch(action) { 321226048Sobrien case FILE_CHECK: 322226048Sobrien c = magic_check(magic, magicfile); 323226048Sobrien break; 324226048Sobrien case FILE_COMPILE: 325226048Sobrien c = magic_compile(magic, magicfile); 326226048Sobrien break; 327226048Sobrien case FILE_LIST: 328226048Sobrien c = magic_list(magic, magicfile); 329226048Sobrien break; 330226048Sobrien default: 331226048Sobrien abort(); 332226048Sobrien } 333133359Sobrien if (c == -1) { 334133359Sobrien (void)fprintf(stderr, "%s: %s\n", progname, 335133359Sobrien magic_error(magic)); 336191736Sobrien return 1; 337133359Sobrien } 338133359Sobrien return 0; 339133359Sobrien default: 340191736Sobrien if (magic == NULL) 341191736Sobrien if ((magic = load(magicfile, flags)) == NULL) 342191736Sobrien return 1; 343284193Sdelphij applyparam(magic); 34468349Sobrien } 34568349Sobrien 34668349Sobrien if (optind == argc) { 347191736Sobrien if (!didsomefiles) 34874784Sobrien usage(); 34968349Sobrien } 35068349Sobrien else { 351175296Sobrien size_t j, wid, nw; 352175296Sobrien for (wid = 0, j = (size_t)optind; j < (size_t)argc; j++) { 353175296Sobrien nw = file_mbswidth(argv[j]); 35468349Sobrien if (nw > wid) 35568349Sobrien wid = nw; 35668349Sobrien } 357175296Sobrien /* 358175296Sobrien * If bflag is only set twice, set it depending on 359175296Sobrien * number of files [this is undocumented, and subject to change] 360175296Sobrien */ 361175296Sobrien if (bflag == 2) { 362175296Sobrien bflag = optind >= argc - 1; 363175296Sobrien } 36468349Sobrien for (; optind < argc; optind++) 365191736Sobrien e |= process(magic, argv[optind], wid); 36668349Sobrien } 36768349Sobrien 368191736Sobrien if (magic) 369191736Sobrien magic_close(magic); 370191736Sobrien return e; 37168349Sobrien} 37268349Sobrien 373284193Sdelphijprivate void 374284193Sdelphijapplyparam(magic_t magic) 375284193Sdelphij{ 376284193Sdelphij size_t i; 37768349Sobrien 378284193Sdelphij for (i = 0; i < __arraycount(pm); i++) { 379284193Sdelphij if (pm[i].value == 0) 380284193Sdelphij continue; 381284193Sdelphij if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1) { 382284193Sdelphij (void)fprintf(stderr, "%s: Can't set %s %s\n", progname, 383284193Sdelphij pm[i].name, strerror(errno)); 384284193Sdelphij exit(1); 385284193Sdelphij } 386284193Sdelphij } 387284193Sdelphij} 388284193Sdelphij 389284193Sdelphijprivate void 390284193Sdelphijsetparam(const char *p) 391284193Sdelphij{ 392284193Sdelphij size_t i; 393284193Sdelphij char *s; 394284193Sdelphij 395284193Sdelphij if ((s = strchr(p, '=')) == NULL) 396284193Sdelphij goto badparm; 397284193Sdelphij 398284193Sdelphij for (i = 0; i < __arraycount(pm); i++) { 399284193Sdelphij if (strncmp(p, pm[i].name, s - p) != 0) 400284193Sdelphij continue; 401284193Sdelphij pm[i].value = atoi(s + 1); 402284193Sdelphij return; 403284193Sdelphij } 404284193Sdelphijbadparm: 405284193Sdelphij (void)fprintf(stderr, "%s: Unknown param %s\n", progname, p); 406284193Sdelphij exit(1); 407284193Sdelphij} 408284193Sdelphij 409191736Sobrienprivate struct magic_set * 410159764Sobrien/*ARGSUSED*/ 411191736Sobrienload(const char *magicfile, int flags) 412133359Sobrien{ 413191736Sobrien struct magic_set *magic = magic_open(flags); 414133359Sobrien if (magic == NULL) { 415133359Sobrien (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno)); 416191736Sobrien return NULL; 417133359Sobrien } 418133359Sobrien if (magic_load(magic, magicfile) == -1) { 419133359Sobrien (void)fprintf(stderr, "%s: %s\n", 420133359Sobrien progname, magic_error(magic)); 421191736Sobrien magic_close(magic); 422191736Sobrien return NULL; 423133359Sobrien } 424191736Sobrien return magic; 425133359Sobrien} 426133359Sobrien 42768349Sobrien/* 42868349Sobrien * unwrap -- read a file of filenames, do each one. 42968349Sobrien */ 430191736Sobrienprivate int 431191736Sobrienunwrap(struct magic_set *ms, const char *fn) 43268349Sobrien{ 43368349Sobrien FILE *f; 434226048Sobrien ssize_t len; 435226048Sobrien char *line = NULL; 436226048Sobrien size_t llen = 0; 43768349Sobrien int wid = 0, cwid; 438191736Sobrien int e = 0; 43968349Sobrien 44068349Sobrien if (strcmp("-", fn) == 0) { 44168349Sobrien f = stdin; 44268349Sobrien wid = 1; 44368349Sobrien } else { 44468349Sobrien if ((f = fopen(fn, "r")) == NULL) { 445133359Sobrien (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n", 446133359Sobrien progname, fn, strerror(errno)); 447191736Sobrien return 1; 44868349Sobrien } 44968349Sobrien 450226048Sobrien while ((len = getline(&line, &llen, f)) > 0) { 451226048Sobrien if (line[len - 1] == '\n') 452226048Sobrien line[len - 1] = '\0'; 453226048Sobrien cwid = file_mbswidth(line); 45468349Sobrien if (cwid > wid) 45568349Sobrien wid = cwid; 45668349Sobrien } 45768349Sobrien 45868349Sobrien rewind(f); 45968349Sobrien } 46068349Sobrien 461226048Sobrien while ((len = getline(&line, &llen, f)) > 0) { 462226048Sobrien if (line[len - 1] == '\n') 463226048Sobrien line[len - 1] = '\0'; 464226048Sobrien e |= process(ms, line, wid); 46568349Sobrien if(nobuffer) 466159764Sobrien (void)fflush(stdout); 46768349Sobrien } 46868349Sobrien 469226048Sobrien free(line); 470159764Sobrien (void)fclose(f); 471191736Sobrien return e; 47268349Sobrien} 47368349Sobrien 474169942Sobrien/* 475169942Sobrien * Called for each input file on the command line (or in a list of files) 476169942Sobrien */ 477191736Sobrienprivate int 478191736Sobrienprocess(struct magic_set *ms, const char *inname, int wid) 479133359Sobrien{ 480133359Sobrien const char *type; 481133359Sobrien int std_in = strcmp(inname, "-") == 0; 48268349Sobrien 483169942Sobrien if (wid > 0 && !bflag) { 484169942Sobrien (void)printf("%s", std_in ? "/dev/stdin" : inname); 485169942Sobrien if (nulsep) 486169962Sobrien (void)putc('\0', stdout); 487226048Sobrien (void)printf("%s", separator); 488169942Sobrien (void)printf("%*s ", 489169942Sobrien (int) (nopad ? 0 : (wid - file_mbswidth(inname))), ""); 490169942Sobrien } 491133359Sobrien 492191736Sobrien type = magic_file(ms, std_in ? NULL : inname); 493191736Sobrien if (type == NULL) { 494191736Sobrien (void)printf("ERROR: %s\n", magic_error(ms)); 495191736Sobrien return 1; 496191736Sobrien } else { 497159764Sobrien (void)printf("%s\n", type); 498191736Sobrien return 0; 499191736Sobrien } 500133359Sobrien} 501133359Sobrien 502267843Sdelphijprotected size_t 503133359Sobrienfile_mbswidth(const char *s) 50468349Sobrien{ 505133359Sobrien#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) 506133359Sobrien size_t bytesconsumed, old_n, n, width = 0; 507133359Sobrien mbstate_t state; 508133359Sobrien wchar_t nextchar; 509133359Sobrien (void)memset(&state, 0, sizeof(mbstate_t)); 510133359Sobrien old_n = n = strlen(s); 51168349Sobrien 512133359Sobrien while (n > 0) { 513133359Sobrien bytesconsumed = mbrtowc(&nextchar, s, n, &state); 514133359Sobrien if (bytesconsumed == (size_t)(-1) || 515133359Sobrien bytesconsumed == (size_t)(-2)) { 516133359Sobrien /* Something went wrong, return something reasonable */ 517133359Sobrien return old_n; 51868349Sobrien } 519133359Sobrien if (s[0] == '\n') { 520133359Sobrien /* 521133359Sobrien * do what strlen() would do, so that caller 522133359Sobrien * is always right 523133359Sobrien */ 524133359Sobrien width++; 525267843Sdelphij } else { 526267843Sdelphij int w = wcwidth(nextchar); 527267843Sdelphij if (w > 0) 528267843Sdelphij width += w; 529267843Sdelphij } 53068349Sobrien 531133359Sobrien s += bytesconsumed, n -= bytesconsumed; 53268349Sobrien } 533133359Sobrien return width; 534133359Sobrien#else 535133359Sobrien return strlen(s); 53668349Sobrien#endif 53768349Sobrien} 53868349Sobrien 539133359Sobrienprivate void 540103373Sobrienusage(void) 54174784Sobrien{ 542226048Sobrien (void)fprintf(stderr, USAGE, progname, progname, progname); 54374784Sobrien exit(1); 54474784Sobrien} 545103373Sobrien 546133359Sobrienprivate void 547267843Sdelphijdocprint(const char *opts) 548267843Sdelphij{ 549267843Sdelphij size_t i; 550267843Sdelphij int comma; 551267843Sdelphij char *sp, *p; 552267843Sdelphij 553267843Sdelphij p = strstr(opts, "%o"); 554267843Sdelphij if (p == NULL) { 555267843Sdelphij fprintf(stdout, "%s", opts); 556267843Sdelphij return; 557267843Sdelphij } 558267843Sdelphij 559267843Sdelphij for (sp = p - 1; sp > opts && *sp == ' '; sp--) 560267843Sdelphij continue; 561267843Sdelphij 562267843Sdelphij fprintf(stdout, "%.*s", (int)(p - opts), opts); 563267843Sdelphij 564267843Sdelphij comma = 0; 565267843Sdelphij for (i = 0; i < __arraycount(nv); i++) { 566267843Sdelphij fprintf(stdout, "%s%s", comma++ ? ", " : "", nv[i].name); 567267843Sdelphij if (i && i % 5 == 0) { 568267843Sdelphij fprintf(stdout, ",\n%*s", (int)(p - sp - 1), ""); 569267843Sdelphij comma = 0; 570267843Sdelphij } 571267843Sdelphij } 572267843Sdelphij 573267843Sdelphij fprintf(stdout, "%s", opts + (p - opts) + 2); 574267843Sdelphij} 575267843Sdelphij 576267843Sdelphijprivate void 577103373Sobrienhelp(void) 578103373Sobrien{ 579175296Sobrien (void)fputs( 580175296Sobrien"Usage: file [OPTION...] [FILE...]\n" 581175296Sobrien"Determine type of FILEs.\n" 582226048Sobrien"\n", stdout); 583175296Sobrien#define OPT(shortname, longname, opt, doc) \ 584267843Sdelphij fprintf(stdout, " -%c, --" longname, shortname), \ 585267843Sdelphij docprint(doc); 586175296Sobrien#define OPT_LONGONLY(longname, opt, doc) \ 587267843Sdelphij fprintf(stdout, " --" longname), \ 588267843Sdelphij docprint(doc); 589175296Sobrien#include "file_opts.h" 590175296Sobrien#undef OPT 591175296Sobrien#undef OPT_LONGONLY 592226048Sobrien fprintf(stdout, "\nReport bugs to http://bugs.gw.com/\n"); 593103373Sobrien exit(0); 594103373Sobrien} 595