wdvi

network DVI viewer
Log | Files | Refs

util.c (10263B)


      1 /*========================================================================*\
      2 
      3 Copyright (c) 1990-2013  Paul Vojta
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining a copy
      6 of this software and associated documentation files (the "Software"), to
      7 deal in the Software without restriction, including without limitation the
      8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      9 sell copies of the Software, and to permit persons to whom the Software is
     10 furnished to do so, subject to the following conditions:
     11 
     12 The above copyright notice and this permission notice shall be included in
     13 all copies or substantial portions of the Software.
     14 
     15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18 PAUL VOJTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     21 
     22 NOTE:
     23 	xdvi is based on prior work, as noted in the modification history
     24 	in xdvi.c.
     25 
     26 \*========================================================================*/
     27 
     28 #include <sys/socket.h>
     29 #include <sys/file.h>	/* this defines FASYNC */
     30 #include <sys/ioctl.h>	/* this defines SIOCSPGRP and FIOASYNC */
     31 
     32 #include <ctype.h>	/* tolower() */
     33 #include <err.h>
     34 #include <stdlib.h>	/* malloc(), realloc() */
     35 #include <string.h>	/* strdup(), memcpy() */
     36 #include <strings.h>	/* bcopy() */
     37 #include <pwd.h>	/* getpwuid(), getpwnam() */
     38 #include <unistd.h>	/* getuid(), getpid() */
     39 
     40 #include "util.h"
     41 #include "xdvi.h"
     42 
     43 
     44 /*
     45  *	General utility routines.
     46  */
     47 
     48 /*
     49  *	Either (re)allocate storage or fail with explanation.
     50  */
     51 
     52 void *
     53 xmalloc(size_t size)
     54 {
     55 	void	*mem	= malloc(size);
     56 
     57 	if (mem == NULL)
     58 	    err(1, "malloc %zu bytes", size);
     59 	return mem;
     60 }
     61 
     62 
     63 void *
     64 xrealloc(void *where, size_t size)
     65 {
     66 	void	*mem	= realloc(where, size);
     67 
     68 	if (mem == NULL)
     69 	    err(1, "realloc %zu bytes", size);
     70 	return mem;
     71 }
     72 
     73 
     74 /*
     75  *	Allocate a new string.
     76  */
     77 
     78 char *
     79 xstrdup(const char *str)
     80 {
     81 	char	*new;
     82 
     83 	if ((new = strdup(str)) == NULL)
     84 		err(1, "strdup");
     85 	return new;
     86 }
     87 
     88 
     89 /*
     90  *	Allocate a new string.  The second argument is the length.
     91  */
     92 
     93 char *
     94 xmemdup(const char *str, size_t len)
     95 {
     96 	char	*new;
     97 
     98 	new = xmalloc(len);
     99 	memcpy(new, str, len);
    100 	return new;
    101 }
    102 
    103 
    104 /*
    105  *	Expand the matrix *ffline to at least the given size.
    106  */
    107 
    108 void
    109 expandline(n)
    110 	size_t	n;
    111 {
    112 	size_t	newlen	= n + 128;
    113 
    114 	ffline = (ffline == NULL) ? xmalloc(newlen) : xrealloc(ffline, newlen);
    115 	ffline_len = newlen;
    116 }
    117 
    118 
    119 /*
    120  *	Allocate bitmap for given font and character
    121  */
    122 
    123 void
    124 alloc_bitmap(bitmap)
    125 	struct bitmap	*bitmap;
    126 {
    127 	unsigned int	size;
    128 
    129 	/* width must be multiple of 16 bits for raster_op */
    130 	bitmap->bytes_wide = ROUNDUP((int) bitmap->w, BMBITS) * BMBYTES;
    131 	size = bitmap->bytes_wide * bitmap->h;
    132 	bitmap->bits = xmalloc(size != 0 ? size : 1);
    133 }
    134 
    135 
    136 #ifndef KPATHSEA
    137 
    138 /*
    139  *	Put a variable in the environment or abort on error.
    140  */
    141 void
    142 xputenv(const char *var, const char *value)
    143 {
    144 	if (setenv(var, value, True) != 0)
    145 	    err(1, "setenv");
    146 	return;
    147 }
    148 
    149 #endif /* not KPATHSEA */
    150 
    151 
    152 /*
    153  *	Hopefully a self-explanatory name.  This code assumes the second
    154  *	argument is lower case.
    155  */
    156 
    157 int
    158 memicmp(const char *s1, const char *s2, size_t n)
    159 {
    160 	while (n > 0) {
    161 	    int i = tolower(*s1) - *s2;
    162 	    if (i != 0) return i;
    163 	    ++s1;
    164 	    ++s2;
    165 	    --n;
    166 	}
    167 	return 0;
    168 }
    169 
    170 
    171 
    172 /*
    173  *	Open a file in the given mode.
    174  */
    175 
    176 FILE *
    177 xfopen(const char *filename, const char *type)
    178 {
    179 	FILE	*f;
    180 
    181 	f = fopen(filename, type);
    182 
    183 	if (f != NULL)
    184 	    (void) fcntl(fileno(f), F_SETFD, 1);
    185 
    186 	return f;
    187 }
    188 
    189 
    190 /*
    191  *	Create a pipe.
    192  *	We use socketpair() instead of pipe() because several operating
    193  *	systems don't support SIGPOLL/SIGIO on pipes:
    194  *		SGI IRIX 6.5	F_SETOWN not implemented
    195  *		Linux 2.4.2	Not supported
    196  */
    197 
    198 int
    199 xpipe(int *fd)
    200 {
    201 	int	retval;
    202 
    203 	retval = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
    204 
    205 	return retval;
    206 }
    207 
    208 
    209 /*
    210  *	Perform tilde expansion, updating the character pointer unless the
    211  *	user was not found.
    212  */
    213 
    214 const	struct passwd *
    215 ff_getpw(pp, p_end)
    216 	const	char	**pp;
    217 	const	char	*p_end;
    218 {
    219 	const	char		*p	= *pp;
    220 	const	char		*p1;
    221 	unsigned		len;
    222 	const	struct passwd	*pw;
    223 	int			count;
    224 
    225 	++p;	/* skip the tilde */
    226 	p1 = p;
    227 	while (p1 < p_end && *p1 != '/') ++p1;
    228 	len = p1 - p;
    229 
    230 	if (len != 0) {
    231 	    if (len >= ffline_len)
    232 		expandline(len);
    233 	    bcopy(p, ffline, len);
    234 	    ffline[len] = '\0';
    235 	}
    236 
    237 	for (count = 0;; ++count) {
    238 	    if (len == 0)	/* if no user name */
    239 		pw = getpwuid(getuid());
    240 	    else
    241 		pw = getpwnam(ffline);
    242 
    243 	    if (pw != NULL) {
    244 		*pp = p1;
    245 		return pw;
    246 	    }
    247 
    248 	    /* On some systems, getpw{uid,nam} return without setting errno,
    249 	     * even if the call failed because of too many open files.
    250 	     * Therefore, we play it safe here.
    251 	     */
    252 	    if (count >= 2 && len != 0 && getpwuid(getuid()) != NULL)
    253 		return NULL;
    254 	}
    255 }
    256 
    257 
    258 /*
    259  *
    260  *      Read size bytes from the FILE fp, constructing them into a
    261  *      signed/unsigned integer.
    262  *
    263  */
    264 
    265 unsigned long
    266 num(fp, size)
    267 	FILE	*fp;
    268 	int	size;
    269 {
    270 	long	x	= 0;
    271 
    272 	while (size--) x = (x << 8) | one(fp);
    273 	return x;
    274 }
    275 
    276 long
    277 snum(fp, size)
    278 	FILE	*fp;
    279 	int	size;
    280 {
    281 	long	x;
    282 
    283 	x = (signed char) getc(fp);
    284 	while (--size) x = (x << 8) | one(fp);
    285 	return x;
    286 }
    287 
    288 
    289 
    290 /*
    291  *	General AVL tree mechanism.  Search for a node, and return it if found.
    292  *	Otherwise insert a node.
    293  *	This uses the AVL algorithm from Knuth Vol. 3.
    294  */
    295 
    296 struct avl *
    297 avladd(key, key_len, headp, size)
    298 	const char	*key;
    299 	size_t		key_len;
    300 	struct avl	**headp;
    301 	size_t		size;
    302 {
    303 	struct avl	*ap;
    304 	struct avl	**app;
    305 	struct avl	*sp;	/* place where rebalancing may be necessary */
    306 	struct avl	**spp;	/* points to sp */
    307 	int		i;
    308 
    309 	/* Search */
    310 	spp = app = headp;
    311 	for (;;) {
    312 	    ap = *app;
    313 	    if (ap == NULL)	/* bottom of tree */
    314 		break;
    315 	    if (ap->bal != 0)
    316 		spp = app;
    317 	    i = key_len - ap->key_len;
    318 	    if (i == 0)
    319 		i = memcmp(key, ap->key, key_len);
    320 	    if (i == 0)		/* found record already */
    321 		return ap;
    322 	    if (i < 0)		/* move left */
    323 		app = &ap->left;
    324 	    else
    325 		app = &ap->right;
    326 	}
    327 
    328 	/* Insert */
    329 	ap = xmalloc(size);
    330 	ap->key = key;
    331 	ap->key_len = key_len;
    332 	ap->bal = 0;
    333 	ap->left = ap->right = NULL;
    334 	*app = ap;
    335 
    336 	/* Adjust balance factors */
    337 	sp = *spp;
    338 	if (sp == ap)
    339 	    return ap;
    340 	i = key_len - sp->key_len;
    341 	if (i == 0)
    342 	    i = memcmp(key, sp->key, key_len);
    343 	sp = (i < 0 ? sp->left : sp->right);
    344 	while (sp != ap) {
    345 	    i = key_len - sp->key_len;
    346 	    if (i == 0)
    347 		i = memcmp(key, sp->key, key_len);
    348 	    if (i < 0) {
    349 		sp->bal = -1;
    350 		sp = sp->left;
    351 	    }
    352 	    else {
    353 		sp->bal = 1;
    354 		sp = sp->right;
    355 	    }
    356 	}
    357 
    358 	/* Balancing act */
    359 	sp = *spp;
    360 	i = key_len - sp->key_len;
    361 	if (i == 0)
    362 	    i = memcmp(key, sp->key, key_len);
    363 	if (i < 0) {
    364 	    if (sp->bal >= 0)
    365 		--sp->bal;
    366 	    else {	/* need to rebalance */
    367 		struct avl *left;
    368 
    369 		left = sp->left;
    370 		if (left->bal < 0) {	/* single rotation */
    371 		    sp->left = left->right;
    372 		    left->right = sp;
    373 		    sp->bal = left->bal = 0;
    374 		    *spp = left;
    375 		}
    376 		else {			/* double rotation */
    377 		    struct avl	*newtop;
    378 
    379 		    newtop = left->right;
    380 		    sp->left = newtop->right;
    381 		    newtop->right = sp;
    382 		    left->right = newtop->left;
    383 		    newtop->left = left;
    384 		    sp->bal = left->bal = 0;
    385 		    if (newtop->bal < 0) ++sp->bal;
    386 		    else if (newtop->bal > 0) --left->bal;
    387 		    newtop->bal = 0;
    388 		    *spp = newtop;
    389 		}
    390 	    }
    391 	}
    392 	else {
    393 	    if (sp->bal <= 0)
    394 		++sp->bal;
    395 	    else {	/* need to rebalance */
    396 		struct avl *right;
    397 
    398 		right = sp->right;
    399 		if (right->bal > 0) {	/* single rotation */
    400 		    sp->right = right->left;
    401 		    right->left = sp;
    402 		    sp->bal = right->bal = 0;
    403 		    *spp = right;
    404 		}
    405 		else {			/* double rotation */
    406 		    struct avl	*newtop;
    407 
    408 		    newtop = right->left;
    409 		    sp->right = newtop->left;
    410 		    newtop->left = sp;
    411 		    right->left = newtop->right;
    412 		    newtop->right = right;
    413 		    sp->bal = right->bal = 0;
    414 		    if (newtop->bal > 0) --sp->bal;
    415 		    else if (newtop->bal < 0) ++right->bal;
    416 		    newtop->bal = 0;
    417 		    *spp = newtop;
    418 		}
    419 	    }
    420 	}
    421 
    422 	return ap;
    423 }
    424 
    425 /*
    426  *	Prepare the file descriptor to generate SIGPOLL/SIGIO events.
    427  *	If called with a True argument, set it up for non-blocking I/O.
    428  */
    429 
    430 void
    431 prep_fd(int fd, Boolean noblock)
    432 {
    433 	/* Set file descriptor for non-blocking I/O */
    434 	if (noblock)
    435 	    (void) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
    436 	{
    437 # ifdef FASYNC
    438 	    if (fcntl(fd, F_SETOWN, getpid()) == -1)
    439 		perror("xdvi: fcntl F_SETOWN");
    440 	    if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | FASYNC) == -1)
    441 		perror("xdvi: fcntl F_SETFL");
    442 # elif defined SIOCSPGRP && defined FIOASYNC
    443 	    /* For HP-UX B.10.10 and maybe others.  See "man 7 socket".  */
    444 	    int arg;
    445 
    446 	    arg = getpid();
    447 	    if (ioctl(fd, SIOCSPGRP, &arg) == -1)
    448 		perror("xdvi: ioctl SIOCSPGRP");
    449 	    arg = 1;
    450 	    if (ioctl(fd, FIOASYNC, &arg) == -1)
    451 		perror("xdvi: ioctl FIOASYNC");
    452 # endif
    453 	}
    454 }
    455 
    456 /*
    457  * Try and parse dest_url[] as a url:
    458  * - ignore protocol if found
    459  * - hostname is either what's remaining or up to a colon or slash character
    460  * - if colon is found, port number is either what is remaining or up to slash
    461  *   character
    462  * - if slash is found the rest of the string is a document path
    463  *
    464  * On success a struct url pointer is returned that the caller must
    465  * free, otherwise NULL returned.
    466  */
    467 struct url *
    468 parse_url(const char dest_url[])
    469 {
    470 	struct url	*url;
    471 	char		*sep_url;
    472 	char		*proto_loc;
    473 	char		*reset;
    474 
    475 	url = xmalloc(sizeof(struct url));
    476 	url->port = "443";
    477 	url->orig_url = dest_url;
    478 	url->document = "";
    479 
    480 	/* dest_url[] will be modified */
    481 	sep_url = xstrdup(dest_url);
    482 
    483 	/* Do not care if this is specified, https is assumed. */
    484 	if ((proto_loc = strstr(sep_url, "://")) != NULL) {
    485 		*proto_loc = '\0';
    486 		if (strcmp(sep_url, "https") != 0)
    487 			warnx("ignoring scheme '%s'", sep_url);
    488 		sep_url = proto_loc + strlen("://");
    489 	}
    490 
    491 	url->hostname = sep_url;
    492 
    493 	reset = strsep(&sep_url, ":");
    494 	if (sep_url != NULL)
    495 		url->port = sep_url;
    496 	else
    497 		sep_url = reset;
    498 
    499 	strsep(&sep_url, "/");
    500 	if (sep_url != NULL)
    501 		/* / was found, set document path */
    502 		url->document = sep_url;
    503 
    504 	return url;
    505 }