9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

archive.c (4689B)


      1 #include	"mk.h"
      2 #define	ARMAG	"!<arch>\n"
      3 #define	SARMAG	8
      4 
      5 #define	ARFMAG	"`\n"
      6 #define SARNAME	16
      7 
      8 struct	ar_hdr
      9 {
     10 	char	name[SARNAME];
     11 	char	date[12];
     12 	char	uid[6];
     13 	char	gid[6];
     14 	char	mode[8];
     15 	char	size[10];
     16 	char	fmag[2];
     17 };
     18 #define	SAR_HDR	(SARNAME+44)
     19 
     20 static int dolong = 1;
     21 
     22 static void atimes(char *);
     23 static char *split(char*, char**);
     24 
     25 long
     26 readn(int f, void *av, long n)
     27 {
     28 	char *a;
     29 	long m, t;
     30 
     31 	a = av;
     32 	t = 0;
     33 	while(t < n){
     34 		m = read(f, a+t, n-t);
     35 		if(m <= 0){
     36 			if(t == 0)
     37 				return m;
     38 			break;
     39 		}
     40 		t += m;
     41 	}
     42 	return t;
     43 }
     44 long
     45 atimeof(int force, char *name)
     46 {
     47 	Symtab *sym;
     48 	long t;
     49 	char *archive, *member, buf[512];
     50 
     51 	archive = split(name, &member);
     52 	if(archive == 0)
     53 		Exit();
     54 
     55 	t = mtime(archive);
     56 	sym = symlook(archive, S_AGG, 0);
     57 	if(sym){
     58 		if(force || (t > sym->u.value)){
     59 			atimes(archive);
     60 			sym->u.value = t;
     61 		}
     62 	}
     63 	else{
     64 		atimes(archive);
     65 		/* mark the aggegate as having been done */
     66 		symlook(strdup(archive), S_AGG, "")->u.value = t;
     67 	}
     68 		/* truncate long member name to sizeof of name field in archive header */
     69 	if(dolong)
     70 		snprint(buf, sizeof(buf), "%s(%s)", archive, member);
     71 	else
     72 		snprint(buf, sizeof(buf), "%s(%.*s)", archive, SARNAME, member);
     73 	sym = symlook(buf, S_TIME, 0);
     74 	if (sym)
     75 		return sym->u.value;
     76 	return 0;
     77 }
     78 
     79 void
     80 atouch(char *name)
     81 {
     82 	char *archive, *member;
     83 	int fd, i;
     84 	struct ar_hdr h;
     85 	long t;
     86 
     87 	archive = split(name, &member);
     88 	if(archive == 0)
     89 		Exit();
     90 
     91 	fd = open(archive, ORDWR);
     92 	if(fd < 0){
     93 		fd = create(archive, OWRITE, 0666);
     94 		if(fd < 0){
     95 			fprint(2, "create %s: %r\n", archive);
     96 			Exit();
     97 		}
     98 		write(fd, ARMAG, SARMAG);
     99 	}
    100 	if(symlook(name, S_TIME, 0)){
    101 		/* hoon off and change it in situ */
    102 		LSEEK(fd, SARMAG, 0);
    103 		while(read(fd, (char *)&h, sizeof(h)) == sizeof(h)){
    104 			for(i = SARNAME-1; i > 0 && h.name[i] == ' '; i--)
    105 					;
    106 			h.name[i+1]=0;
    107 			if(strcmp(member, h.name) == 0){
    108 				t = SARNAME-sizeof(h);	/* ughgghh */
    109 				LSEEK(fd, t, 1);
    110 				fprint(fd, "%-12ld", time(0));
    111 				break;
    112 			}
    113 			t = atol(h.size);
    114 			if(t&01) t++;
    115 			LSEEK(fd, t, 1);
    116 		}
    117 	}
    118 	close(fd);
    119 }
    120 
    121 static void
    122 atimes(char *ar)
    123 {
    124 	struct ar_hdr h;
    125 	long t;
    126 	int fd, i, namelen;
    127 	char buf[2048], *p, *strings;
    128 	char name[1024];
    129 	Symtab *sym;
    130 
    131 	strings = nil;
    132 	fd = open(ar, OREAD);
    133 	if(fd < 0)
    134 		return;
    135 
    136 	if(read(fd, buf, SARMAG) != SARMAG){
    137 		close(fd);
    138 		return;
    139 	}
    140 	while(readn(fd, (char *)&h, sizeof(h)) == sizeof(h)){
    141 		t = atol(h.date);
    142 		if(t == 0)	/* as it sometimes happens; thanks ken */
    143 			t = 1;
    144 		namelen = 0;
    145 		if(memcmp(h.name, "#1/", 3) == 0){	/* BSD */
    146 			namelen = atoi(h.name+3);
    147 			if(namelen >= sizeof name){
    148 				namelen = 0;
    149 				goto skip;
    150 			}
    151 			if(readn(fd, name, namelen) != namelen)
    152 				break;
    153 			name[namelen] = 0;
    154 		}else if(memcmp(h.name, "// ", 2) == 0){ /* GNU */
    155 			/* date, uid, gid, mode all ' ' */
    156 			for(i=2; i<16+12+6+6+8; i++)
    157 				if(h.name[i] != ' ')
    158 					goto skip;
    159 			t = atol(h.size);
    160 			if(t&01)
    161 				t++;
    162 			free(strings);
    163 			strings = malloc(t+1);
    164 			if(strings){
    165 				if(readn(fd, strings, t) != t){
    166 					free(strings);
    167 					strings = nil;
    168 					break;
    169 				}
    170 				strings[t] = 0;
    171 				continue;
    172 			}
    173 			goto skip;
    174 		}else if(strings && h.name[0]=='/' && isdigit((uchar)h.name[1])){
    175 			i = strtol(h.name+1, &p, 10);
    176 			if(*p != ' ' || i >= strlen(strings))
    177 				goto skip;
    178 			p = strings+i;
    179 			for(; *p && *p != '/'; p++)
    180 				;
    181 			namelen = p-(strings+i);
    182 			if(namelen >= sizeof name){
    183 				namelen = 0;
    184 				goto skip;
    185 			}
    186 			memmove(name, strings+i, namelen);
    187 			name[namelen] = 0;
    188 			namelen = 0;
    189 		}else{
    190 			strncpy(name, h.name, sizeof(h.name));
    191 			for(i = sizeof(h.name)-1; i > 0 && name[i] == ' '; i--)
    192 					;
    193 			if(name[i] == '/')		/* system V bug */
    194 				i--;
    195 			name[i+1]=0;
    196 		}
    197 		snprint(buf, sizeof buf, "%s(%s)", ar, name);
    198 		sym = symlook(strdup(buf), S_TIME, (void *)t);
    199 		sym->u.value = t;
    200 	skip:
    201 		t = atol(h.size);
    202 		if(t&01) t++;
    203 		t -= namelen;
    204 		LSEEK(fd, t, 1);
    205 	}
    206 	close(fd);
    207 	free(strings);
    208 }
    209 
    210 static int
    211 type(char *file)
    212 {
    213 	int fd;
    214 	char buf[SARMAG];
    215 
    216 	fd = open(file, OREAD);
    217 	if(fd < 0){
    218 		if(symlook(file, S_BITCH, 0) == 0){
    219 			if(strlen(file) < 2 || strcmp(file+strlen(file)-2, ".a") != 0)
    220 				Bprint(&bout, "%s doesn't exist: assuming it will be an archive\n", file);
    221 			symlook(file, S_BITCH, (void *)file);
    222 		}
    223 		return 1;
    224 	}
    225 	if(read(fd, buf, SARMAG) != SARMAG){
    226 		close(fd);
    227 		return 0;
    228 	}
    229 	close(fd);
    230 	return !strncmp(ARMAG, buf, SARMAG);
    231 }
    232 
    233 static char*
    234 split(char *name, char **member)
    235 {
    236 	char *p, *q;
    237 
    238 	p = strdup(name);
    239 	q = utfrune(p, '(');
    240 	if(q){
    241 		*q++ = 0;
    242 		if(member)
    243 			*member = q;
    244 		q = utfrune(q, ')');
    245 		if (q)
    246 			*q = 0;
    247 		if(type(p))
    248 			return p;
    249 		free(p);
    250 		fprint(2, "mk: '%s' is not an archive\n", name);
    251 	}
    252 	return 0;
    253 }