pathcomp.c revision 204556
1/*- 2 * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/usr.bin/csup/pathcomp.c 204556 2010-03-02 07:26:07Z lulf $ 27 */ 28 29#include <assert.h> 30#include <stdlib.h> 31#include <string.h> 32 33#include "misc.h" 34#include "pathcomp.h" 35 36struct pathcomp { 37 char *target; 38 size_t targetlen; 39 char *trashed; 40 char *prev; 41 size_t prevlen; 42 size_t goal; 43 size_t curlen; 44}; 45 46struct pathcomp * 47pathcomp_new(void) 48{ 49 struct pathcomp *pc; 50 51 pc = xmalloc(sizeof(struct pathcomp)); 52 pc->curlen = 0; 53 pc->target = NULL; 54 pc->targetlen = 0; 55 pc->trashed = NULL; 56 pc->prev = NULL; 57 pc->prevlen = 0; 58 return (pc); 59} 60 61int 62pathcomp_put(struct pathcomp *pc, int type, char *path) 63{ 64 char *cp; 65 66 assert(pc->target == NULL); 67 if (*path == '/') 68 return (-1); 69 70 switch (type) { 71 case PC_DIRDOWN: 72 pc->target = path; 73 pc->targetlen = strlen(path); 74 break; 75 case PC_FILE: 76 case PC_DIRUP: 77 cp = strrchr(path, '/'); 78 pc->target = path; 79 if (cp != NULL) 80 pc->targetlen = cp - path; 81 else 82 pc->targetlen = 0; 83 break; 84 } 85 if (pc->prev != NULL) 86 pc->goal = commonpathlength(pc->prev, pc->prevlen, pc->target, 87 pc->targetlen); 88 else 89 pc->goal = 0; 90 if (pc->curlen == pc->goal) /* No need to go up. */ 91 pc->goal = pc->targetlen; 92 return (0); 93} 94 95int 96pathcomp_get(struct pathcomp *pc, int *type, char **name) 97{ 98 char *cp; 99 size_t slashpos, start; 100 101 if (pc->curlen > pc->goal) { /* Going up. */ 102 assert(pc->prev != NULL); 103 pc->prev[pc->curlen] = '\0'; 104 cp = pc->prev + pc->curlen - 1; 105 while (cp >= pc->prev) { 106 if (*cp == '/') 107 break; 108 cp--; 109 } 110 if (cp >= pc->prev) 111 slashpos = cp - pc->prev; 112 else 113 slashpos = 0; 114 pc->curlen = slashpos; 115 if (pc->curlen <= pc->goal) { /* Done going up. */ 116 assert(pc->curlen == pc->goal); 117 pc->goal = pc->targetlen; 118 } 119 *type = PC_DIRUP; 120 *name = pc->prev; 121 return (1); 122 } else if (pc->curlen < pc->goal) { /* Going down. */ 123 /* Restore the previously overwritten '/' character. */ 124 if (pc->trashed != NULL) { 125 *pc->trashed = '/'; 126 pc->trashed = NULL; 127 } 128 if (pc->curlen == 0) 129 start = pc->curlen; 130 else 131 start = pc->curlen + 1; 132 slashpos = start; 133 while (slashpos < pc->goal) { 134 if (pc->target[slashpos] == '/') 135 break; 136 slashpos++; 137 } 138 if (pc->target[slashpos] != '\0') { 139 assert(pc->target[slashpos] == '/'); 140 pc->trashed = pc->target + slashpos; 141 pc->target[slashpos] = '\0'; 142 } 143 pc->curlen = slashpos; 144 *type = PC_DIRDOWN; 145 *name = pc->target; 146 return (1); 147 } else { /* Done. */ 148 if (pc->target != NULL) { 149 if (pc->trashed != NULL) { 150 *pc->trashed = '/'; 151 pc->trashed = NULL; 152 } 153 if (pc->prev != NULL) 154 free(pc->prev); 155 pc->prev = xmalloc(pc->targetlen + 1); 156 memcpy(pc->prev, pc->target, pc->targetlen); 157 pc->prev[pc->targetlen] = '\0'; 158 pc->prevlen = pc->targetlen; 159 pc->target = NULL; 160 pc->targetlen = 0; 161 } 162 return (0); 163 } 164} 165 166void 167pathcomp_finish(struct pathcomp *pc) 168{ 169 170 pc->target = NULL; 171 pc->targetlen = 0; 172 pc->goal = 0; 173} 174 175void 176pathcomp_free(struct pathcomp *pc) 177{ 178 179 if (pc->prev != NULL) 180 free(pc->prev); 181 free(pc); 182} 183