{ This file is part of the Free Pascal run time library. Copyright (c) 1999-2000 by Michael Van Canneyt, member of the Free Pascal development team. Getopt implementation for Free Pascal, modeled after GNU getopt See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} unit getopts; Interface Const No_Argument = 0; Required_Argument = 1; Optional_Argument = 2; EndOfOptions = #255; Type POption = ^TOption; TOption = Record Name : String; Has_arg : Integer; Flag : PChar; Value : Char; end; Orderings = (require_order,permute,return_in_order); Const OptSpecifier : set of char=['-']; Var OptArg : String; OptInd : Longint; OptErr : Boolean; OptOpt : Char; Function GetOpt (ShortOpts : String) : char; Function GetLongOpts (ShortOpts : String;LongOpts : POption;var Longind : Longint) : char; Implementation {$IFNDEF FPC} {$ifdef TP} uses strings; {$else } uses SysUtils; type PtrInt = Integer; {$endif} {$ENDIF FPC} {*************************************************************************** Create an ArgV ***************************************************************************} {$IF not Declared(argv)} //{$ifdef TP} type ppchar = ^pchar; apchar = array[0..127] of pchar; var argc : longint; argv : apchar; const CHAR_SIZE = SizeOf(Char); procedure setup_arguments; var arglen, count : longint; argstart, cmdline : pchar; quote : set of char; argsbuf : array[0..127] of pchar; s : string; i : integer; begin { create argv[0] which is the started filename } s:=paramstr(0); arglen:=length(s); getmem(argsbuf[0], ( ( arglen + 1 ) * CHAR_SIZE ) ); strpcopy(argsbuf[0],s); { create commandline } s:=''; for i:=1 to paramcount do begin if Pos(' ', paramstr(i)) > 0 then s := s + '"' + paramstr(i) + '" ' else s:=s+paramstr(i)+' '; end; s:=s+#0; cmdline:=@s[1]; count:=1; repeat { skip leading spaces } while cmdline^ in [' ',#9,#13] do inc(PtrInt(cmdline),CHAR_SIZE); case cmdline^ of #0 : break; '"' : begin quote:=['"']; inc(PtrInt(cmdline),CHAR_SIZE); end; '''' : begin quote:=['''']; inc(PtrInt(cmdline),CHAR_SIZE); end; else quote:=[' ',#9,#13]; end; { scan until the end of the argument } argstart:=cmdline; while (cmdline^<>#0) and not(cmdline^ in quote) do inc(PtrInt(cmdline),CHAR_SIZE); { reserve some memory } arglen:=cmdline-argstart; getmem(argsbuf[count],(arglen+1) * CHAR_SIZE); move(argstart^,argsbuf[count]^,arglen * CHAR_SIZE); argsbuf[count][arglen]:=#0; { skip quote } if cmdline^ in quote then inc(PtrInt(cmdline),CHAR_SIZE); inc(count); until false; { create argc } argc:=count; { create an nil entry } argsbuf[count]:=nil; inc(count); { create the argv } move(argsbuf,argv,count shl 2); end; {$IFEND} //{$endif TP} {*************************************************************************** Real Getopts ***************************************************************************} Var NextChar, Nrargs, first_nonopt, last_nonopt : Longint; Ordering : Orderings; Procedure Exchange; var bottom, middle, top,i,len : longint; temp : pchar; begin bottom:=first_nonopt; middle:=last_nonopt; top:=optind; while (top>middle) and (middle>bottom) do begin if (top-middle>middle-bottom) then begin len:=middle-bottom; for i:=1 to len-1 do begin temp:=argv[bottom+i]; argv[bottom+i]:=argv[top-(middle-bottom)+i]; argv[top-(middle-bottom)+i]:=temp; end; top:=top-len; end else begin len:=top-middle; for i:=0 to len-1 do begin temp:=argv[bottom+i]; argv[bottom+i]:=argv[middle+i]; argv[middle+i]:=temp; end; bottom:=bottom+len; end; end; first_nonopt:=first_nonopt + optind-last_nonopt; last_nonopt:=optind; end; { exchange } procedure getopt_init (var opts : string); begin { Initialize some defaults. } Optarg:=''; Optind:=1; First_nonopt:=1; Last_nonopt:=1; OptOpt:='?'; Nextchar:=0; case opts[1] of '-' : begin ordering:=return_in_order; delete(opts,1,1); end; '+' : begin ordering:=require_order; delete(opts,1,1); end; else ordering:=permute; end; end; Function Internal_getopt (Var Optstring : string;LongOpts : POption; LongInd : pointer;Long_only : boolean ) : char; var temp,endopt, option_index : byte; indfound : integer; currentarg, optname : string; p,pfound : POption; exact,ambig : boolean; c : char; begin optarg:=''; if optind=0 then getopt_init(optstring); { Check if We need the next argument. } if (optindlast_nonopt) and (last_nonopt<>optind) then exchange else if last_nonopt<>optind then first_nonopt:=optind; while (optindnrargs) and (currentarg='--') then begin inc(optind); if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then exchange else if first_nonopt=last_nonopt then first_nonopt:=optind; last_nonopt:=nrargs; optind:=nrargs; end; { Are we at the end of all arguments ? } if optind>=nrargs then begin if first_nonopt<>last_nonopt then optind:=first_nonopt; Internal_getopt:=EndOfOptions; exit; end; if optindnil) and ((currentarg[2]='-') and (currentArg[1]='-')) then inc(nextchar); { So, now nextchar points at the first character of an option } end; { Check if we have a long option } if longopts<>nil then if length(currentarg)>1 then if ((currentarg[2]='-') and (currentArg[1]='-')) or ((not long_only) and (pos(currentarg[2],optstring)<>0)) then begin { Get option name } endopt:=pos('=',currentarg); if endopt=0 then endopt:=length(currentarg)+1; optname:=copy(currentarg,nextchar,endopt-nextchar); { Match partial or full } p:=longopts; pfound:=nil; exact:=false; ambig:=false; option_index:=0; indfound:=0; while (p^.name<>'') and (not exact) do begin if pos(optname,p^.name)<>0 then begin if length(optname)=length(p^.name) then begin exact:=true; pfound:=p; indfound:=option_index; end else if pfound=nil then begin indfound:=option_index; pfound:=p end else ambig:=true; end; inc(PByte(p),sizeof(toption)); //inc(pointer(p),sizeof(toption)); // for Delphi compatibility inc(option_index); end; if ambig and not exact then begin if opterr then writeln(argv[0],': option "',optname,'" is ambiguous'); nextchar:=0; inc(optind); Internal_getopt:='?'; end; if pfound<>nil then begin inc(optind); if endopt<=length(currentarg) then begin if pfound^.has_arg>0 then optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt) else begin if opterr then if currentarg[2]='-' then writeln(argv[0],': option "--',pfound^.name,'" doesn''t allow an argument') else writeln(argv[0],': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument'); nextchar:=0; internal_getopt:='?'; exit; end; end else { argument in next paramstr... } begin if pfound^.has_arg=1 then begin if optindnil then plongint(longind)^:=indfound+1; if pfound^.flag<>nil then begin pfound^.flag^:=pfound^.value; internal_getopt:=#0; exit; end; internal_getopt:=pfound^.value; exit; end; { pfound<>nil } { We didn't find it as an option } if (not long_only) or ((currentarg[2]='-') or (pos(CurrentArg[nextchar],optstring)=0)) then begin if opterr then if currentarg[2]='-' then writeln(argv[0],' unrecognized option "--',optname,'"') else writeln(argv[0],' unrecognized option "',currentarg[1],optname,'"'); nextchar:=0; inc(optind); Internal_getopt:='?'; exit; end; end; { Of long options.} { We check for a short option. } temp:=pos(currentarg[nextchar],optstring); c:=currentarg[nextchar]; inc(nextchar); if nextchar>length(currentarg) then begin inc(optind); nextchar:=0; end; if (temp=0) or (c=':') then begin if opterr then writeln(argv[0],': illegal option -- ',c); optopt:=c; internal_getopt:='?'; exit; end; Internal_getopt:=optstring[temp]; if optstring[temp+1]=':' then if optstring[temp+2]=':' then begin { optional argument } if nextchar>0 then begin optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1); inc(optind); nextchar:=0; end else if (optind<>nrargs) then begin optarg:=strpas(argv[optind]); if optarg[1]='-' then optarg:='' else inc(optind); nextchar:=0; end; end else begin { required argument } if nextchar>0 then begin optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1); inc(optind); end else if (optind=nrargs) then begin if opterr then writeln (argv[0],': option requires an argument -- ',optstring[temp]); optopt:=optstring[temp]; if optstring[1]=':' then Internal_getopt:=':' else Internal_Getopt:='?'; end else begin optarg:=strpas(argv[optind]); inc(optind) end; nextchar:=0; end; { End of required argument} end; { End of internal getopt...} Function GetOpt(ShortOpts : String) : char; begin getopt:=internal_getopt(shortopts,nil,nil,false); end; Function GetLongOpts(ShortOpts : String;LongOpts : POption;var Longind : Longint) : char; begin getlongopts:=internal_getopt(shortopts,longopts,@longind,true); end; {$ifdef FPC} initialization {$endif} {$ifndef FPC} {$ifdef TP} begin {$else} initialization {$endif} {$endif} { create argv if running under TP } {$ifndef FPC} setup_arguments; {$endif} { Needed to detect startup } Opterr:=true; Optind:=0; nrargs:=argc; end.