/* * abcm2ps: a program to typeset tunes written in ABC format * using PostScript or SVG * * Copyright (C) 1998-2019 Jean-François Moine (http://moinejf.free.fr) * * Adapted from abc2ps * Copyright (C) 1996-1998 Michael Methfessel (https://github.com/methf/abc2ps/) * * abcm2ps is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include "abcm2ps.h" #ifdef HAVE_MMAP #include #include #elif defined(linux) #include #endif /* -- global variables -- */ INFO info; struct SYMBOL *sym; /* (points to the symbols of the current voice) */ int tunenum; /* number of current tune */ int pagenum = 1; /* current page in output file */ int pagenum_nr = 1; /* current page (non-resettable) */ /* switches modified by command line flags: */ int quiet; /* quiet mode */ int secure; /* secure mode */ int annotate; /* output source references */ int pagenumbers; /* write page numbers */ int epsf; /* 1: EPSF, 2: SVG, 3: embedded ABC */ int svg; /* 1: SVG, 2: XHTML */ int showerror; /* show the errors */ int pipeformat = 0; /* format for bagpipes regardless of key */ char outfn[FILENAME_MAX]; /* output file name */ int file_initialized; /* for output file */ FILE *fout; /* output file */ char *in_fname; /* top level input file name */ time_t mtime; /* last modification time of the input file */ static time_t fmtime; /* " " of all files */ int s_argc; /* command line arguments */ char **s_argv; struct tblt_s *tblts[MAXTBLT]; struct cmdtblt_s cmdtblts[MAXCMDTBLT]; int ncmdtblt; /* -- local variables -- */ static char *styd = DEFAULT_FDIR; /* format search directory */ static int def_fmt_done = 0; /* default format read */ static struct SYMBOL notitle; /* memory arena (for clrarena, lvlarena & getarena) */ #define MAXAREAL 3 /* max area levels: * 0; global, 1: tune, 2: generation */ #define AREANASZ 0x4000 /* standard allocation size */ #define MAXAREANASZ 0x20000 /* biggest allocation size */ static int str_level; /* current arena level */ static struct str_a { struct str_a *n; /* next area */ char *p; /* pointer in area */ int r; /* remaining space in area */ int sz; /* size of str[] */ char str[2]; /* start of memory area */ } *str_r[MAXAREAL], *str_c[MAXAREAL]; /* root and current area pointers */ /* -- local functions -- */ static void read_def_format(void); static FILE *open_ext(char *fn, char *ext) { FILE *fp; char *p; if ((fp = fopen(fn, "rb")) != NULL) return fp; if ((p = strrchr(fn, DIRSEP)) == NULL) p = fn; if (strrchr(p, '.') != NULL) return NULL; strcat(p, "."); strcat(p, ext); if ((fp = fopen(fn, "rb")) != NULL) return fp; return NULL; } /* -- open a file for reading -- */ FILE *open_file(char *fn, /* file name */ char *ext, /* file type */ char *rfn) /* returned real file name */ { FILE *fp; char *p; int l; /* if there was some ABC file, try its directory */ if (in_fname && in_fname != fn && (p = strrchr(in_fname, DIRSEP)) != NULL) { l = p - in_fname + 1; strncpy(rfn, in_fname, l); strcpy(&rfn[l], fn); if ((fp = open_ext(rfn, ext)) != NULL) return fp; } /* try locally */ strcpy(rfn, fn); if ((fp = open_ext(rfn, ext)) != NULL) return fp; /* try a format in the format directory */ if (*ext != 'f' || *styd == '\0') return NULL; l = strlen(styd) - 1; if (styd[l] == DIRSEP) sprintf(rfn, "%s%s", styd, fn); else sprintf(rfn, "%s%c%s", styd, DIRSEP, fn); return open_ext(rfn, ext); } /* -- read a whole input file -- */ /* return the real/full file name in tex_buf[] */ static char *read_file(char *fn, char *ext) { size_t fsize; FILE *fin; char *file; if (*fn == '\0') { strcpy(tex_buf, "stdin"); fsize = 0; file = malloc(8192); for (;;) { int l; l = fread(&file[fsize], 1, 8192, stdin); fsize += l; if (l != 8192) break; file = realloc(file, fsize + 8192); } if (ferror(stdin) != 0) { free(file); return 0; } if (fsize % 8192 == 0) file = realloc(file, fsize + 2); time(&fmtime); } else { struct stat sbuf; fin = open_file(fn, ext, tex_buf); if (!fin) return NULL; if (fseek(fin, 0L, SEEK_END) < 0) { fclose(fin); return NULL; } fsize = ftell(fin); rewind(fin); if ((file = malloc(fsize + 2)) == NULL) { fclose(fin); return NULL; } if (fread(file, 1, fsize, fin) != fsize) { fclose(fin); free(file); return NULL; } fstat(fileno(fin), &sbuf); memcpy(&fmtime, &sbuf.st_mtime, sizeof fmtime); fclose(fin); } file[fsize] = '\0'; return file; } /* -- treat an input file and generate the ABC file -- */ static void treat_file(char *fn, char *ext) { char *file; char *abc_fn; int file_type, l; /* initialize if not already done */ if (!fout) { read_def_format(); if (strcmp(fn, tex_buf) == 0) return; // if xx.default.fmt, done } /* read the file into memory */ /* the real/full file name is put in tex_buf[] */ if ((file = read_file(fn, ext)) == NULL) { if (strcmp(fn, "default.fmt") != 0) { error(1, NULL, "Cannot read the input file '%s'", fn); #if defined(unix) || defined(__unix__) perror(" read_file"); #endif } return; } abc_fn = strdup(tex_buf); if (!quiet) fprintf(strcmp(outfn, "-") == 0 ? stderr : stdout, "File %s\n", abc_fn); /* convert the strings */ l = strlen(abc_fn); if (strcmp(&abc_fn[l - 3], ".ps") == 0) { file_type = FE_PS; } else if (strcmp(&abc_fn[l - 4], ".fmt") == 0) { file_type = FE_FMT; } else { file_type = FE_ABC; // in_fname = abc_fn; mtime = fmtime; } frontend((unsigned char *) file, file_type, abc_fn, 0); free(file); if (file_type == FE_PS) /* PostScript file */ frontend((unsigned char *) "%%endps", FE_ABC, abc_fn, 0); if (file_type == FE_ABC) /* if ABC file */ clrarena(1); /* clear previous tunes */ } /* call back to handle %%format/%%abc-include - see front.c */ void include_file(unsigned char *fn) { static int nbfiles; if (nbfiles > 2) { error(1, NULL, "Too many included files"); return; } nbfiles++; treat_file((char *) fn, "fmt"); nbfiles--; } /* -- treat an ABC input file and generate the music -- */ /* this function also treats ABC in XHTML */ static void treat_abc_file(char *fn) { FILE *fin; char *file, *file_tmp; char *abc_fn, *p, *q; size_t fsize, l, l2; int linenum; #ifdef HAVE_MMAP int fd; #endif lvlarena(0); parse.abc_state = ABC_S_GLOBAL; if (epsf != 3) { treat_file(fn, "abc"); /* not '-z' */ return; } if (*fn == '\0') { error(1, NULL, "cannot use stdin with -z - aborting"); exit(EXIT_FAILURE); } fin = open_file(fn, "abc", tex_buf); if (!fin) goto err; if (fseek(fin, 0L, SEEK_END) < 0) { fclose(fin); goto err; } fsize = ftell(fin); rewind(fin); #ifdef HAVE_MMAP fd = fileno(fin); file = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0); if (!file) goto err; #else file = malloc(fsize); if (!file) goto err; if (fread(file, 1, fsize, fin) != fsize) { free(file); goto err; } fclose(fin); #endif /* copy the HTML/XML/XHTML file and generate the music */ abc_fn = strdup(tex_buf); l = fsize; p = file; linenum = 0; while (l > 0) { /* search the start of ABC lines */ #if 1 for (q = p, l2 = l - 10; l2 > 0; l2--, q++) { if (strncmp(q, "\n%abc", 5) == 0 // || strncmp(q, "\n%%", 3) == 0 || strncmp(q, "\nX:", 3) == 0) break; } #else for (q = p, l2 = l - 5; l2 > 0; l2--, q++) if (strncmp(q, "", 5) == 0) break; #endif if (l2 <= 0) { fwrite(p, 1, l, fout); break; } q++; fwrite(p, 1, q - p, fout); l -= q - p; while (p != q) { if (*p++ == '\n') linenum++; } /* search the end of ABC lines */ for (q = p, l2 = l - 10; l2 > 0; l2--, q++) if (*q == '\n' && q[1] == '<') break; if (l2 <= 0) { error(1, NULL, "no end of ABC sequence"); q += 9; // break; } q++; /* must copy ... :( */ l2 = q - p; file_tmp = malloc(l2 + 1); if (!file_tmp) { error(1, NULL, "out of memory"); break; } memcpy(file_tmp, p, l2); file_tmp[l2] = '\0'; frontend((unsigned char *) file_tmp, FE_ABC, abc_fn, linenum); free(file_tmp); clrarena(1); /* clear previous tunes */ file_initialized = -1; /* don't put
before first image */ l -= q - p; while (p != q) { if (*p++ == '\n') linenum++; } } #ifdef HAVE_MMAP munmap(file, fsize); fclose(fin); #else free(file); #endif return; err: error(1, NULL, "input file %s error %s - aborting", fn, strerror(errno)); exit(EXIT_FAILURE); } /* -- read the default format -- */ static void read_def_format(void) { if (def_fmt_done) return; def_fmt_done = 1; treat_file("default.fmt", "fmt"); } /* -- set extension on a file name -- */ void strext(char *fn, char *ext) { char *p, *q; if ((p = strrchr(fn, DIRSEP)) == NULL) p = fn; if ((q = strrchr(p, '.')) == NULL) strcat(p, "."); else q[1] = '\0'; strcat(p, ext); } /* -- write the program version -- */ static void display_version(int full) { FILE *log = strcmp(outfn, "-") == 0 ? stderr : stdout; fputs("abcm2ps-" VERSION " (" VDATE ")\n", log); if (!full) return; fputs("Options:" #ifdef A4_FORMAT " A4_FORMAT" #endif #ifdef DECO_IS_ROLL " DECO_IS_ROLL" #endif #ifdef HAVE_PANGO " PANGO" #endif #if !defined(A4_FORMAT) && !defined(DECO_IS_ROLL) && !defined(HAVE_PANGO) " NONE" #endif "\n", log); if (styd[0] != '\0') fprintf(log, "Default format directory: %s\n", styd); } /* -- display usage and exit -- */ static void usage(void) { display_version(0); printf( "ABC to Postscript/SVG translator.\n" "Usage: abcm2ps [options] file [file_options] ..\n" "where:\n" " file input ABC file, or '-'\n" " options and file_options:\n" " .output file options:\n" " -E produce EPSF output, one tune per file\n" " -g produce SVG output, one tune per file\n" " -v produce SVG output, one page per file\n" " -X produce SVG output in one XHTML file\n" " -z produce SVG output from embedded ABC\n" " -O fff set outfile name to fff\n" " -O = make outfile name from infile/title\n" " -i indicate where are the errors\n" " -k kk size of the PS output buffer in Kibytes\n" " .output formatting:\n" " -s xx set scale factor to xx\n" " -w xx set staff width (cm/in/pt)\n" " -m xx set left margin (cm/in/pt)\n" " -d xx set staff separation (cm/in/pt)\n" " -a xx set max shrinkage to xx (between 0 and 1)\n" " -F foo read format file \"foo.fmt\"\n" " -D bar look for format files in directory \"bar\"\n" " -p format for bagpipes regardless of key\n" " .output options:\n" " -l landscape mode\n" " -I xx indent 1st line (cm/in/pt)\n" " -x add xref numbers in titles\n" " -M don't output the lyrics\n" " -N n set page numbering mode to n=\n" " 0=off 1=left 2=right 3=even left,odd right 4=even right,odd left\n" " -1 write one tune per page\n" " -G no slur in grace notes\n" " -j n[b] number the measures every n bars (or on the left if n=0)\n" " if 'b', display in a box\n" " -b n set the first measure number to n\n" " -f have flat beams\n" " -T n[v] output the tablature 'n' for voice 'v' / all voices\n" " .line breaks:\n" " -c auto line break\n" " -B n break every n bars\n" " .input file selection/options:\n" " -e pattern\n" " tune selection\n" " .help/configuration:\n" " -V show program version\n" " -h show this command summary\n" " -H show the format parameters\n" " -S secure mode\n" " -q quiet mode\n"); exit(EXIT_SUCCESS); } #ifdef linux /* -- where is the default format directory -- */ static void wherefmtdir(void) { char exe[512], *p; FILE *f; int l; if ((l = readlink("/proc/self/exe", exe, sizeof exe)) <= 0) return; if ((p = strrchr(exe, '/')) == NULL) return; p++; if (p > &exe[5] && strncmp(p - 5, "/bin", 4) == 0) { strcpy(p - 4, "share/abcm2ps/"); p += -4 + 14; } /* else, assume this is the source directory */ /* check if a format file is present */ strcpy(p, "tight.fmt"); if ((f = fopen(exe, "r")) == NULL) return; fclose(f); /* change the format directory */ p[-1] = '\0'; styd = strdup(exe); } #endif /* -- parse the tablature command ('-T n[v]') -- */ static struct cmdtblt_s *cmdtblt_parse(char *p) { struct cmdtblt_s *cmdtblt; short val; if (ncmdtblt >= MAXCMDTBLT) { error(1, NULL, "++++ Too many '-T'"); return NULL; } if (*p == '\0') val = -1; else { val = *p++ - '0' - 1; if ((unsigned) val > MAXTBLT) { error(1, NULL, "++++ Bad tablature number in '-T'\n"); return 0; } } cmdtblt = &cmdtblts[ncmdtblt++]; cmdtblt->index = val; cmdtblt->vn = p; return cmdtblt; } /* set a command line option */ static void set_opt(char *w, char *v) { static char prefix = '%'; /* pseudo-comment prefix */ if (!v) v = ""; if (strlen(w) + strlen(v) >= TEX_BUF_SZ - 10) { error(1, NULL, "Command line '%s' option too long", w); return; } sprintf(tex_buf, /* this buffer is available */ "%%%c%s %s lock\n", prefix, w, v); if (strcmp(w, "abcm2ps") == 0) prefix = *v; frontend((unsigned char *) tex_buf, FE_ABC, "cmd_line", 0); } /* -- main program -- */ int main(int argc, char **argv) { unsigned j; char *p, c, *aaa; if (argc <= 1) usage(); outfn[0] = '\0'; init_outbuf(64); /* set the global flags */ s_argc = argc; s_argv = argv; aaa = NULL; while (--argc > 0) { argv++; p = *argv; if (*p != '-' || p[1] == '-') { if (*p == '+' && p[1] == 'F') /* +F : no default format */ def_fmt_done = 1; continue; } while ((c = *++p) != '\0') { /* '-xxx' */ switch (c) { case 'E': svg = 0; /* EPS */ epsf = 1; break; case 'g': svg = 0; /* SVG one file per tune */ epsf = 2; break; case 'H': quiet = 1; // don't output the version break; case 'h': usage(); /* no return */ case 'p': pipeformat = 1; /* format for bagpipe regardless of key */ break; case 'q': quiet = 1; break; case 'S': secure = 1; break; case 'V': display_version(1); return EXIT_SUCCESS; case 'v': svg = 1; /* SVG one file per page */ epsf = 0; break; case 'X': svg = 2; /* SVG/XHTML */ epsf = 0; break; case 'k': { int kbsz; if (p[1] == '\0') { if (--argc <= 0) { error(1, NULL, "No value for '-k' - aborting"); return EXIT_FAILURE; } aaa = *++argv; } else { aaa = p + 1; p += strlen(p) - 1; } sscanf(aaa, "%d", &kbsz); init_outbuf(kbsz); break; } case 'O': if (p[1] == '\0') { if (--argc <= 0) { error(1, NULL, "No value for '-O' - aborting"); return EXIT_FAILURE; } aaa = *++argv; } else { aaa = p + 1; p += strlen(p) - 1; } if (strlen(aaa) >= sizeof outfn) { error(1, NULL, "'-O' too large - aborting"); return EXIT_FAILURE; } strcpy(outfn, aaa); break; case 'z': epsf = 3; /* ABC embedded in XML */ svg = 0; break; default: if (strchr("aBbDdeFfIjmNOsTw", c)) /* if with arg */ p += strlen(p) - 1; /* skip */ break; } } } if (!quiet) display_version(0); /* initialize */ clrarena(0); /* global */ clrarena(1); /* tunes */ clrarena(2); /* generation */ // memset(&info, 0, sizeof info); info['T' - 'A'] = ¬itle; notitle.text = "T:"; set_format(); init_deco(); #ifdef linux /* if not set, try to find where is the default format directory */ if (styd[0] == '\0') wherefmtdir(); #endif #ifdef HAVE_PANGO pg_init(); #endif /* if ABC embedded in XML, open the output file */ if (epsf == 3) { open_fout(); read_def_format(); } /* parse the arguments - finding a new file, treat the previous one */ argc = s_argc; argv = s_argv; while (--argc > 0) { argv++; p = *argv; if ((c = *p) == '\0') continue; if (c == '-') { int i; if (p[1] == '\0') { /* '-' alone */ if (in_fname) { treat_abc_file(in_fname); frontend((unsigned char *) "select\n", FE_FMT, "cmd_line", 0); } in_fname = ""; /* read from stdin */ continue; } i = strlen(p) - 1; if (p[i] == '-' && p[1] != '-' //fixme: 'e' may be preceded by other options && p[1] != 'e' && p[i -1] != 'O') c = '+'; /* switch off flags with '-x-' */ } if (c == '+') { /* switch off flags with '+' */ while (*++p != '\0') { switch (*p) { case '-': break; case 'B': cfmt.barsperstaff = 0; lock_fmt(&cfmt.barsperstaff); break; case 'c': cfmt.continueall = 0; lock_fmt(&cfmt.continueall); break; case 'F': // def_fmt_done = 1; break; case 'G': cfmt.graceslurs = 1; lock_fmt(&cfmt.graceslurs); break; case 'i': showerror = 0; break; case 'j': cfmt.measurenb = -1; lock_fmt(&cfmt.measurenb); break; case 'l': cfmt.landscape = 0; lock_fmt(&cfmt.landscape); break; case 'M': cfmt.fields[1] = 1 << ('w' - 'a'); lock_fmt(&cfmt.fields); break; case 'N': pagenumbers = 0; break; case 'O': outfn[0] = '\0'; break; case 'T': { struct cmdtblt_s *cmdtblt; aaa = p + 1; if (*aaa == '\0') { if (argc > 1 && argv[1][0] != '-') { aaa = *++argv; argc--; } } else { while (p[1] != '\0') /* stop */ p++; if (*p == '-') *p-- = '\0'; /* (not clean) */ } cmdtblt = cmdtblt_parse(aaa); if (cmdtblt != 0) cmdtblt->active = 0; break; } case 'x': cfmt.fields[0] &= ~(1 << ('X' - 'A')); lock_fmt(&cfmt.fields); break; case '0': cfmt.splittune = 0; lock_fmt(&cfmt.splittune); break; case '1': cfmt.oneperpage = 0; lock_fmt(&cfmt.oneperpage); break; default: error(1, NULL, "++++ Cannot switch off flag: +%c", *p); break; } } continue; } if (c == '-') { /* interpret a flag with '-' */ if (p[1] == '-') { /* long argument */ p += 2; if (--argc <= 0) { error(1, NULL, "No argument for '--'"); return EXIT_FAILURE; } argv++; set_opt(p, *argv); continue; } while ((c = *++p) != '\0') { switch (c) { /* simple flags */ case 'A': annotate = 1; break; case 'c': cfmt.continueall = 1; lock_fmt(&cfmt.continueall); break; case 'E': break; case 'f': cfmt.flatbeams = 1; lock_fmt(&cfmt.flatbeams); break; case 'G': cfmt.graceslurs = 0; lock_fmt(&cfmt.graceslurs); break; case 'g': break; case 'H': if (!fout) { read_def_format(); make_font_list(); do_tune(); } print_format(); return EXIT_SUCCESS; case 'i': showerror = 1; break; case 'l': cfmt.landscape = 1; lock_fmt(&cfmt.landscape); break; case 'M': cfmt.fields[1] &= ~(1 << ('w' - 'a')); lock_fmt(&cfmt.fields); break; case 'p': case 'q': case 'S': break; case 'v': case 'X': case 'z': break; case 'x': cfmt.fields[0] |= 1 << ('X' - 'A'); lock_fmt(&cfmt.fields); break; case '0': cfmt.splittune = 1; lock_fmt(&cfmt.splittune); break; case '1': cfmt.oneperpage = 1; lock_fmt(&cfmt.oneperpage); break; /* flag with optional parameter */ case 'N': if (p[1] == '\0' && (argc <= 1 || !isdigit((unsigned) argv[1][0]))) { pagenumbers = 2; /* old behaviour */ break; } /* fall thru */ /* flags with parameter.. */ case 'a': case 'B': case 'b': case 'D': case 'd': case 'e': case 'F': case 'I': case 'j': case 'k': case 'L': case 'm': case 'O': case 's': case 'T': case 'w': aaa = p + 1; if (*aaa == '\0') { aaa = *++argv; if (--argc <= 0 || (*aaa == '-' && c != 'O')) { error(1, NULL, "Missing parameter after '-%c' - aborting", c); return EXIT_FAILURE; } } else { p += strlen(p) - 1; /* stop */ } if (strchr("BbfjkNs", c)) { /* check num args */ for (j = 0; j < strlen(aaa); j++) { if (!strchr("0123456789.", aaa[j])) { if (aaa[j] == 'b' && aaa[j + 1] == '\0' && c == 'j') break; error(1, NULL, "Invalid parameter <%s> for flag -%c", aaa, c); return EXIT_FAILURE; } } } switch (c) { case 'a': set_opt("maxshrink", aaa); break; case 'B': set_opt("barsperstaff", aaa); break; case 'b': set_opt("measurefirst", aaa); break; case 'D': styd = aaa; break; case 'd': set_opt("staffsep", aaa); break; case 'e': set_opt("select", aaa); break; case 'F': treat_file(aaa, "fmt"); break; case 'I': set_opt("indent", aaa); break; case 'j': sscanf(aaa, "%d", &cfmt.measurenb); lock_fmt(&cfmt.measurenb); if (aaa[strlen(aaa) - 1] == 'b') cfmt.measurebox = 1; else cfmt.measurebox = 0; lock_fmt(&cfmt.measurebox); break; case 'k': break; case 'm': set_opt("leftmargin", aaa); break; case 'N': sscanf(aaa, "%d", &pagenumbers); if ((unsigned) pagenumbers > 4) { error(1, NULL, "'-N' value %s - changed to 2", aaa); pagenumbers = 2; } break; case 'O': if (strlen(aaa) >= sizeof outfn) { error(1, NULL, "'-O' too large - aborting"); exit(EXIT_FAILURE); } strcpy(outfn, aaa); break; case 's': set_opt("scale", aaa); break; case 'T': { struct cmdtblt_s *cmdtblt; cmdtblt = cmdtblt_parse(aaa); if (cmdtblt) cmdtblt->active = 1; break; } case 'w': set_opt("staffwidth", aaa); break; } break; default: error(1, NULL, "Unknown flag: -%c ignored", c); break; } } continue; } if (in_fname) { treat_abc_file(in_fname); frontend((unsigned char *) "select\n", FE_FMT, "cmd_line", 0); } in_fname = p; } if (in_fname) treat_abc_file(in_fname); if (multicol_start != 0) { /* lack of %%multicol end */ error(1, NULL, "Lack of %%%%multicol end"); multicol_start = 0; buffer_eob(0); if (!info['X' - 'A'] && !epsf) write_buffer(); } if (!epsf && !fout) { error(1, NULL, "Nothing to generate!"); return EXIT_FAILURE; } close_output_file(); return severity == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } /* -- arena routines -- */ void clrarena(int level) { struct str_a *a_p; if ((a_p = str_r[level]) == NULL) { str_r[level] = a_p = malloc(sizeof *str_r[0] + AREANASZ - 2); a_p->sz = AREANASZ; a_p->n = NULL; } str_c[level] = a_p; a_p->p = a_p->str; a_p->r = a_p->sz; } int lvlarena(int level) { int old_level; old_level = str_level; str_level = level; return old_level; } /* The area is 8 bytes aligned to handle correctly int and pointers access * on some machines as Sun Sparc. */ void *getarena(int len) { char *p; struct str_a *a_p; a_p = str_c[str_level]; len = (len + 7) & ~7; /* align at 64 bits boundary */ if (len > a_p->r) { if (len > MAXAREANASZ) { error(1, NULL, "getarena - data too wide %d - aborting", len); exit(EXIT_FAILURE); } if (len > AREANASZ) { /* big allocation */ struct str_a *a_n; a_n = a_p->n; a_p->n = malloc(sizeof *str_r[0] + len - 2); a_p->n->n = a_n; a_p->n->sz = len; } else if (a_p->n == 0) { /* standard allocation */ a_p->n = malloc(sizeof *str_r[0] + AREANASZ - 2); a_p->n->n = NULL; a_p->n->sz = AREANASZ; } str_c[str_level] = a_p = a_p->n; a_p->p = a_p->str; a_p->r = a_p->sz; } p = a_p->p; a_p->p += len; a_p->r -= len; return p; }