/* * Copyright (c) 2005 - 2006, Nils R. Weller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "preprocess.h" #include "n_libc.h" static size_t n_xmalloc_guard; void n_xmalloc_set_guard(size_t nbytes) { n_xmalloc_guard = nbytes; } void * (n_xmalloc)(size_t nbytes) { void *ret; if ((ret = malloc(nbytes+n_xmalloc_guard)) == NULL) { perror("malloc"); exit(EXIT_FAILURE); } return ret; } void * (n_xrealloc)(void *block, size_t nbytes) { void *ret; if ((ret = realloc(block, nbytes)) == NULL) { perror("realloc"); exit(EXIT_FAILURE); } return ret; } char * (n_strdup)(const char *msg) { char *ret; size_t len = strlen(msg) + 1; if ((ret = malloc(len)) == NULL) { return NULL; } memcpy(ret, msg, len); return ret; } char * (n_xstrdup)(const char *msg) { char *ret; if ((ret = n_strdup(msg)) == NULL) { perror("n_strdup"); exit(EXIT_FAILURE); } return ret; } void * (n_memdup)(const void *data, size_t len) { void *ret; if ((ret = malloc(len)) == NULL) { return NULL; } memcpy(ret, data, len); return ret; } void * (n_xmemdup)(const void *data, size_t len) { void *ret; if ((ret = n_memdup(data, len)) == NULL) { perror("n_memdup"); exit(EXIT_FAILURE); } return ret; } void (x_fprintf)(FILE *fd, const char *fmt, ...) { va_list va; int rc; if (g_ignore_text) return; va_start(va, fmt); rc = vfprintf(fd, fmt, va); va_end(va); if (rc < 0) { perror("vfprintf"); exit(EXIT_FAILURE); } } void (x_fflush)(FILE *fd) { if (fflush(fd) == EOF) { perror("fflush"); exit(EXIT_FAILURE); } } void (x_fputc)(int ch, FILE *fd) { if (g_ignore_text && ch != '\n') { return; } if (fputc(ch, fd) == EOF) { perror("x_fputc"); exit(EXIT_FAILURE); } } /* * If some data is modified unexpectedly by someone unknown, just * do * ptr = debug_malloc_pages(bufsiz); * memcpy(ptr, buf, bufsiz); * mprotect(ptr, bufsiz, PROT_READ); * buf = ptr; * ... then a bus error or segfault will be caused when someone * attempts to change buf, and gdb gives us a backtrace */ void * debug_malloc_pages(size_t nbytes) { long psize = sysconf(_SC_PAGESIZE); size_t npages = nbytes / psize; void *ret; if (npages * psize < nbytes) { ++npages; } #ifdef MAP_ANON ret = mmap(0, npages * psize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); if (ret == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); } #else puts("debug_malloc_pages() does not work on this system"); exit(1); #endif return ret; } void (dounimpl)(const char *f, int line) { printf("In function `%s', line %d:\n", f, line); puts("WHOOPS! This code path should not have been executed"); puts("because it is unimplemented. Sorry about that. Rest"); puts("assured that I'll work on this soon."); puts(""); puts("How about a game of snake in the meantime? [y/n] "); if (isatty(1) && tolower(getchar()) == 'y') { struct stat sbuf; char *path; if (stat("/usr/local/nwcc/bin/snake", &sbuf) == 0) { path = "/usr/local/nwcc/bin/snake"; } else { path = "./snake"; } (void) execl(path, "snake", (char *)NULL); perror(path); exit(EXIT_FAILURE); } puts("Calling abort() ..."); abort(); } /* XXX remove */ void make_room(char **p, size_t *size, size_t nbytes) { if (nbytes > *size) { if (*size == 0) *size = 128; else *size *= 2; *p = n_xrealloc(*p, *size); } } int n_optind; char *n_optarg; #define NEXT_ARG() do { \ if (++curind >= argc || \ (argv[curind][0] == '-' \ && argv[curind][1] != 0)) { \ (void) fprintf(stderr, "No argument given for " \ "option `%s'\n", argv[curind-1]); \ return '?'; \ } \ n_optind = curind; \ } while (0) /* * This mostly resembles getopt() but doesn't stop processing when there is a * non-option argument, as BSD implementations do, and supports long arguments. * * XXX this should probably mess with argv to make n_optind meaningful when * normal arguments are mixed with options, as does GNU getopt() */ int nw_get_arg(int argc, char **argv, struct ga_option *options,int nopts,int*idx) { static int curind; char *p; char *p2 = NULL; int i; *idx = 0; if (n_optind == 0) { n_optind = 1; /* XXX ?!?! */ } #if 0 do { if (++curind >= argc) { return -1; } p = argv[curind]; } while (*p != '-'); #endif if (++curind >= argc) { return -1; } p = argv[curind]; if (*p != '-') { n_optarg = argv[curind]; n_optind = curind; return '!'; } n_optind = curind; if (strcmp(p+1, "-") == 0) { /* Done */ curind = argc; return -1; } else if (p[1] == 0) { return '-'; } else if (p[2] == 0) { /* Single char option */ ++p; do_single: for (i = 0; i < nopts; ++i) { if (options[i].ch == *p) { if (!options[i].takesarg && p[1] != 0) { if (*p == 'W' || *p == 'f' || *p == 'm') { /* XXX gcc compatibility... */ return '!'; } (void) fprintf(stderr, "Option %c does not take an argument\n", *p); return '?'; } break; } } } else { int is_long = 0; if (*++p == '-') { /* --long-option */ ++p; is_long = 1; } p2 = strchr(p, '='); for (i = 0; i < nopts; ++i) { if (options[i].name != NULL) { if (p2 != NULL) { /* -opt=val */ if (strncmp(p, options[i].name, p2 - p) == 0) { break; } } else { if (strcmp(p, options[i].name) == 0) { break; } } } } if (i == nopts && !is_long) { /* * This may be a single-char option plus argument, as * in; * -lfoo equals -l foo */ goto do_single; } } if (i == nopts) { (void) fprintf(stderr, "Unknown option -- %s\n", argv[curind]); return '?'; } if (options[i].takesarg) { if (options[i].name != NULL) { if (p2 == NULL) { /* Argument must be next string */ NEXT_ARG(); n_optarg = argv[curind]; } else { n_optarg = p2+1; } } else { if (p[1] != 0) { n_optarg = p+1; } else { NEXT_ARG(); n_optarg = argv[curind]; } } } else { n_optind = curind+1; } *idx = i; /* XXXX !!!!!!!!!!!!!!!!!!!! should set to -1 :-( */ if (options[i].name == NULL) { return options[i].ch; } else { return '?'; } } /* * gdb on IRIX usually doesn't seem to work with abort()-generated * core dumps :-( I do not know why :-( * :-( :-( */ void irix_abort(void) { fflush(NULL); *(char *)0 = 0; } static int search_dir(const char *path, const char *name) { DIR *d; struct dirent *dir; if ((d = opendir(path)) == NULL) { perror(path); return -1; } while ((dir = readdir(d)) != NULL) { if (strcmp(dir->d_name, name) == 0) { /* Found command */ closedir(d); return 0; } } closedir(d); return -1; } int find_cmd(const char *name, char *output, size_t outsiz) { char *path; char *saved_path; char *p; if ((path = getenv("PATH")) == NULL) { (void) fprintf(stderr, "find_cmd: No PATH environment variable set!\n"); return -1; } saved_path = path = n_xstrdup(path); for (;;) { p = strchr(path, ':'); if (p != NULL) *p = 0; if (search_dir(path, name) == 0) { /* Found */ if (output != NULL) { if ((strlen(path) + strlen(name) + 2) > outsiz) { (void) fprintf(stderr, "find_cmd: Path too long for buffer\n"); free(saved_path); return -1; } else { sprintf(output, "%s/%s", path, name); } } free(saved_path); return 0; } if (p == NULL) break; else path = p + 1; } free(saved_path); return -1; } FILE * get_tmp_file(char *temp, char *output, char *postfix) { FILE *ret; int fd; size_t i; i = 0; do { sprintf(output, "%s%lu.%s", temp, (unsigned long)i++, postfix); fd = open(output, O_CREAT | O_EXCL | O_RDWR, S_IRWXU); } while (fd == -1 && errno == EEXIST); if (fd == -1) { perror(output); return NULL; } if ((ret = fdopen(fd, "w+")) == NULL) { perror("fdopen"); close(fd); remove(output); return NULL; } return ret; } #ifdef TEST_GET_ARG int main(int argc, char **argv) { int ch; int idx; struct ga_option opts[] = { { 'x', NULL, 0 }, { 'y', NULL, 1 }, { 0, "zm", 1 }, { 0, "zorg", 0 }, { 0, "hehe", 1 } }; int nopts = sizeof opts / sizeof opts[0]; while ((ch = nw_get_arg(argc, argv, opts, nopts, &idx)) != -1) { switch (ch) { case 'x': printf("read x!\n"); break; case 'y': printf("read y! arg=%s\n", n_optarg); break; case '!': printf("read normal argument `%s'\n", n_optarg); break; case '?': if (idx) { printf("read option %s!\n", opts[idx].name); if (opts[idx].takesarg) { printf("arg=%s\n", n_optarg); } } else { puts("hm"); } } } argc -= n_optind; argv += n_optind; while (argc--) { printf("%s\n", *argv++); } return 0; } #endif