getopts.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Michael Van Canneyt,
  4. member of the Free Pascal development team.
  5. Getopt implementation for Free Pascal, modeled after GNU getopt
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit getopts;
  13. Interface
  14. Const
  15. No_Argument = 0;
  16. Required_Argument = 1;
  17. Optional_Argument = 2;
  18. EndOfOptions = #255;
  19. Type
  20. POption = ^TOption;
  21. TOption = Record
  22. Name : String;
  23. Has_arg : Integer;
  24. Flag : PChar;
  25. Value : Char;
  26. end;
  27. Orderings = (require_order,permute,return_in_order);
  28. Const
  29. OptSpecifier : set of char=['-'];
  30. Var
  31. OptArg : String;
  32. OptInd : Longint;
  33. OptErr : Boolean;
  34. OptOpt : Char;
  35. Function GetOpt (ShortOpts : String) : char;
  36. Function GetLongOpts (ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  37. Implementation
  38. {$IFNDEF FPC}
  39. {$ifdef TP}
  40. uses strings;
  41. {$else }
  42. uses SysUtils;
  43. type PtrInt = Integer;
  44. {$endif}
  45. {$ENDIF FPC}
  46. {***************************************************************************
  47. Create an ArgV
  48. ***************************************************************************}
  49. {$IF not Declared(argv)} //{$ifdef TP}
  50. type
  51. ppchar = ^pchar;
  52. apchar = array[0..127] of pchar;
  53. var
  54. argc : longint;
  55. argv : apchar;
  56. const
  57. CHAR_SIZE = SizeOf(Char);
  58. procedure setup_arguments;
  59. var
  60. arglen,
  61. count : longint;
  62. argstart,
  63. cmdline : pchar;
  64. quote : set of char;
  65. argsbuf : array[0..127] of pchar;
  66. s : string;
  67. i : integer;
  68. begin
  69. { create argv[0] which is the started filename }
  70. s:=paramstr(0);
  71. arglen:=length(s);
  72. getmem(argsbuf[0], ( ( arglen + 1 ) * CHAR_SIZE ) );
  73. strpcopy(argsbuf[0],s);
  74. { create commandline }
  75. s:='';
  76. for i:=1 to paramcount do
  77. begin
  78. if Pos(' ', paramstr(i)) > 0 then
  79. s := s + '"' + paramstr(i) + '" '
  80. else
  81. s:=s+paramstr(i)+' ';
  82. end;
  83. s:=s+#0;
  84. cmdline:=@s[1];
  85. count:=1;
  86. repeat
  87. { skip leading spaces }
  88. while cmdline^ in [' ',#9,#13] do
  89. inc(PtrInt(cmdline),CHAR_SIZE);
  90. case cmdline^ of
  91. #0 : break;
  92. '"' : begin
  93. quote:=['"'];
  94. inc(PtrInt(cmdline),CHAR_SIZE);
  95. end;
  96. '''' : begin
  97. quote:=[''''];
  98. inc(PtrInt(cmdline),CHAR_SIZE);
  99. end;
  100. else
  101. quote:=[' ',#9,#13];
  102. end;
  103. { scan until the end of the argument }
  104. argstart:=cmdline;
  105. while (cmdline^<>#0) and not(cmdline^ in quote) do
  106. inc(PtrInt(cmdline),CHAR_SIZE);
  107. { reserve some memory }
  108. arglen:=cmdline-argstart;
  109. getmem(argsbuf[count],(arglen+1) * CHAR_SIZE);
  110. move(argstart^,argsbuf[count]^,arglen * CHAR_SIZE);
  111. argsbuf[count][arglen]:=#0;
  112. { skip quote }
  113. if cmdline^ in quote then
  114. inc(PtrInt(cmdline),CHAR_SIZE);
  115. inc(count);
  116. until false;
  117. { create argc }
  118. argc:=count;
  119. { create an nil entry }
  120. argsbuf[count]:=nil;
  121. inc(count);
  122. { create the argv }
  123. move(argsbuf,argv,count shl 2);
  124. end;
  125. {$IFEND} //{$endif TP}
  126. {***************************************************************************
  127. Real Getopts
  128. ***************************************************************************}
  129. Var
  130. NextChar,
  131. Nrargs,
  132. first_nonopt,
  133. last_nonopt : Longint;
  134. Ordering : Orderings;
  135. Procedure Exchange;
  136. var
  137. bottom,
  138. middle,
  139. top,i,len : longint;
  140. temp : pchar;
  141. begin
  142. bottom:=first_nonopt;
  143. middle:=last_nonopt;
  144. top:=optind;
  145. while (top>middle) and (middle>bottom) do
  146. begin
  147. if (top-middle>middle-bottom) then
  148. begin
  149. len:=middle-bottom;
  150. for i:=1 to len-1 do
  151. begin
  152. temp:=argv[bottom+i];
  153. argv[bottom+i]:=argv[top-(middle-bottom)+i];
  154. argv[top-(middle-bottom)+i]:=temp;
  155. end;
  156. top:=top-len;
  157. end
  158. else
  159. begin
  160. len:=top-middle;
  161. for i:=0 to len-1 do
  162. begin
  163. temp:=argv[bottom+i];
  164. argv[bottom+i]:=argv[middle+i];
  165. argv[middle+i]:=temp;
  166. end;
  167. bottom:=bottom+len;
  168. end;
  169. end;
  170. first_nonopt:=first_nonopt + optind-last_nonopt;
  171. last_nonopt:=optind;
  172. end; { exchange }
  173. procedure getopt_init (var opts : string);
  174. begin
  175. { Initialize some defaults. }
  176. Optarg:='';
  177. Optind:=1;
  178. First_nonopt:=1;
  179. Last_nonopt:=1;
  180. OptOpt:='?';
  181. Nextchar:=0;
  182. case opts[1] of
  183. '-' : begin
  184. ordering:=return_in_order;
  185. delete(opts,1,1);
  186. end;
  187. '+' : begin
  188. ordering:=require_order;
  189. delete(opts,1,1);
  190. end;
  191. else
  192. ordering:=permute;
  193. end;
  194. end;
  195. Function Internal_getopt (Var Optstring : string;LongOpts : POption;
  196. LongInd : pointer;Long_only : boolean ) : char;
  197. var
  198. temp,endopt,
  199. option_index : byte;
  200. indfound : integer;
  201. currentarg,
  202. optname : string;
  203. p,pfound : POption;
  204. exact,ambig : boolean;
  205. c : char;
  206. begin
  207. optarg:='';
  208. if optind=0 then
  209. getopt_init(optstring);
  210. { Check if We need the next argument. }
  211. if (optind<nrargs) then
  212. currentarg:=strpas(argv[optind])
  213. else
  214. currentarg:='';
  215. if (nextchar=0) then
  216. begin
  217. if ordering=permute then
  218. begin
  219. { If we processed options following non-options : exchange }
  220. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  221. exchange
  222. else
  223. if last_nonopt<>optind then
  224. first_nonopt:=optind;
  225. while (optind<nrargs) and (not(argv[optind][0] in OptSpecifier) or
  226. (length(strpas(argv[optind]))=1)) do
  227. inc(optind);
  228. last_nonopt:=optind;
  229. end;
  230. { Check for '--' argument }
  231. if optind<nrargs then
  232. currentarg:=strpas(argv[optind])
  233. else
  234. currentarg:='';
  235. if (optind<>nrargs) and (currentarg='--') then
  236. begin
  237. inc(optind);
  238. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  239. exchange
  240. else
  241. if first_nonopt=last_nonopt then
  242. first_nonopt:=optind;
  243. last_nonopt:=nrargs;
  244. optind:=nrargs;
  245. end;
  246. { Are we at the end of all arguments ? }
  247. if optind>=nrargs then
  248. begin
  249. if first_nonopt<>last_nonopt then
  250. optind:=first_nonopt;
  251. Internal_getopt:=EndOfOptions;
  252. exit;
  253. end;
  254. if optind<nrargs then
  255. currentarg:=strpas(argv[optind])
  256. else
  257. currentarg:='';
  258. { Are we at a non-option ? }
  259. if not(currentarg[1] in OptSpecifier) or (length(currentarg)=1) then
  260. begin
  261. if ordering=require_order then
  262. begin
  263. Internal_getopt:=EndOfOptions;
  264. exit;
  265. end
  266. else
  267. begin
  268. optarg:=strpas(argv[optind]);
  269. inc(optind);
  270. Internal_getopt:=#0;
  271. exit;
  272. end;
  273. end;
  274. { At this point we're at an option ...}
  275. nextchar:=2;
  276. if (longopts<>nil) and ((currentarg[2]='-') and
  277. (currentArg[1]='-')) then
  278. inc(nextchar);
  279. { So, now nextchar points at the first character of an option }
  280. end;
  281. { Check if we have a long option }
  282. if longopts<>nil then
  283. if length(currentarg)>1 then
  284. if ((currentarg[2]='-') and (currentArg[1]='-'))
  285. or
  286. ((not long_only) and (pos(currentarg[2],optstring)<>0)) then
  287. begin
  288. { Get option name }
  289. endopt:=pos('=',currentarg);
  290. if endopt=0 then
  291. endopt:=length(currentarg)+1;
  292. optname:=copy(currentarg,nextchar,endopt-nextchar);
  293. { Match partial or full }
  294. p:=longopts;
  295. pfound:=nil;
  296. exact:=false;
  297. ambig:=false;
  298. option_index:=0;
  299. indfound:=0;
  300. while (p^.name<>'') and (not exact) do
  301. begin
  302. if pos(optname,p^.name)<>0 then
  303. begin
  304. if length(optname)=length(p^.name) then
  305. begin
  306. exact:=true;
  307. pfound:=p;
  308. indfound:=option_index;
  309. end
  310. else
  311. if pfound=nil then
  312. begin
  313. indfound:=option_index;
  314. pfound:=p
  315. end
  316. else
  317. ambig:=true;
  318. end;
  319. inc(PByte(p),sizeof(toption)); //inc(pointer(p),sizeof(toption)); // for Delphi compatibility
  320. inc(option_index);
  321. end;
  322. if ambig and not exact then
  323. begin
  324. if opterr then
  325. writeln(argv[0],': option "',optname,'" is ambiguous');
  326. nextchar:=0;
  327. inc(optind);
  328. Internal_getopt:='?';
  329. end;
  330. if pfound<>nil then
  331. begin
  332. inc(optind);
  333. if endopt<=length(currentarg) then
  334. begin
  335. if pfound^.has_arg>0 then
  336. optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt)
  337. else
  338. begin
  339. if opterr then
  340. if currentarg[2]='-' then
  341. writeln(argv[0],': option "--',pfound^.name,'" doesn''t allow an argument')
  342. else
  343. writeln(argv[0],': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument');
  344. nextchar:=0;
  345. internal_getopt:='?';
  346. exit;
  347. end;
  348. end
  349. else { argument in next paramstr... }
  350. begin
  351. if pfound^.has_arg=1 then
  352. begin
  353. if optind<nrargs then
  354. begin
  355. optarg:=strpas(argv[optind]);
  356. inc(optind);
  357. end { required argument }
  358. else
  359. begin { no req argument}
  360. if opterr then
  361. writeln(argv[0],': option ',pfound^.name,' requires an argument');
  362. nextchar:=0;
  363. if optstring[1]=':' then
  364. Internal_getopt:=':'
  365. else
  366. Internal_getopt:='?';
  367. exit;
  368. end;
  369. end;
  370. end; { argument in next parameter end;}
  371. nextchar:=0;
  372. if longind<>nil then
  373. plongint(longind)^:=indfound+1;
  374. if pfound^.flag<>nil then
  375. begin
  376. pfound^.flag^:=pfound^.value;
  377. internal_getopt:=#0;
  378. exit;
  379. end;
  380. internal_getopt:=pfound^.value;
  381. exit;
  382. end; { pfound<>nil }
  383. { We didn't find it as an option }
  384. if (not long_only) or
  385. ((currentarg[2]='-') or (pos(CurrentArg[nextchar],optstring)=0)) then
  386. begin
  387. if opterr then
  388. if currentarg[2]='-' then
  389. writeln(argv[0],' unrecognized option "--',optname,'"')
  390. else
  391. writeln(argv[0],' unrecognized option "',currentarg[1],optname,'"');
  392. nextchar:=0;
  393. inc(optind);
  394. Internal_getopt:='?';
  395. exit;
  396. end;
  397. end; { Of long options.}
  398. { We check for a short option. }
  399. temp:=pos(currentarg[nextchar],optstring);
  400. c:=currentarg[nextchar];
  401. inc(nextchar);
  402. if nextchar>length(currentarg) then
  403. begin
  404. inc(optind);
  405. nextchar:=0;
  406. end;
  407. if (temp=0) or (c=':') then
  408. begin
  409. if opterr then
  410. writeln(argv[0],': illegal option -- ',c);
  411. optopt:=c;
  412. internal_getopt:='?';
  413. exit;
  414. end;
  415. Internal_getopt:=optstring[temp];
  416. if optstring[temp+1]=':' then
  417. if optstring[temp+2]=':' then
  418. begin { optional argument }
  419. if nextchar>0 then
  420. begin
  421. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  422. inc(optind);
  423. nextchar:=0;
  424. end else if (optind<>nrargs) then
  425. begin
  426. optarg:=strpas(argv[optind]);
  427. if optarg[1]='-' then
  428. optarg:=''
  429. else
  430. inc(optind);
  431. nextchar:=0;
  432. end;
  433. end
  434. else
  435. begin { required argument }
  436. if nextchar>0 then
  437. begin
  438. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  439. inc(optind);
  440. end
  441. else
  442. if (optind=nrargs) then
  443. begin
  444. if opterr then
  445. writeln (argv[0],': option requires an argument -- ',optstring[temp]);
  446. optopt:=optstring[temp];
  447. if optstring[1]=':' then
  448. Internal_getopt:=':'
  449. else
  450. Internal_Getopt:='?';
  451. end
  452. else
  453. begin
  454. optarg:=strpas(argv[optind]);
  455. inc(optind)
  456. end;
  457. nextchar:=0;
  458. end; { End of required argument}
  459. end; { End of internal getopt...}
  460. Function GetOpt(ShortOpts : String) : char;
  461. begin
  462. getopt:=internal_getopt(shortopts,nil,nil,false);
  463. end;
  464. Function GetLongOpts(ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  465. begin
  466. getlongopts:=internal_getopt(shortopts,longopts,@longind,true);
  467. end;
  468. {$ifdef FPC}
  469. initialization
  470. {$endif}
  471. {$ifndef FPC}
  472. {$ifdef TP}
  473. begin
  474. {$else}
  475. initialization
  476. {$endif}
  477. {$endif}
  478. { create argv if running under TP }
  479. {$ifndef FPC}
  480. setup_arguments;
  481. {$endif}
  482. { Needed to detect startup }
  483. Opterr:=true;
  484. Optind:=0;
  485. nrargs:=argc;
  486. end.