getopts.pp 13 KB

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