| /* |
| * Copyright (c) 2000-2001 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * 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. |
| * |
| * This program is distributed in the hope that it would 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 the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "config.h" |
| |
| #include "path.h" |
| |
| struct pem { |
| char *pem_head; |
| char *pem_next; |
| }; |
| |
| typedef struct pem pem_t; |
| |
| static pem_t * pem_alloc(char *path); |
| static void pem_free(pem_t *pemp); |
| static char * pem_next(pem_t *pemp); |
| |
| #define PAMAX 1024 |
| |
| struct pa { |
| char *pa_array[PAMAX]; |
| int pa_cnt; |
| }; |
| |
| typedef struct pa pa_t; |
| |
| static pa_t * pa_alloc(void); |
| static void pa_free(pa_t *pap); |
| static void pa_append(pa_t *pap, char *pep); |
| static int pa_peel(pa_t *pap); |
| static char * pa_gen(pa_t *pap); |
| |
| char * |
| path_diff(char *path, char *base) |
| { |
| char *diff; |
| |
| assert(*base == '/'); |
| assert(*path == '/'); |
| |
| if (!path_beginswith(path, base)) { |
| return 0; |
| } |
| |
| for (; *base && *path == *base; path++, base++) |
| ; |
| |
| if (*path == 0) { |
| return 0; |
| } |
| |
| if (*path == '/') { |
| path++; |
| } |
| |
| diff = (char *)calloc(1, strlen(path) + 1); |
| assert(diff); |
| strcpy(diff, path); |
| |
| return diff; |
| } |
| |
| int |
| path_beginswith(char *path, char *base) |
| { |
| if (!base) { |
| return 0; |
| } |
| return !strncmp(base, path, strlen(base)); |
| } |
| |
| char * |
| path_reltoabs(char *dir, char *basedir) |
| { |
| char *absdir; |
| |
| /* check if the path starts with a / or |
| * is a remote path (i.e. contains machine:/path/name). |
| */ |
| if ((*dir != '/') && (strchr(dir, ':') == 0)) { |
| char *absdir; |
| absdir = (char *)malloc(strlen(basedir) |
| + |
| 1 |
| + |
| strlen(dir) |
| + |
| 1); |
| assert(absdir); |
| |
| (void )sprintf(absdir, "%s/%s", basedir, dir); |
| |
| dir = absdir; |
| } |
| |
| if (strchr(dir, ':') == 0) { |
| absdir = path_normalize(dir); |
| } else { |
| absdir = (char *)malloc(strlen(dir) + 1); |
| (void )sprintf(absdir, "%s", dir); |
| } |
| |
| return absdir; |
| } |
| |
| char * |
| path_normalize(char *path) |
| { |
| pem_t *pemp = pem_alloc(path); |
| pa_t *pap = pa_alloc(); |
| char *pep; |
| char *npath; |
| |
| assert(path[0] == '/'); |
| |
| while ((pep = pem_next(pemp)) != 0) { |
| if (!strcmp(pep, "")) { |
| free((void *)pep); |
| continue; |
| } |
| if (!strcmp(pep, ".")) { |
| free((void *)pep); |
| continue; |
| } |
| if (!strcmp(pep, "..")) { |
| int ok; |
| free((void *)pep); |
| ok = pa_peel(pap); |
| if (!ok) { |
| pa_free(pap); |
| pem_free(pemp); |
| return 0; |
| } |
| continue; |
| } |
| pa_append(pap, pep); |
| } |
| |
| npath = pa_gen(pap); |
| pa_free(pap); |
| pem_free(pemp); |
| |
| return npath; |
| } |
| |
| static pem_t * |
| pem_alloc(char *path) |
| { |
| pem_t *pemp = (pem_t *)calloc(1, sizeof(pem_t)); |
| assert(pemp); |
| pemp->pem_head = path; |
| pemp->pem_next = pemp->pem_head; |
| |
| return pemp; |
| } |
| |
| static void |
| pem_free(pem_t *pemp) |
| { |
| free((void *)pemp); |
| } |
| |
| static char * |
| pem_next(pem_t *pemp) |
| { |
| char *nextnext; |
| size_t len; |
| char *p; |
| |
| /* no more left |
| */ |
| if (*pemp->pem_next == 0) { |
| return 0; |
| } |
| |
| /* find the following slash |
| */ |
| nextnext = strchr(pemp->pem_next + 1, '/'); |
| |
| /* if end of string encountered, place next next at end of string |
| */ |
| if (!nextnext) { |
| for (nextnext = pemp->pem_next; *nextnext; nextnext++) |
| ; |
| } |
| |
| /* determine the length of the path element, sans the leading slash |
| */ |
| len = (size_t)(nextnext - pemp->pem_next - 1); |
| |
| /* allocate buffer to hold the path element, incl null termination |
| */ |
| p = (char *)malloc(len + 1); |
| assert(p); |
| |
| /* copy the path element into the buffer |
| */ |
| strncpy(p, pemp->pem_next + 1, len); |
| |
| /* null-terminate |
| */ |
| p[len] = 0; |
| |
| /* update next |
| */ |
| pemp->pem_next = nextnext; |
| |
| /* return the allocated buffer to the caller |
| */ |
| return p; |
| } |
| |
| static pa_t * |
| pa_alloc(void) |
| { |
| pa_t *pap = (pa_t *)calloc(1, sizeof(pa_t)); |
| assert(pap); |
| |
| return pap; |
| } |
| |
| static void |
| pa_free(pa_t *pap) |
| { |
| int i; |
| |
| for (i = 0; i < pap->pa_cnt; i++) { |
| free((void *)pap->pa_array[i]); |
| } |
| |
| free((void *)pap); |
| } |
| |
| static void |
| pa_append(pa_t *pap, char *pep) |
| { |
| assert(pap->pa_cnt < PAMAX); |
| |
| pap->pa_array[pap->pa_cnt] = pep; |
| |
| pap->pa_cnt++; |
| } |
| |
| static int |
| pa_peel(pa_t *pap) |
| { |
| if (pap->pa_cnt <= 0) { |
| assert(pap->pa_cnt == 0); |
| return 0; |
| } |
| |
| pap->pa_cnt--; |
| assert(pap->pa_array[pap->pa_cnt]); |
| free((void *)pap->pa_array[pap->pa_cnt]); |
| pap->pa_array[pap->pa_cnt] = 0; |
| |
| return 1; |
| } |
| |
| static char * |
| pa_gen(pa_t *pap) |
| { |
| size_t sz; |
| int i; |
| char *retp; |
| char *p; |
| |
| sz = 0; |
| for (i = 0; i < pap->pa_cnt; i++) { |
| sz += strlen(pap->pa_array[i]) + 1; |
| } |
| if (i == 0) |
| sz++; |
| sz++; |
| |
| retp = (char *)malloc(sz); |
| |
| if (pap->pa_cnt <= 0) { |
| assert(pap->pa_cnt == 0); |
| sprintf(retp, "/"); |
| } else { |
| p = retp; |
| for (i = 0; i < pap->pa_cnt; i++) { |
| sprintf(p, "/%s", pap->pa_array[ i]); |
| p += strlen(p); |
| } |
| } |
| |
| return retp; |
| } |