getopts.pp 13 KB

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