{ $Id$ This file is part of the Free Pascal run time library. Copyright (c) 1993,97 by Michael Van Canneyt, member of the Free Pascal development team. 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; {$I os.inc} { -------------------------------------------------------------------- Getopt implementation for FPK pascal, modeled after GNU getopt. Tested under Linux. Tested under DOS Michael Van Canneyt, 1997 *NOTE* The routines are a more or less straightforward conversion of the GNU C implementation of getopt. One day they should be replaced by some 'real pascal code'. -------------------------------------------------------------------- } Interface Const No_Argument = 0; Required_Argument = 1; Optional_Argument = 2; EndOfOptions = #255; Type Option = Record Name : String; Has_arg : Integer; Flag : ^char; Value : Char; end; POption = ^Option; Orderings = (require_order,permute,return_in_order); Var OptArg : String; OptInd : Integer; OptErr : Boolean; OptOpt : Char; Function GetOpt (ShortOpts : String) : char; Function GetLongOpts (ShortOpts : String; LongOpts : POption; var Longind : Integer) : char; Implementation Var NextChar : integer; first_nonopt,last_nonopt,Nrargs : Integer; Ordering : orderings; {$ifndef linux} argv : ^pchar; {$endif} { Copied straight from strings.pp, avoids the 'uses strings' } function strpas(p : pchar) : string; begin asm cld movl 12(%ebp),%edi movl %edi,%esi movl $0xffffffff,%ecx xorb %al,%al repne scasb notl %ecx decl %ecx movl 8(%ebp),%edi movb %cl,%al stosb rep movsb end ['ECX','EAX','ESI','EDI']; end; Procedure Exchange; var bottom,middle,top,i,len : integer; 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; if opts[1]='-' then begin ordering:=return_in_order; delete(opts,1,1); end else if opts[1]='+' then begin ordering:=require_order; delete(opts,1,1); end else ordering:=permute; end; Function Internal_getopt (Var Optstring : string; LongOpts : POption; LongInd : pointer; Long_only : boolean ) : char; type pint=^integer; 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 (optind'-') or (length(strpas(argv[optind]))=1)) do begin inc(optind); end; last_nonopt:=optind; end; { Check for '--' argument } if 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 optind'-') or (currentarg='-') then begin if ordering=require_order then begin Internal_getopt:=EndOfOptions; exit; end else begin optarg:=strpas(argv[optind]); inc(optind); Internal_getopt:=#1; exit; end; end; { At this point we're at an option ...} nextchar:=2; if (longopts<>nil) and (currentarg[2]='-') 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]='-') 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 (longint(p),sizeof(option)); inc (option_index); end; if ambig and not exact then begin if opterr then writeln (paramstr(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 begin optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt); end else begin if opterr then if currentarg[2]='-' then writeln (paramstr(0),': option "--',pfound^.name,'" doesn''t allow an argument') else writeln (paramstr(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 pint(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 (paramstr(0),' unrecognized option "--',optname,'"') else writeln (paramstr(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 (paramstr(0),': illegal option -- ',c); optopt:=currentarg[nextchar-1]; internal_getopt:='?'; exit; end; Internal_getopt:=optstring[temp]; if optstring[temp+1]=':' then if currentarg[temp+2]=':' then begin { optional argument } optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1); nextchar:=0; 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 (paramstr(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 : Integer) : char; begin getlongopts:=internal_getopt ( shortopts,longopts,@longind,true); end; {$ifndef linux} function args : pointer; begin asm movl _args,%eax leave ret end ['EAX']; end; {$endif} begin { Needed to detect startup } Opterr:=true; Optind:=0; nrargs:=paramcount+1; {$ifndef linux} argv:=args; {$endif} end. { $Log$ Revision 1.1 1998-03-25 11:18:42 root Initial revision Revision 1.3 1998/01/26 11:58:56 michael + Added log at the end Working file: rtl/i386/getopts.pp description: ---------------------------- revision 1.2 date: 1997/12/01 12:34:38; author: michael; state: Exp; lines: +15 -2 + added copyright reference in header. ---------------------------- revision 1.1 date: 1997/11/27 08:33:47; author: michael; state: Exp; Initial revision ---------------------------- revision 1.1.1.1 date: 1997/11/27 08:33:47; author: michael; state: Exp; lines: +0 -0 FPC RTL CVS start ============================================================================= }