libbb: add unit tests for is_prefixed_with()
[oweals/busybox.git] / libbb / parse_mode.c
index ba34ea9293955ae4cc1b1b4bf84d8b6984908487..5a4e1c579c3fdfa1c330b61f16fded15e98f05a5 100644 (file)
 /* vi: set sw=4 ts=4: */
 /*
- * Utility routines.
+ * parse_mode implementation for busybox
  *
- * Copyright (C) many different people.  If you wrote this, please
- * acknowledge your work.
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * 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
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
+
 #include "libbb.h"
 
+/* This function is used from NOFORK applets. It must not allocate anything */
+
+#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
 
-/* This function parses the sort of string you might pass
- * to chmod (i.e., [ugoa]{+|-|=}[rwxst] ) and returns the
- * correct mode described by the string. */
-extern int parse_mode(const char *s, mode_t * theMode)
+int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
 {
-       static const mode_t group_set[] = { 
-               S_ISUID | S_IRWXU,              /* u */
-               S_ISGID | S_IRWXG,              /* g */
-               S_IRWXO,                                /* o */
-               S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO /* a */
+       static const mode_t who_mask[] = {
+               S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
+               S_ISUID | S_IRWXU,           /* u */
+               S_ISGID | S_IRWXG,           /* g */
+               S_IRWXO                      /* o */
        };
-
-       static const mode_t mode_set[] = {
+       static const mode_t perm_mask[] = {
                S_IRUSR | S_IRGRP | S_IROTH, /* r */
                S_IWUSR | S_IWGRP | S_IWOTH, /* w */
                S_IXUSR | S_IXGRP | S_IXOTH, /* x */
-               S_ISUID | S_ISGID,              /* s */
-               S_ISVTX                                 /* t */
+               S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
+               S_ISUID | S_ISGID,           /* s */
+               S_ISVTX                      /* t */
        };
-
-       static const char group_chars[] = "ugoa";
-       static const char mode_chars[] = "rwxst";
+       static const char who_chars[] ALIGN1 = "augo";
+       static const char perm_chars[] ALIGN1 = "rwxXst";
 
        const char *p;
+       mode_t wholist;
+       mode_t permlist;
+       mode_t new_mode;
+       char op;
 
-       mode_t andMode =
-               S_ISVTX | S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
-       mode_t orMode = 0;
-       mode_t mode;
-       mode_t groups;
-       char type;
-       char c;
+       if ((unsigned char)(*s - '0') < 8) {
+               unsigned long tmp;
+               char *e;
 
-       if (s==NULL) {
-               return (FALSE);
+               tmp = strtoul(s, &e, 8);
+               if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
+                       return 0;
+               }
+               *current_mode = tmp;
+               return 1;
        }
 
-       do {
-               mode = 0;
-               groups = 0;
-       NEXT_GROUP:
-               if ((c = *s++) == '\0') {
-                       return -1;
+       new_mode = *current_mode;
+
+       /* Note: we allow empty clauses, and hence empty modes.
+        * We treat an empty mode as no change to perms. */
+
+       while (*s) {  /* Process clauses. */
+               if (*s == ',') {  /* We allow empty clauses. */
+                       ++s;
+                       continue;
                }
-               for (p=group_chars ; *p ; p++) {
-                       if (*p == c) {
-                               groups |= group_set[(int)(p-group_chars)];
-                               goto NEXT_GROUP;
+
+               /* Get a wholist. */
+               wholist = 0;
+ WHO_LIST:
+               p = who_chars;
+               do {
+                       if (*p == *s) {
+                               wholist |= who_mask[(int)(p-who_chars)];
+                               if (!*++s) {
+                                       return 0;
+                               }
+                               goto WHO_LIST;
                        }
-               }
-               switch (c) {
-                       case '=':
-                       case '+':
-                       case '-':
-                               type = c;
-                               if (groups == 0) { /* The default is "all" */
-                                       groups |= S_ISUID | S_ISGID | S_ISVTX
-                                                       | S_IRWXU | S_IRWXG | S_IRWXO;
+               } while (*++p);
+
+               do {    /* Process action list. */
+                       if ((*s != '+') && (*s != '-')) {
+                               if (*s != '=') {
+                                       return 0;
                                }
-                               break;
-                       default:
-                               if ((c < '0') || (c > '7') || (mode | groups)) {
-                                       return (FALSE);
-                               } else {
-                                       *theMode = strtol(--s, NULL, 8);
-                                       return (TRUE);
+                               /* Since op is '=', clear all bits corresponding to the
+                                * wholist, or all file bits if wholist is empty. */
+                               permlist = ~FILEMODEBITS;
+                               if (wholist) {
+                                       permlist = ~wholist;
                                }
-               }
+                               new_mode &= permlist;
+                       }
+                       op = *s++;
 
-       NEXT_MODE:
-               if (((c = *s++) != '\0') && (c != ',')) {
-                       for (p=mode_chars ; *p ; p++) {
-                               if (*p == c) {
-                                       mode |= mode_set[(int)(p-mode_chars)];
-                                       goto NEXT_MODE;
+                       /* Check for permcopy. */
+                       p = who_chars + 1;  /* Skip 'a' entry. */
+                       do {
+                               if (*p == *s) {
+                                       int i = 0;
+                                       permlist = who_mask[(int)(p-who_chars)]
+                                                        & (S_IRWXU | S_IRWXG | S_IRWXO)
+                                                        & new_mode;
+                                       do {
+                                               if (permlist & perm_mask[i]) {
+                                                       permlist |= perm_mask[i];
+                                               }
+                                       } while (++i < 3);
+                                       ++s;
+                                       goto GOT_ACTION;
                                }
-                       }
-                       break;                          /* We're done so break out of loop.*/
-               }
-               switch (type) {
-                       case '=':
-                               andMode &= ~(groups); /* Now fall through. */
-                       case '+':
-                               orMode |= mode & groups;
-                               break;
-                       case '-':
-                               andMode &= ~(mode & groups);
-                               orMode &= ~(mode & groups);
-                               break;
-               }
-       } while (c == ',');
+                       } while (*++p);
 
-       *theMode &= andMode;
-       *theMode |= orMode;
+                       /* It was not a permcopy, so get a permlist. */
+                       permlist = 0;
+ PERM_LIST:
+                       p = perm_chars;
+                       do {
+                               if (*p == *s) {
+                                       if ((*p != 'X')
+                                        || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
+                                       ) {
+                                               permlist |= perm_mask[(int)(p-perm_chars)];
+                                       }
+                                       if (!*++s) {
+                                               break;
+                                       }
+                                       goto PERM_LIST;
+                               }
+                       } while (*++p);
+ GOT_ACTION:
+                       if (permlist) { /* The permlist was nonempty. */
+                               mode_t tmp = wholist;
+                               if (!wholist) {
+                                       mode_t u_mask = umask(0);
+                                       umask(u_mask);
+                                       tmp = ~u_mask;
+                               }
+                               permlist &= tmp;
+                               if (op == '-') {
+                                       new_mode &= ~permlist;
+                               } else {
+                                       new_mode |= permlist;
+                               }
+                       }
+               } while (*s && (*s != ','));
+       }
 
-       return TRUE;
+       *current_mode = new_mode;
+       return 1;
 }
-
-/* END CODE */
-/*
-Local Variables:
-c-file-style: "linux"
-c-basic-offset: 4
-tab-width: 4
-End:
-*/