Source file: /~heha/hsn/bl/stm32flash.zip/src/gpsystem.cpp

#include "gpsystem.h"
#include <string.h>
#include <stdlib.h>	// exit()
#include <stdio.h>
#include <stdarg.h>

void gp_checkarg(const char*arg, const char*opts) {
 if (!arg) return;	// another problem
 if (arg[0]!='-') return;	// cannot be a long option
 if (arg[1]!='-') return;	// cannot be a long option
 if (arg[2]) {
  const char*a = arg+2;
  const char*e = strchr(a,'=');	// possible end of long option
  if (!e) e = a+strlen(a);	// end of long option
  size_t la = e-a;		// length of given tag, 1 = minimum

  for (;*opts;opts+=strlen(opts)+1) {
   const char*q0 = opts+1;	// long option tag
   const char*qe = strpbrk(q0,"\1\2");	// possible end of tag
   if (!qe) qe = q0+strlen(q0);	// end of tag
   size_t lo = qe-q0;		// length of tag to compare, can be 0!
   if (la==lo && !memcmp(a,q0,la)) goto silly;
  }
 }else{
silly:
  fprintf(stderr, "Error: This option may not be parameter of another option: \"%s\"\n",arg);
  exit(1);
 }
}

// This "getopt" function uses a callback for every found option,
// and uses a compact double-zero-terminated string for control.

void gp_getopt(const char*const*argv,const char*opts,void(*cb)(void*,char,const char*),void*cd) {
 bool parsestop = false;	// after "-" or "--", end parsing for options, callback with plain argument
 const char*argv0 = *argv;	// keep program name for error messages
 while (*++argv) {	// The argv[] array is zero terminated by C/C++ standard
  const char*arg = *argv;
  if (parsestop) cb(cd,0,arg);
  else if (arg[0]=='-') {
   if (arg[1]==arg[0]) {
    if (!arg[2]) parsestop = true;
    else{		// scan for known long option
     const char*a = arg+2;
     const char*a2 = strchr(a,'=');	// allow "=" between tag and value
     const char*e = a2 ? a2 : a+strlen(a);
     size_t la = e-a;		// length of given tag, 1 = minimum
     for(const char*q=opts;*q;q+=strlen(q)+1) {
      const char*q0 = q+1;		// skip short option for now
      const char*q1 = strpbrk(q0,"\1\2");	// scan for "argument" terminator
      const char*qe = q1 ? q1 : q0+strlen(q0);
      size_t lo = qe-q0;		// length of tag to compare, can be 0!
      if (la==lo && !memcmp(a,q0,la)) {	// correct long option, look for argument
       const char*b = 0;
       if (q1) if (*q1==1) {
        b = a2 ? a2+1 : *++argv;	// unconditionally take next argument, even if 0
	if (!b) fprintf(stderr,"%s: option '%s' requires an argument\n",argv0,arg);
       }else{
	if (a2) b = a2+1;
        else if (argv[1] && argv[1][0]!='-') b = *++argv;	// take next arg if not looking like an option
       }
       cb(cd,*q,b);	// call back (in any case)
       if (!*argv) return;		// break-out when next argument is nullptr
       goto found;
      }
     }
     fprintf(stderr,"%s: unrecognized option '%s'\n",argv0,arg);
     cb(cd,-1,arg);			// take argument as (possibly wrong) file name?
found:;
    }
   }else if (!arg[1]) parsestop = true;
// process concatenated short options
   else for (const char*a = arg;*++a;) {
    const char*b = a[1]=='=' ? a+2 : a[1] ? a+1 : 0;
    for(const char*q=opts;*q;q+=strlen(q)+1) {
     if (*a==*q) {
      const char*q1 = strpbrk(q+1,"\1\2");
      if (q1) {
       if (*q1==1) {	// required argument?
        if (!b) b = *++argv;	// if no tail, take next arg
	if (!b) fprintf(stderr,"%s: option requires an argument -- %c\n",argv0,*a);
       }else{		// optional argument?
        if (!b) {		// if no tail, check next arg
         b = argv[1] && argv[1][0]!='-' ? *++argv : 0;
        }
       }
      }else b = 0;		// Always no argument
      goto found2;		// exit inner loop with known option
     }
    }
    fprintf(stderr,"%s: invalid option -- %c\n",argv0,*a);
found2:
    cb(cd,*a,b);
    if (b) break;		// exit outer loop when argument was taken
// As side effect, unknown options are reported with argument, e.g.
// "-?=1" goes to callback with c == '?' and arg == "1".
// "-?" (same as "-h")		c == '?' and arg == nullptr
// whereas "=" forces non-nullptr argument:
// "-?="			c == '?' and arg == ""
// however known 'h' produces:
// "-h=="			c == 'h' and arg == nullptr
// and then
//				c == '=' and arg == "" (for the second '=')
   }
  }else cb(cd,0,arg);		// non-option argument (file name etc.)
 }
}

// This functor-like class cuts long strings into words and checks whether it will fit
// onto the screen.
// Moreover, it emits TAB characters and take the current position into account;
// tab width is fixed to 8.
// UTF-8 non-combining characters are measured correctly. Assume monospaced font.
struct PrintW{
  int left,width;
  mutable int x;
  char last;
  void tab(int) const;
  const PrintW&operator()(int x) const {tab(x);return*this;}
  void put(char c) const {if (c) {putchar(c); x+=measure(c);}}
  const PrintW&operator<<(char c) const {put(c);return*this;}
  void put(const char*s, size_t slen) const {if (slen) do put(*s++); while(--slen);}
  void put(const char*s) const {while (*s) put(*s++);}
  const PrintW&operator<<(const char*s) const {put(s);return*this;}
  int measure(char c) const;
  static int measure(const char*s,size_t slen);
  void operator()(const char*s);
  void put_option(const char*arg,bool optional);
  PrintW(int l,int w):left(l),width(w),x(0),last(0) {}
};

void PrintW::tab(int col) const{
  while (x+7<col) put('\t');
  while (x<col) put(' ');
}

int PrintW::measure(char c) const{
  if (c < -0x40) return 0;	// don't count UTF-8 trailing bytes
  if (!c) return 0;		// don't count "no character"
  if (c=='\t') return 8-(x&7);	// spaces to next tabstop
  if (c=='\n') return -x;	// let rewind to zero
  return 1;			// one character
}

// For simplicity, skip 0x80..0xBF character codes, will work with most UTF-8
int PrintW::measure(const char*s, size_t slen) {
  int len=0;
  if (slen) do if (*s++ >= -0x40) ++len; while(--slen);
  return len;
}

void PrintW::operator()(const char*s) {
  int indent=0;
  while (*s) {
    const char*eow = strpbrk(s,"\n\t ");	// only these three characters are allowed!
    if (!eow) eow = s+strlen(s);
    int len = measure(s,eow-s);
    if (x+measure(last)+len>width) {
      put('\n');
      tab(left+indent);
    }else{
      put(last);
      if (last=='\n') {	// the first '\n' in text switches the indent to 3 as a bullet list is expected to follow
        tab(left);
	indent=3;
      }
    }
    put(s,eow-s);
    if ((last=*(s=eow))) ++s;
  }
  put('\n');
}

void PrintW::put_option(const char*arg,bool optional) {
  if (!arg || !*arg) return;
  put(' ');
  if (optional) put('[');	// show bracket for optional argument
  put(arg);
  if (optional) put(']');
}

// This generic usage printout function uses the same opts as gp_getopt()
// and writes them out using localizable desc string list.
// Important: Options and descriptions must fit one-by-one.
// <left> must be less than <width> and both non-negative!
// TODO: Summary line (always the same?)
// TODO: Auto-detect screen width
// TODO: UTF-8 to OEMCP conversion for Windows (for non-english description)
// TODO: Use string resource for localized description (Windows only)
void _cdecl gp_usage(const char*argv0,const char*opts,const char*desc,
	int left,int width,const char*footer,...) {
  if (argv0) printf("Usage: %s [options] file\n",argv0);
  printf("Options: [defaults in brackets after descriptions]\n");
  PrintW pr(left+2,width);	// this class avoids need for C++11 lambdas
  for(;*opts;opts+=strlen(opts)+1, desc+=strlen(desc)+1) {
    const char*q0=opts+1;	// long option (if any)
    const char*q1=strpbrk(q0,"\1\2");	// required (1) / optional (2) argument
    const char*arg=0;		// argument name?
    if (q1) arg=q1+1;		// Here is the name
    else q1=q0+strlen(q0);	// no (0) argument
    if (*opts>' ') {		// Have short option?
      pr(2)<<'-'<<*opts;	// show it
      pr.put_option(arg,*q1==2);
      if (*q0>' ') pr<<',';
      pr<<' ';			// TODO: Nicht ausgeben wenn TAB folgen kann
    }
    if (*q0>' ') {
      pr(6);
      pr<<'-'<<'-';
      pr.put(q0,q1-q0);
      pr.put_option(arg,*q1==2);
      pr<<' '<<' ';
    }
    if (pr.x>pr.left+8) pr<<'\n';
    pr(left);
    pr(desc);
  }
  if (footer) {
    va_list va;
    va_start(va,footer);
    vprintf(footer,va);
    va_end(va);
  }
  printf("\tReport bugs to: %s\n",BUGREPORT);
}
Detected encoding: ASCII (7 bit)2