1*260e9a87SYuri Pankov /* $Id: mdoc_argv.c,v 1.100 2015/02/04 18:59:45 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 395c635efSGarrett D'Amore * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*260e9a87SYuri Pankov * Copyright (c) 2012, 2014 Ingo Schwarze <schwarze@openbsd.org> 595c635efSGarrett D'Amore * 695c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 795c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 895c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 995c635efSGarrett D'Amore * 1095c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1295c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1395c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1495c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1595c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1695c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1795c635efSGarrett D'Amore */ 1895c635efSGarrett D'Amore #include "config.h" 1995c635efSGarrett D'Amore 2095c635efSGarrett D'Amore #include <sys/types.h> 2195c635efSGarrett D'Amore 2295c635efSGarrett D'Amore #include <assert.h> 2395c635efSGarrett D'Amore #include <stdlib.h> 2495c635efSGarrett D'Amore #include <stdio.h> 2595c635efSGarrett D'Amore #include <string.h> 2695c635efSGarrett D'Amore 2795c635efSGarrett D'Amore #include "mdoc.h" 2895c635efSGarrett D'Amore #include "mandoc.h" 29*260e9a87SYuri Pankov #include "mandoc_aux.h" 3095c635efSGarrett D'Amore #include "libmdoc.h" 3195c635efSGarrett D'Amore #include "libmandoc.h" 3295c635efSGarrett D'Amore 3395c635efSGarrett D'Amore #define MULTI_STEP 5 /* pre-allocate argument values */ 3495c635efSGarrett D'Amore #define DELIMSZ 6 /* max possible size of a delimiter */ 3595c635efSGarrett D'Amore 3695c635efSGarrett D'Amore enum argsflag { 3795c635efSGarrett D'Amore ARGSFL_NONE = 0, 3895c635efSGarrett D'Amore ARGSFL_DELIM, /* handle delimiters of [[::delim::][ ]+]+ */ 3995c635efSGarrett D'Amore ARGSFL_TABSEP /* handle tab/`Ta' separated phrases */ 4095c635efSGarrett D'Amore }; 4195c635efSGarrett D'Amore 4295c635efSGarrett D'Amore enum argvflag { 4395c635efSGarrett D'Amore ARGV_NONE, /* no args to flag (e.g., -split) */ 4495c635efSGarrett D'Amore ARGV_SINGLE, /* one arg to flag (e.g., -file xxx) */ 45698f87a4SGarrett D'Amore ARGV_MULTI /* multiple args (e.g., -column xxx yyy) */ 4695c635efSGarrett D'Amore }; 4795c635efSGarrett D'Amore 4895c635efSGarrett D'Amore struct mdocarg { 4995c635efSGarrett D'Amore enum argsflag flags; 5095c635efSGarrett D'Amore const enum mdocargt *argvs; 5195c635efSGarrett D'Amore }; 5295c635efSGarrett D'Amore 5395c635efSGarrett D'Amore static void argn_free(struct mdoc_arg *, int); 5495c635efSGarrett D'Amore static enum margserr args(struct mdoc *, int, int *, 5595c635efSGarrett D'Amore char *, enum argsflag, char **); 5695c635efSGarrett D'Amore static int args_checkpunct(const char *, int); 57*260e9a87SYuri Pankov static void argv_multi(struct mdoc *, int, 5895c635efSGarrett D'Amore struct mdoc_argv *, int *, char *); 59*260e9a87SYuri Pankov static void argv_single(struct mdoc *, int, 6095c635efSGarrett D'Amore struct mdoc_argv *, int *, char *); 6195c635efSGarrett D'Amore 6295c635efSGarrett D'Amore static const enum argvflag argvflags[MDOC_ARG_MAX] = { 6395c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Split */ 6495c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Nosplit */ 6595c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Ragged */ 6695c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Unfilled */ 6795c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Literal */ 6895c635efSGarrett D'Amore ARGV_SINGLE, /* MDOC_File */ 69698f87a4SGarrett D'Amore ARGV_SINGLE, /* MDOC_Offset */ 7095c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Bullet */ 7195c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Dash */ 7295c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Hyphen */ 7395c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Item */ 7495c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Enum */ 7595c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Tag */ 7695c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Diag */ 7795c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Hang */ 7895c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Ohang */ 7995c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Inset */ 8095c635efSGarrett D'Amore ARGV_MULTI, /* MDOC_Column */ 81698f87a4SGarrett D'Amore ARGV_SINGLE, /* MDOC_Width */ 8295c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Compact */ 8395c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Std */ 8495c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Filled */ 8595c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Words */ 8695c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Emphasis */ 8795c635efSGarrett D'Amore ARGV_NONE, /* MDOC_Symbolic */ 8895c635efSGarrett D'Amore ARGV_NONE /* MDOC_Symbolic */ 8995c635efSGarrett D'Amore }; 9095c635efSGarrett D'Amore 9195c635efSGarrett D'Amore static const enum mdocargt args_Ex[] = { 9295c635efSGarrett D'Amore MDOC_Std, 9395c635efSGarrett D'Amore MDOC_ARG_MAX 9495c635efSGarrett D'Amore }; 9595c635efSGarrett D'Amore 9695c635efSGarrett D'Amore static const enum mdocargt args_An[] = { 9795c635efSGarrett D'Amore MDOC_Split, 9895c635efSGarrett D'Amore MDOC_Nosplit, 9995c635efSGarrett D'Amore MDOC_ARG_MAX 10095c635efSGarrett D'Amore }; 10195c635efSGarrett D'Amore 10295c635efSGarrett D'Amore static const enum mdocargt args_Bd[] = { 10395c635efSGarrett D'Amore MDOC_Ragged, 10495c635efSGarrett D'Amore MDOC_Unfilled, 10595c635efSGarrett D'Amore MDOC_Filled, 10695c635efSGarrett D'Amore MDOC_Literal, 10795c635efSGarrett D'Amore MDOC_File, 10895c635efSGarrett D'Amore MDOC_Offset, 10995c635efSGarrett D'Amore MDOC_Compact, 11095c635efSGarrett D'Amore MDOC_Centred, 11195c635efSGarrett D'Amore MDOC_ARG_MAX 11295c635efSGarrett D'Amore }; 11395c635efSGarrett D'Amore 11495c635efSGarrett D'Amore static const enum mdocargt args_Bf[] = { 11595c635efSGarrett D'Amore MDOC_Emphasis, 11695c635efSGarrett D'Amore MDOC_Literal, 11795c635efSGarrett D'Amore MDOC_Symbolic, 11895c635efSGarrett D'Amore MDOC_ARG_MAX 11995c635efSGarrett D'Amore }; 12095c635efSGarrett D'Amore 12195c635efSGarrett D'Amore static const enum mdocargt args_Bk[] = { 12295c635efSGarrett D'Amore MDOC_Words, 12395c635efSGarrett D'Amore MDOC_ARG_MAX 12495c635efSGarrett D'Amore }; 12595c635efSGarrett D'Amore 12695c635efSGarrett D'Amore static const enum mdocargt args_Bl[] = { 12795c635efSGarrett D'Amore MDOC_Bullet, 12895c635efSGarrett D'Amore MDOC_Dash, 12995c635efSGarrett D'Amore MDOC_Hyphen, 13095c635efSGarrett D'Amore MDOC_Item, 13195c635efSGarrett D'Amore MDOC_Enum, 13295c635efSGarrett D'Amore MDOC_Tag, 13395c635efSGarrett D'Amore MDOC_Diag, 13495c635efSGarrett D'Amore MDOC_Hang, 13595c635efSGarrett D'Amore MDOC_Ohang, 13695c635efSGarrett D'Amore MDOC_Inset, 13795c635efSGarrett D'Amore MDOC_Column, 13895c635efSGarrett D'Amore MDOC_Width, 13995c635efSGarrett D'Amore MDOC_Offset, 14095c635efSGarrett D'Amore MDOC_Compact, 14195c635efSGarrett D'Amore MDOC_Nested, 14295c635efSGarrett D'Amore MDOC_ARG_MAX 14395c635efSGarrett D'Amore }; 14495c635efSGarrett D'Amore 14595c635efSGarrett D'Amore static const struct mdocarg mdocargs[MDOC_MAX] = { 146698f87a4SGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ap */ 14795c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Dd */ 14895c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Dt */ 14995c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Os */ 15095c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Sh */ 15195c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Ss */ 15295c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Pp */ 15395c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* D1 */ 15495c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Dl */ 15595c635efSGarrett D'Amore { ARGSFL_NONE, args_Bd }, /* Bd */ 15695c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Ed */ 15795c635efSGarrett D'Amore { ARGSFL_NONE, args_Bl }, /* Bl */ 15895c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* El */ 15995c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* It */ 16095c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ad */ 16195c635efSGarrett D'Amore { ARGSFL_DELIM, args_An }, /* An */ 16295c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ar */ 163698f87a4SGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Cd */ 16495c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Cm */ 16595c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Dv */ 16695c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Er */ 16795c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ev */ 16895c635efSGarrett D'Amore { ARGSFL_NONE, args_Ex }, /* Ex */ 16995c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Fa */ 17095c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Fd */ 17195c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Fl */ 17295c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Fn */ 17395c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ft */ 17495c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ic */ 175698f87a4SGarrett D'Amore { ARGSFL_DELIM, NULL }, /* In */ 17695c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Li */ 17795c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Nd */ 17895c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Nm */ 17995c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Op */ 180*260e9a87SYuri Pankov { ARGSFL_DELIM, NULL }, /* Ot */ 18195c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Pa */ 18295c635efSGarrett D'Amore { ARGSFL_NONE, args_Ex }, /* Rv */ 18395c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* St */ 18495c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Va */ 18595c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Vt */ 18695c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Xr */ 18795c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %A */ 18895c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %B */ 18995c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %D */ 19095c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %I */ 19195c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %J */ 19295c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %N */ 19395c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %O */ 19495c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %P */ 19595c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %R */ 19695c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %T */ 19795c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %V */ 19895c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ac */ 19995c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Ao */ 20095c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Aq */ 20195c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* At */ 20295c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Bc */ 20395c635efSGarrett D'Amore { ARGSFL_NONE, args_Bf }, /* Bf */ 20495c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Bo */ 20595c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Bq */ 20695c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Bsx */ 20795c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Bx */ 20895c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Db */ 20995c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Dc */ 21095c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Do */ 21195c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Dq */ 21295c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ec */ 21395c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Ef */ 21495c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Em */ 21595c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Eo */ 21695c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Fx */ 21795c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ms */ 21895c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* No */ 21995c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ns */ 22095c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Nx */ 22195c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ox */ 22295c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Pc */ 22395c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Pf */ 22495c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Po */ 22595c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Pq */ 22695c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Qc */ 22795c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ql */ 22895c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Qo */ 22995c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Qq */ 23095c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Re */ 23195c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Rs */ 23295c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Sc */ 23395c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* So */ 23495c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Sq */ 23595c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Sm */ 23695c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Sx */ 23795c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Sy */ 23895c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Tn */ 23995c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Ux */ 24095c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Xc */ 24195c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Xo */ 24295c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Fo */ 243698f87a4SGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Fc */ 24495c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Oo */ 24595c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Oc */ 24695c635efSGarrett D'Amore { ARGSFL_NONE, args_Bk }, /* Bk */ 24795c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Ek */ 24895c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Bt */ 24995c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Hf */ 250*260e9a87SYuri Pankov { ARGSFL_DELIM, NULL }, /* Fr */ 25195c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Ud */ 252698f87a4SGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Lb */ 25395c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Lp */ 25495c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Lk */ 25595c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Mt */ 25695c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Brq */ 25795c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Bro */ 25895c635efSGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Brc */ 25995c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %C */ 26095c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Es */ 261*260e9a87SYuri Pankov { ARGSFL_DELIM, NULL }, /* En */ 262698f87a4SGarrett D'Amore { ARGSFL_DELIM, NULL }, /* Dx */ 26395c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %Q */ 26495c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* br */ 26595c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* sp */ 26695c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* %U */ 26795c635efSGarrett D'Amore { ARGSFL_NONE, NULL }, /* Ta */ 268*260e9a87SYuri Pankov { ARGSFL_NONE, NULL }, /* ll */ 26995c635efSGarrett D'Amore }; 27095c635efSGarrett D'Amore 27195c635efSGarrett D'Amore 27295c635efSGarrett D'Amore /* 273*260e9a87SYuri Pankov * Parse flags and their arguments from the input line. 274*260e9a87SYuri Pankov * These come in the form -flag [argument ...]. 275*260e9a87SYuri Pankov * Some flags take no argument, some one, some multiple. 27695c635efSGarrett D'Amore */ 277*260e9a87SYuri Pankov void 278698f87a4SGarrett D'Amore mdoc_argv(struct mdoc *mdoc, int line, enum mdoct tok, 279*260e9a87SYuri Pankov struct mdoc_arg **reta, int *pos, char *buf) 28095c635efSGarrett D'Amore { 281*260e9a87SYuri Pankov struct mdoc_argv tmpv; 282*260e9a87SYuri Pankov struct mdoc_argv **retv; 283*260e9a87SYuri Pankov const enum mdocargt *argtable; 284*260e9a87SYuri Pankov char *argname; 285*260e9a87SYuri Pankov int ipos, retc; 286*260e9a87SYuri Pankov char savechar; 28795c635efSGarrett D'Amore 288*260e9a87SYuri Pankov *reta = NULL; 289*260e9a87SYuri Pankov 290*260e9a87SYuri Pankov /* Which flags does this macro support? */ 291*260e9a87SYuri Pankov 292*260e9a87SYuri Pankov argtable = mdocargs[tok].argvs; 293*260e9a87SYuri Pankov if (argtable == NULL) 294*260e9a87SYuri Pankov return; 295*260e9a87SYuri Pankov 296*260e9a87SYuri Pankov /* Loop over the flags on the input line. */ 297*260e9a87SYuri Pankov 298*260e9a87SYuri Pankov ipos = *pos; 299*260e9a87SYuri Pankov while (buf[ipos] == '-') { 30095c635efSGarrett D'Amore 30195c635efSGarrett D'Amore /* Seek to the first unescaped space. */ 30295c635efSGarrett D'Amore 303*260e9a87SYuri Pankov for (argname = buf + ++ipos; buf[ipos] != '\0'; ipos++) 304*260e9a87SYuri Pankov if (buf[ipos] == ' ' && buf[ipos - 1] != '\\') 30595c635efSGarrett D'Amore break; 30695c635efSGarrett D'Amore 30795c635efSGarrett D'Amore /* 308*260e9a87SYuri Pankov * We want to nil-terminate the word to look it up. 309*260e9a87SYuri Pankov * But we may not have a flag, in which case we need 310*260e9a87SYuri Pankov * to restore the line as-is. So keep around the 311*260e9a87SYuri Pankov * stray byte, which we'll reset upon exiting. 31295c635efSGarrett D'Amore */ 31395c635efSGarrett D'Amore 314*260e9a87SYuri Pankov if ((savechar = buf[ipos]) != '\0') 315*260e9a87SYuri Pankov buf[ipos++] = '\0'; 31695c635efSGarrett D'Amore 31795c635efSGarrett D'Amore /* 318*260e9a87SYuri Pankov * Now look up the word as a flag. Use temporary 319*260e9a87SYuri Pankov * storage that we'll copy into the node's flags. 32095c635efSGarrett D'Amore */ 32195c635efSGarrett D'Amore 322*260e9a87SYuri Pankov while ((tmpv.arg = *argtable++) != MDOC_ARG_MAX) 323*260e9a87SYuri Pankov if ( ! strcmp(argname, mdoc_argnames[tmpv.arg])) 32495c635efSGarrett D'Amore break; 32595c635efSGarrett D'Amore 326*260e9a87SYuri Pankov /* If it isn't a flag, restore the saved byte. */ 32795c635efSGarrett D'Amore 328*260e9a87SYuri Pankov if (tmpv.arg == MDOC_ARG_MAX) { 329*260e9a87SYuri Pankov if (savechar != '\0') 330*260e9a87SYuri Pankov buf[ipos - 1] = savechar; 33195c635efSGarrett D'Amore break; 33295c635efSGarrett D'Amore } 33395c635efSGarrett D'Amore 334*260e9a87SYuri Pankov /* Read to the next word (the first argument). */ 33595c635efSGarrett D'Amore 336*260e9a87SYuri Pankov while (buf[ipos] == ' ') 337*260e9a87SYuri Pankov ipos++; 33895c635efSGarrett D'Amore 339*260e9a87SYuri Pankov /* Parse the arguments of the flag. */ 34095c635efSGarrett D'Amore 341*260e9a87SYuri Pankov tmpv.line = line; 342*260e9a87SYuri Pankov tmpv.pos = *pos; 343*260e9a87SYuri Pankov tmpv.sz = 0; 344*260e9a87SYuri Pankov tmpv.value = NULL; 345*260e9a87SYuri Pankov 346*260e9a87SYuri Pankov switch (argvflags[tmpv.arg]) { 347*260e9a87SYuri Pankov case ARGV_SINGLE: 348*260e9a87SYuri Pankov argv_single(mdoc, line, &tmpv, &ipos, buf); 349*260e9a87SYuri Pankov break; 350*260e9a87SYuri Pankov case ARGV_MULTI: 351*260e9a87SYuri Pankov argv_multi(mdoc, line, &tmpv, &ipos, buf); 352*260e9a87SYuri Pankov break; 353*260e9a87SYuri Pankov case ARGV_NONE: 354*260e9a87SYuri Pankov break; 355*260e9a87SYuri Pankov } 356*260e9a87SYuri Pankov 357*260e9a87SYuri Pankov /* Append to the return values. */ 358*260e9a87SYuri Pankov 359*260e9a87SYuri Pankov if (*reta == NULL) 360*260e9a87SYuri Pankov *reta = mandoc_calloc(1, sizeof(**reta)); 361*260e9a87SYuri Pankov 362*260e9a87SYuri Pankov retc = ++(*reta)->argc; 363*260e9a87SYuri Pankov retv = &(*reta)->argv; 364*260e9a87SYuri Pankov *retv = mandoc_reallocarray(*retv, retc, sizeof(**retv)); 365*260e9a87SYuri Pankov memcpy(*retv + retc - 1, &tmpv, sizeof(**retv)); 366*260e9a87SYuri Pankov 367*260e9a87SYuri Pankov /* Prepare for parsing the next flag. */ 368*260e9a87SYuri Pankov 369*260e9a87SYuri Pankov *pos = ipos; 370*260e9a87SYuri Pankov argtable = mdocargs[tok].argvs; 371*260e9a87SYuri Pankov } 37295c635efSGarrett D'Amore } 37395c635efSGarrett D'Amore 37495c635efSGarrett D'Amore void 37595c635efSGarrett D'Amore mdoc_argv_free(struct mdoc_arg *p) 37695c635efSGarrett D'Amore { 37795c635efSGarrett D'Amore int i; 37895c635efSGarrett D'Amore 37995c635efSGarrett D'Amore if (NULL == p) 38095c635efSGarrett D'Amore return; 38195c635efSGarrett D'Amore 38295c635efSGarrett D'Amore if (p->refcnt) { 38395c635efSGarrett D'Amore --(p->refcnt); 38495c635efSGarrett D'Amore if (p->refcnt) 38595c635efSGarrett D'Amore return; 38695c635efSGarrett D'Amore } 38795c635efSGarrett D'Amore assert(p->argc); 38895c635efSGarrett D'Amore 38995c635efSGarrett D'Amore for (i = (int)p->argc - 1; i >= 0; i--) 39095c635efSGarrett D'Amore argn_free(p, i); 39195c635efSGarrett D'Amore 39295c635efSGarrett D'Amore free(p->argv); 39395c635efSGarrett D'Amore free(p); 39495c635efSGarrett D'Amore } 39595c635efSGarrett D'Amore 39695c635efSGarrett D'Amore static void 39795c635efSGarrett D'Amore argn_free(struct mdoc_arg *p, int iarg) 39895c635efSGarrett D'Amore { 39995c635efSGarrett D'Amore struct mdoc_argv *arg; 40095c635efSGarrett D'Amore int j; 40195c635efSGarrett D'Amore 40295c635efSGarrett D'Amore arg = &p->argv[iarg]; 40395c635efSGarrett D'Amore 40495c635efSGarrett D'Amore if (arg->sz && arg->value) { 40595c635efSGarrett D'Amore for (j = (int)arg->sz - 1; j >= 0; j--) 40695c635efSGarrett D'Amore free(arg->value[j]); 40795c635efSGarrett D'Amore free(arg->value); 40895c635efSGarrett D'Amore } 40995c635efSGarrett D'Amore 41095c635efSGarrett D'Amore for (--p->argc; iarg < (int)p->argc; iarg++) 41195c635efSGarrett D'Amore p->argv[iarg] = p->argv[iarg+1]; 41295c635efSGarrett D'Amore } 41395c635efSGarrett D'Amore 41495c635efSGarrett D'Amore enum margserr 415698f87a4SGarrett D'Amore mdoc_args(struct mdoc *mdoc, int line, int *pos, 41695c635efSGarrett D'Amore char *buf, enum mdoct tok, char **v) 41795c635efSGarrett D'Amore { 41895c635efSGarrett D'Amore struct mdoc_node *n; 419*260e9a87SYuri Pankov char *v_local; 420*260e9a87SYuri Pankov enum argsflag fl; 42195c635efSGarrett D'Amore 422*260e9a87SYuri Pankov if (v == NULL) 423*260e9a87SYuri Pankov v = &v_local; 424*260e9a87SYuri Pankov fl = tok == MDOC_MAX ? ARGSFL_NONE : mdocargs[tok].flags; 425*260e9a87SYuri Pankov if (tok != MDOC_It) 426698f87a4SGarrett D'Amore return(args(mdoc, line, pos, buf, fl, v)); 42795c635efSGarrett D'Amore 42895c635efSGarrett D'Amore /* 42995c635efSGarrett D'Amore * We know that we're in an `It', so it's reasonable to expect 43095c635efSGarrett D'Amore * us to be sitting in a `Bl'. Someday this may not be the case 43195c635efSGarrett D'Amore * (if we allow random `It's sitting out there), so provide a 43295c635efSGarrett D'Amore * safe fall-back into the default behaviour. 43395c635efSGarrett D'Amore */ 43495c635efSGarrett D'Amore 435698f87a4SGarrett D'Amore for (n = mdoc->last; n; n = n->parent) 43695c635efSGarrett D'Amore if (MDOC_Bl == n->tok) 43795c635efSGarrett D'Amore if (LIST_column == n->norm->Bl.type) { 43895c635efSGarrett D'Amore fl = ARGSFL_TABSEP; 43995c635efSGarrett D'Amore break; 44095c635efSGarrett D'Amore } 44195c635efSGarrett D'Amore 442698f87a4SGarrett D'Amore return(args(mdoc, line, pos, buf, fl, v)); 44395c635efSGarrett D'Amore } 44495c635efSGarrett D'Amore 44595c635efSGarrett D'Amore static enum margserr 446698f87a4SGarrett D'Amore args(struct mdoc *mdoc, int line, int *pos, 44795c635efSGarrett D'Amore char *buf, enum argsflag fl, char **v) 44895c635efSGarrett D'Amore { 44995c635efSGarrett D'Amore char *p, *pp; 450698f87a4SGarrett D'Amore int pairs; 45195c635efSGarrett D'Amore enum margserr rc; 45295c635efSGarrett D'Amore 45395c635efSGarrett D'Amore if ('\0' == buf[*pos]) { 454698f87a4SGarrett D'Amore if (MDOC_PPHRASE & mdoc->flags) 45595c635efSGarrett D'Amore return(ARGS_EOLN); 45695c635efSGarrett D'Amore /* 45795c635efSGarrett D'Amore * If we're not in a partial phrase and the flag for 45895c635efSGarrett D'Amore * being a phrase literal is still set, the punctuation 45995c635efSGarrett D'Amore * is unterminated. 46095c635efSGarrett D'Amore */ 461698f87a4SGarrett D'Amore if (MDOC_PHRASELIT & mdoc->flags) 462*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ARG_QUOTE, 463*260e9a87SYuri Pankov mdoc->parse, line, *pos, NULL); 46495c635efSGarrett D'Amore 465698f87a4SGarrett D'Amore mdoc->flags &= ~MDOC_PHRASELIT; 46695c635efSGarrett D'Amore return(ARGS_EOLN); 46795c635efSGarrett D'Amore } 46895c635efSGarrett D'Amore 46995c635efSGarrett D'Amore *v = &buf[*pos]; 47095c635efSGarrett D'Amore 47195c635efSGarrett D'Amore if (ARGSFL_DELIM == fl) 47295c635efSGarrett D'Amore if (args_checkpunct(buf, *pos)) 47395c635efSGarrett D'Amore return(ARGS_PUNCT); 47495c635efSGarrett D'Amore 47595c635efSGarrett D'Amore /* 47695c635efSGarrett D'Amore * First handle TABSEP items, restricted to `Bl -column'. This 47795c635efSGarrett D'Amore * ignores conventional token parsing and instead uses tabs or 47895c635efSGarrett D'Amore * `Ta' macros to separate phrases. Phrases are parsed again 47995c635efSGarrett D'Amore * for arguments at a later phase. 48095c635efSGarrett D'Amore */ 48195c635efSGarrett D'Amore 48295c635efSGarrett D'Amore if (ARGSFL_TABSEP == fl) { 48395c635efSGarrett D'Amore /* Scan ahead to tab (can't be escaped). */ 48495c635efSGarrett D'Amore p = strchr(*v, '\t'); 48595c635efSGarrett D'Amore pp = NULL; 48695c635efSGarrett D'Amore 48795c635efSGarrett D'Amore /* Scan ahead to unescaped `Ta'. */ 488698f87a4SGarrett D'Amore if ( ! (MDOC_PHRASELIT & mdoc->flags)) 48995c635efSGarrett D'Amore for (pp = *v; ; pp++) { 49095c635efSGarrett D'Amore if (NULL == (pp = strstr(pp, "Ta"))) 49195c635efSGarrett D'Amore break; 49295c635efSGarrett D'Amore if (pp > *v && ' ' != *(pp - 1)) 49395c635efSGarrett D'Amore continue; 49495c635efSGarrett D'Amore if (' ' == *(pp + 2) || '\0' == *(pp + 2)) 49595c635efSGarrett D'Amore break; 49695c635efSGarrett D'Amore } 49795c635efSGarrett D'Amore 49895c635efSGarrett D'Amore /* By default, assume a phrase. */ 49995c635efSGarrett D'Amore rc = ARGS_PHRASE; 50095c635efSGarrett D'Amore 50195c635efSGarrett D'Amore /* 50295c635efSGarrett D'Amore * Adjust new-buffer position to be beyond delimiter 50395c635efSGarrett D'Amore * mark (e.g., Ta -> end + 2). 50495c635efSGarrett D'Amore */ 50595c635efSGarrett D'Amore if (p && pp) { 50695c635efSGarrett D'Amore *pos += pp < p ? 2 : 1; 50795c635efSGarrett D'Amore rc = pp < p ? ARGS_PHRASE : ARGS_PPHRASE; 50895c635efSGarrett D'Amore p = pp < p ? pp : p; 50995c635efSGarrett D'Amore } else if (p && ! pp) { 51095c635efSGarrett D'Amore rc = ARGS_PPHRASE; 51195c635efSGarrett D'Amore *pos += 1; 51295c635efSGarrett D'Amore } else if (pp && ! p) { 51395c635efSGarrett D'Amore p = pp; 51495c635efSGarrett D'Amore *pos += 2; 51595c635efSGarrett D'Amore } else { 51695c635efSGarrett D'Amore rc = ARGS_PEND; 51795c635efSGarrett D'Amore p = strchr(*v, 0); 51895c635efSGarrett D'Amore } 51995c635efSGarrett D'Amore 52095c635efSGarrett D'Amore /* Whitespace check for eoln case... */ 52195c635efSGarrett D'Amore if ('\0' == *p && ' ' == *(p - 1)) 522*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, 523*260e9a87SYuri Pankov line, *pos, NULL); 52495c635efSGarrett D'Amore 52595c635efSGarrett D'Amore *pos += (int)(p - *v); 52695c635efSGarrett D'Amore 52795c635efSGarrett D'Amore /* Strip delimiter's preceding whitespace. */ 52895c635efSGarrett D'Amore pp = p - 1; 52995c635efSGarrett D'Amore while (pp > *v && ' ' == *pp) { 53095c635efSGarrett D'Amore if (pp > *v && '\\' == *(pp - 1)) 53195c635efSGarrett D'Amore break; 53295c635efSGarrett D'Amore pp--; 53395c635efSGarrett D'Amore } 53495c635efSGarrett D'Amore *(pp + 1) = 0; 53595c635efSGarrett D'Amore 53695c635efSGarrett D'Amore /* Strip delimiter's proceeding whitespace. */ 53795c635efSGarrett D'Amore for (pp = &buf[*pos]; ' ' == *pp; pp++, (*pos)++) 53895c635efSGarrett D'Amore /* Skip ahead. */ ; 53995c635efSGarrett D'Amore 54095c635efSGarrett D'Amore return(rc); 54195c635efSGarrett D'Amore } 54295c635efSGarrett D'Amore 54395c635efSGarrett D'Amore /* 54495c635efSGarrett D'Amore * Process a quoted literal. A quote begins with a double-quote 54595c635efSGarrett D'Amore * and ends with a double-quote NOT preceded by a double-quote. 546698f87a4SGarrett D'Amore * NUL-terminate the literal in place. 547698f87a4SGarrett D'Amore * Collapse pairs of quotes inside quoted literals. 54895c635efSGarrett D'Amore * Whitespace is NOT involved in literal termination. 54995c635efSGarrett D'Amore */ 55095c635efSGarrett D'Amore 551698f87a4SGarrett D'Amore if (MDOC_PHRASELIT & mdoc->flags || '\"' == buf[*pos]) { 552698f87a4SGarrett D'Amore if ( ! (MDOC_PHRASELIT & mdoc->flags)) 55395c635efSGarrett D'Amore *v = &buf[++(*pos)]; 55495c635efSGarrett D'Amore 555698f87a4SGarrett D'Amore if (MDOC_PPHRASE & mdoc->flags) 556698f87a4SGarrett D'Amore mdoc->flags |= MDOC_PHRASELIT; 55795c635efSGarrett D'Amore 558698f87a4SGarrett D'Amore pairs = 0; 55995c635efSGarrett D'Amore for ( ; buf[*pos]; (*pos)++) { 560698f87a4SGarrett D'Amore /* Move following text left after quoted quotes. */ 561698f87a4SGarrett D'Amore if (pairs) 562698f87a4SGarrett D'Amore buf[*pos - pairs] = buf[*pos]; 56395c635efSGarrett D'Amore if ('\"' != buf[*pos]) 56495c635efSGarrett D'Amore continue; 565698f87a4SGarrett D'Amore /* Unquoted quotes end quoted args. */ 56695c635efSGarrett D'Amore if ('\"' != buf[*pos + 1]) 56795c635efSGarrett D'Amore break; 568698f87a4SGarrett D'Amore /* Quoted quotes collapse. */ 569698f87a4SGarrett D'Amore pairs++; 57095c635efSGarrett D'Amore (*pos)++; 57195c635efSGarrett D'Amore } 572698f87a4SGarrett D'Amore if (pairs) 573698f87a4SGarrett D'Amore buf[*pos - pairs] = '\0'; 57495c635efSGarrett D'Amore 57595c635efSGarrett D'Amore if ('\0' == buf[*pos]) { 576698f87a4SGarrett D'Amore if (MDOC_PPHRASE & mdoc->flags) 57795c635efSGarrett D'Amore return(ARGS_QWORD); 578*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ARG_QUOTE, 579*260e9a87SYuri Pankov mdoc->parse, line, *pos, NULL); 58095c635efSGarrett D'Amore return(ARGS_QWORD); 58195c635efSGarrett D'Amore } 58295c635efSGarrett D'Amore 583698f87a4SGarrett D'Amore mdoc->flags &= ~MDOC_PHRASELIT; 58495c635efSGarrett D'Amore buf[(*pos)++] = '\0'; 58595c635efSGarrett D'Amore 58695c635efSGarrett D'Amore if ('\0' == buf[*pos]) 58795c635efSGarrett D'Amore return(ARGS_QWORD); 58895c635efSGarrett D'Amore 58995c635efSGarrett D'Amore while (' ' == buf[*pos]) 59095c635efSGarrett D'Amore (*pos)++; 59195c635efSGarrett D'Amore 59295c635efSGarrett D'Amore if ('\0' == buf[*pos]) 593*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, 594*260e9a87SYuri Pankov line, *pos, NULL); 59595c635efSGarrett D'Amore 59695c635efSGarrett D'Amore return(ARGS_QWORD); 59795c635efSGarrett D'Amore } 59895c635efSGarrett D'Amore 59995c635efSGarrett D'Amore p = &buf[*pos]; 600698f87a4SGarrett D'Amore *v = mandoc_getarg(mdoc->parse, &p, line, pos); 60195c635efSGarrett D'Amore 60295c635efSGarrett D'Amore return(ARGS_WORD); 60395c635efSGarrett D'Amore } 60495c635efSGarrett D'Amore 60595c635efSGarrett D'Amore /* 60695c635efSGarrett D'Amore * Check if the string consists only of space-separated closing 60795c635efSGarrett D'Amore * delimiters. This is a bit of a dance: the first must be a close 60895c635efSGarrett D'Amore * delimiter, but it may be followed by middle delimiters. Arbitrary 60995c635efSGarrett D'Amore * whitespace may separate these tokens. 61095c635efSGarrett D'Amore */ 61195c635efSGarrett D'Amore static int 61295c635efSGarrett D'Amore args_checkpunct(const char *buf, int i) 61395c635efSGarrett D'Amore { 61495c635efSGarrett D'Amore int j; 61595c635efSGarrett D'Amore char dbuf[DELIMSZ]; 61695c635efSGarrett D'Amore enum mdelim d; 61795c635efSGarrett D'Amore 61895c635efSGarrett D'Amore /* First token must be a close-delimiter. */ 61995c635efSGarrett D'Amore 62095c635efSGarrett D'Amore for (j = 0; buf[i] && ' ' != buf[i] && j < DELIMSZ; j++, i++) 62195c635efSGarrett D'Amore dbuf[j] = buf[i]; 62295c635efSGarrett D'Amore 62395c635efSGarrett D'Amore if (DELIMSZ == j) 62495c635efSGarrett D'Amore return(0); 62595c635efSGarrett D'Amore 62695c635efSGarrett D'Amore dbuf[j] = '\0'; 62795c635efSGarrett D'Amore if (DELIM_CLOSE != mdoc_isdelim(dbuf)) 62895c635efSGarrett D'Amore return(0); 62995c635efSGarrett D'Amore 63095c635efSGarrett D'Amore while (' ' == buf[i]) 63195c635efSGarrett D'Amore i++; 63295c635efSGarrett D'Amore 63395c635efSGarrett D'Amore /* Remaining must NOT be open/none. */ 63495c635efSGarrett D'Amore 63595c635efSGarrett D'Amore while (buf[i]) { 63695c635efSGarrett D'Amore j = 0; 63795c635efSGarrett D'Amore while (buf[i] && ' ' != buf[i] && j < DELIMSZ) 63895c635efSGarrett D'Amore dbuf[j++] = buf[i++]; 63995c635efSGarrett D'Amore 64095c635efSGarrett D'Amore if (DELIMSZ == j) 64195c635efSGarrett D'Amore return(0); 64295c635efSGarrett D'Amore 64395c635efSGarrett D'Amore dbuf[j] = '\0'; 64495c635efSGarrett D'Amore d = mdoc_isdelim(dbuf); 64595c635efSGarrett D'Amore if (DELIM_NONE == d || DELIM_OPEN == d) 64695c635efSGarrett D'Amore return(0); 64795c635efSGarrett D'Amore 64895c635efSGarrett D'Amore while (' ' == buf[i]) 64995c635efSGarrett D'Amore i++; 65095c635efSGarrett D'Amore } 65195c635efSGarrett D'Amore 65295c635efSGarrett D'Amore return('\0' == buf[i]); 65395c635efSGarrett D'Amore } 65495c635efSGarrett D'Amore 655*260e9a87SYuri Pankov static void 656698f87a4SGarrett D'Amore argv_multi(struct mdoc *mdoc, int line, 65795c635efSGarrett D'Amore struct mdoc_argv *v, int *pos, char *buf) 65895c635efSGarrett D'Amore { 65995c635efSGarrett D'Amore enum margserr ac; 66095c635efSGarrett D'Amore char *p; 66195c635efSGarrett D'Amore 66295c635efSGarrett D'Amore for (v->sz = 0; ; v->sz++) { 663*260e9a87SYuri Pankov if (buf[*pos] == '-') 66495c635efSGarrett D'Amore break; 665698f87a4SGarrett D'Amore ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p); 666*260e9a87SYuri Pankov if (ac == ARGS_EOLN) 66795c635efSGarrett D'Amore break; 66895c635efSGarrett D'Amore 669*260e9a87SYuri Pankov if (v->sz % MULTI_STEP == 0) 670*260e9a87SYuri Pankov v->value = mandoc_reallocarray(v->value, 671*260e9a87SYuri Pankov v->sz + MULTI_STEP, sizeof(char *)); 67295c635efSGarrett D'Amore 67395c635efSGarrett D'Amore v->value[(int)v->sz] = mandoc_strdup(p); 67495c635efSGarrett D'Amore } 67595c635efSGarrett D'Amore } 67695c635efSGarrett D'Amore 677*260e9a87SYuri Pankov static void 678698f87a4SGarrett D'Amore argv_single(struct mdoc *mdoc, int line, 67995c635efSGarrett D'Amore struct mdoc_argv *v, int *pos, char *buf) 68095c635efSGarrett D'Amore { 68195c635efSGarrett D'Amore enum margserr ac; 68295c635efSGarrett D'Amore char *p; 68395c635efSGarrett D'Amore 684698f87a4SGarrett D'Amore ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p); 685*260e9a87SYuri Pankov if (ac == ARGS_EOLN) 686*260e9a87SYuri Pankov return; 68795c635efSGarrett D'Amore 68895c635efSGarrett D'Amore v->sz = 1; 68995c635efSGarrett D'Amore v->value = mandoc_malloc(sizeof(char *)); 69095c635efSGarrett D'Amore v->value[0] = mandoc_strdup(p); 69195c635efSGarrett D'Amore } 692