#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <locale.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <stdarg.h>
#include <wchar.h>
#define MSGEXISTS "\"%s\": Exists but is not a directory\n"
#define MSGUSAGE "usage: mkdir [-m mode] [-p] dirname ...\n"
#define MSGFMT1 "\"%s\": %s\n"
#define MSGFAILED "Failed to make directory \"%s\"; %s\n"
extern int optind, errno;
extern char *optarg;
static char
*simplify(char *path);
void
errmsg(int severity, int code, char *format, ...);
extern mode_t
newmode(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path);
#define ALLRWX (S_IRWXU | S_IRWXG | S_IRWXO)
int
main(int argc, char *argv[])
{
int pflag, errflg, mflag;
int c, local_errno, tmp_errno;
mode_t cur_umask;
mode_t mode;
mode_t modediff;
char *d;
struct stat buf;
pflag = mflag = errflg = 0;
local_errno = 0;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
cur_umask = umask(0);
mode = ALLRWX;
while ((c = getopt(argc, argv, "m:p")) != EOF) {
switch (c) {
case 'm':
mflag++;
mode = newmode(optarg, ALLRWX, cur_umask, "", "");
break;
case 'p':
pflag++;
break;
case '?':
errflg++;
break;
}
}
if (pflag) {
modediff = cur_umask & (S_IXUSR | S_IWUSR);
if (modediff)
cur_umask &= ~modediff;
}
(void) umask(cur_umask);
argc -= optind;
if (argc < 1 || errflg) {
errmsg(0, 2, gettext(MSGUSAGE));
}
argv = &argv[optind];
errno = 0;
while (argc--) {
if ((d = simplify(*argv++)) == NULL) {
exit(2);
}
if (pflag) {
if (lstat(d, &buf) != -1) {
if (S_ISDIR(buf.st_mode)) {
continue;
} else {
local_errno = EEXIST;
errmsg(0, 0, gettext(MSGEXISTS), d);
continue;
}
}
errno = 0;
if (mkdirp(d, ALLRWX) < 0) {
tmp_errno = errno;
if (tmp_errno == EEXIST) {
if (lstat(d, &buf) != -1) {
if (! S_ISDIR(buf.st_mode)) {
local_errno =
tmp_errno;
errmsg(0, 0, gettext(
MSGEXISTS), d);
continue;
}
} else {
local_errno = tmp_errno;
perror("mkdir");
errmsg(0, 0,
gettext(MSGFAILED), d,
strerror(local_errno));
continue;
}
} else {
local_errno = tmp_errno;
errmsg(0, 0, gettext(MSGFMT1), d,
strerror(tmp_errno));
continue;
}
}
errno = 0;
if (mflag || modediff) {
mode_t tmpmode;
(void) lstat(d, &buf);
if (modediff && !mflag)
tmpmode = (buf.st_mode & 07777)
& ~modediff;
else
tmpmode = mode | (buf.st_mode & 02000);
if (chmod(d, tmpmode) < 0) {
tmp_errno = errno;
local_errno = errno;
errmsg(0, 0, gettext(MSGFMT1), d,
strerror(tmp_errno));
continue;
}
errno = 0;
}
continue;
} else {
errno = 0;
if (mkdir(d, mode) < 0) {
local_errno = tmp_errno = errno;
errmsg(0, 0, gettext(MSGFAILED), d,
strerror(tmp_errno));
continue;
}
if (mflag) {
mode_t tmpmode;
(void) lstat(d, &buf);
tmpmode = mode | (buf.st_mode & 02000);
if (chmod(d, tmpmode) < 0) {
tmp_errno = errno;
local_errno = errno;
errmsg(0, 0, gettext(MSGFMT1), d,
strerror(tmp_errno));
continue;
}
errno = 0;
}
}
}
if (local_errno)
errno = local_errno;
return (errno ? 2: 0);
}
void
errmsg(int severity, int code, char *format, ...)
{
va_list ap;
va_start(ap, format);
(void) fprintf(stderr, "mkdir: ");
(void) vfprintf(stderr, format, ap);
va_end(ap);
if (code > 0) {
exit(code);
}
}
static char *
simplify(char *mbPath)
{
int i;
size_t mbPathlen;
size_t wcPathlen;
wchar_t *wptr;
wchar_t *wcPath;
if (!mbPath)
return (mbPath);
mbPathlen = strlen(mbPath);
if ((wcPath = calloc(sizeof (wchar_t), mbPathlen+1)) == NULL) {
perror("mkdir");
exit(2);
}
if ((wcPathlen = mbstowcs(wcPath, mbPath, mbPathlen)) == (size_t)-1) {
free(wcPath);
return (NULL);
}
for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
*wptr++ = wcPath[i];
if (wcPath[i] == '/') {
i++;
while (wcPath[i] == '/') {
i++;
}
i--;
}
}
*wptr = '\0';
for (wcPathlen = wcslen(wcPath), wptr = wcPath, i = 0;
i < wcPathlen-2 && wcPath[i] == '.' && wcPath[i+1] == '/';
i += 2) {
}
while (i < wcPathlen) {
if (i < wcPathlen-2 && wcPath[i] == '/' &&
wcPath[i+1] == '.' && wcPath[i+2] == '/') {
i += 2;
} else {
*wptr++ = wcPath[i++];
}
}
*wptr = '\0';
if (wcstombs(mbPath, wcPath, mbPathlen) == (size_t)-1) {
free(wcPath);
return (NULL);
}
free(wcPath);
return (mbPath);
}