@@ -13,7 +13,7 @@ Copyright 2019-2022 Daniel T. Borelli <danieltborelli@gmail.com>
13
13
Copyright 2019 Jade Auer <jade@trashwitch.dev>
14
14
Copyright 2020 Sean Brennan <zettix1@gmail.com>
15
15
Copyright 2021 Christopher R. Nelson <christopher.nelson@languidnights.com>
16
- Copyright 2021-2022 Guilherme Janczak <guilherme.janczak@yandex.com>
16
+ Copyright 2021-2023 Guilherme Janczak <guilherme.janczak@yandex.com>
17
17
Copyright 2021 IFo Hancroft <contact@ifohancroft.com>
18
18
Copyright 2021 Peter Wu <peterwu@hotmail.com>
19
19
Copyright 2021 Wilson Smith <01wsmith+gh@gmail.com>
@@ -89,42 +89,57 @@ struct ScrotOptions opt = {
89
89
90
90
static void showUsage (void );
91
91
static void showVersion (void );
92
-
93
- int optionsParseRequiredNumber (const char * str )
92
+ static void optionsParseThumbnail (char * );
93
+
94
+ /* optionsParseNum: "string to number" function.
95
+ *
96
+ * Parses the string representation of an integer in str, and simultaneously
97
+ * ensures that it is >= min and <= max.
98
+ *
99
+ * Returns the integer and sets *errmsg to NULL on success.
100
+ * Returns 0 and sets *errmsg to a pointer to a string containing the
101
+ * reason why the number can't be parsed on error.
102
+ *
103
+ * usage:
104
+ * char *errmsg;
105
+ * unsigned int nonnegative;
106
+ * if ((nonnegative = optionsParseNum(optarg, 0, UINT_MAX, &errmsg)) == NULL)
107
+ * errx(EXIT_FAILURE, "-n: '%s' is %s", optarg, errmsg);
108
+ */
109
+ long long optionsParseNum (const char * str , long long min , long long max ,
110
+ const char * errmsg [static 1 ])
94
111
{
95
- assert (NULL != str ); // fix yout caller function,
96
- // the user does not impose this behavior
97
112
char * end = NULL ;
98
- long ret = 0L ;
99
- errno = 0 ;
100
-
101
- ret = strtol (str , & end , 10 );
102
-
103
- if (errno )
104
- goto range_error ;
105
-
106
- if (str == end )
107
- errx (EXIT_FAILURE , "the option is not a number: %s" , end );
108
-
109
- if (ret > INT_MAX || ret < INT_MIN ) {
110
- errno = ERANGE ;
111
- goto range_error ;
113
+ long long rval ;
114
+ int saved_errno = errno ;
115
+ if (str == NULL ) {
116
+ * errmsg = "missing" ;
117
+ return 0 ;
112
118
}
119
+ * errmsg = NULL ;
113
120
114
- return ret ;
115
-
116
- range_error :
117
- err (EXIT_FAILURE , "error strtol" );
118
- }
119
-
120
- static int nonNegativeNumber (int number )
121
- {
122
- return (number < 0 ) ? 0 : number ;
123
- }
121
+ errno = 0 ;
122
+ rval = strtoll (str , & end , 10 );
123
+ if (errno == ERANGE ) {
124
+ * errmsg = "not representable" ;
125
+ } else if (* str == '\0' ) {
126
+ * errmsg = "the null string" ;
127
+ } else if (* end != '\0' ) {
128
+ * errmsg = "not a number" ;
129
+ } else if (rval < min ) {
130
+ /*
131
+ * rval could be set to 0 due to strtoll() returning error and this
132
+ * could be smaller than min or larger than max. To make sure we don't
133
+ * return the wrong error message, put min/max checks after everything
134
+ * else.
135
+ */
136
+ * errmsg = min == 0 ? "negative" : "too small" ;
137
+ } else if (rval > max ) {
138
+ * errmsg = "too large" ;
139
+ }
140
+ errno = saved_errno ;
124
141
125
- int optionsParseRequireRange (int n , int lo , int hi )
126
- {
127
- return (n < lo ? lo : n > hi ? hi : n );
142
+ return (* errmsg ? 0 : rval );
128
143
}
129
144
130
145
static bool optionsParseIsString (const char * const str )
@@ -202,11 +217,11 @@ static void optionsParseSelection(const char *optarg)
202
217
errx (EXIT_FAILURE , "option --select: Invalid parameter." );
203
218
204
219
if (opt .selection .mode == SELECTION_MODE_BLUR ) {
205
- int const num = nonNegativeNumber ( optionsParseRequiredNumber ( value )) ;
206
-
207
- opt . selection . paramNum = optionsParseRequireRange ( num ,
208
- SELECTION_MODE_BLUR_MIN , SELECTION_MODE_BLUR_MAX );
209
-
220
+ const char * errmsg ;
221
+ opt . selection . paramNum = optionsParseNum ( value ,
222
+ SELECTION_MODE_BLUR_MIN , SELECTION_MODE_BLUR_MAX , & errmsg );
223
+ if ( errmsg )
224
+ errx ( EXIT_FAILURE , "option --select: '%s' is %s" , value , errmsg );
210
225
} else { // SELECTION_MODE_HIDE
211
226
212
227
checkMaxInputFileName (value );
@@ -236,6 +251,7 @@ static void optionsParseLine(char *optarg)
236
251
237
252
char * subopts = optarg ;
238
253
char * value = NULL ;
254
+ const char * errmsg ;
239
255
240
256
while (* subopts != '\0' ) {
241
257
switch (getsubopt (& subopts , token , & value )) {
@@ -255,16 +271,12 @@ static void optionsParseLine(char *optarg)
255
271
}
256
272
break ;
257
273
case Width :
258
- if (!optionsParseIsString (value )) {
259
- errx (EXIT_FAILURE , "Missing value for suboption '%s'" ,
260
- token [Width ]);
261
- }
262
-
263
- opt .lineWidth = optionsParseRequiredNumber (value );
264
-
265
- if (opt .lineWidth <= 0 || opt .lineWidth > 8 ) {
266
- errx (EXIT_FAILURE , "Value of the range (1..8) for "
267
- "suboption '%s': %d" , token [Width ], opt .lineWidth );
274
+ opt .lineWidth = optionsParseNum (value , 1 , 8 , & errmsg );
275
+ if (errmsg ) {
276
+ if (value == NULL )
277
+ value = "(null)" ;
278
+ errx (EXIT_FAILURE , "option --line: suboption '%s': '%s' is %s" ,
279
+ token [Width ], value , errmsg );
268
280
}
269
281
break ;
270
282
case Color :
@@ -293,11 +305,14 @@ static void optionsParseLine(char *optarg)
293
305
opt .lineMode = estrdup (value );
294
306
break ;
295
307
case Opacity :
296
- if (!optionsParseIsString (value )) {
297
- errx (EXIT_FAILURE , "Missing value for suboption '%s'" ,
298
- token [Opacity ]);
308
+ opt .lineOpacity = optionsParseNum (value ,
309
+ SELECTION_OPACITY_MIN , SELECTION_OPACITY_MAX , & errmsg );
310
+ if (errmsg ) {
311
+ if (value == NULL )
312
+ value = "(null)" ;
313
+ errx (EXIT_FAILURE , "option --line: suboption %s: '%s' is %s" ,
314
+ token [Opacity ], value , errmsg );
299
315
}
300
- opt .lineOpacity = optionsParseRequiredNumber (value );
301
316
break ;
302
317
default :
303
318
errx (EXIT_FAILURE , "No match found for token: '%s'" , value );
@@ -378,6 +393,7 @@ void optionsParse(int argc, char *argv[])
378
393
{ 0 , 0 , 0 , 0 }
379
394
};
380
395
int optch = 0 , cmdx = 0 ;
396
+ const char * errmsg ;
381
397
382
398
/* Now to pass some optionarinos */
383
399
while ((optch = getopt_long (argc , argv , stropts , lopts , & cmdx )) != EOF ) {
@@ -394,7 +410,11 @@ void optionsParse(int argc, char *argv[])
394
410
opt .border = 1 ;
395
411
break ;
396
412
case 'd' :
397
- opt .delay = nonNegativeNumber (optionsParseRequiredNumber (optarg ));
413
+ opt .delay = optionsParseNum (optarg , 0 , INT_MAX , & errmsg );
414
+ if (errmsg ) {
415
+ errx (EXIT_FAILURE , "option --delay: '%s' is %s" , optarg ,
416
+ errmsg );
417
+ }
398
418
break ;
399
419
case 'e' :
400
420
opt .exec = estrdup (optarg );
@@ -403,7 +423,11 @@ void optionsParse(int argc, char *argv[])
403
423
opt .multidisp = 1 ;
404
424
break ;
405
425
case 'q' :
406
- opt .quality = optionsParseRequiredNumber (optarg );
426
+ opt .quality = optionsParseNum (optarg , 1 , 100 , & errmsg );
427
+ if (errmsg ) {
428
+ errx (EXIT_FAILURE , "option --quality: '%s' is %s" , optarg ,
429
+ errmsg );
430
+ }
407
431
break ;
408
432
case 's' :
409
433
optionsParseSelection (optarg );
@@ -458,7 +482,11 @@ void optionsParse(int argc, char *argv[])
458
482
optionsParseFileName (optarg );
459
483
break ;
460
484
case 'M' :
461
- opt .monitor = nonNegativeNumber (optionsParseRequiredNumber (optarg ));
485
+ opt .monitor = optionsParseNum (optarg , 0 , INT_MAX , & errmsg );
486
+ if (errmsg ) {
487
+ errx (EXIT_FAILURE , "option --monitor: '%s' is %s" , optarg ,
488
+ errmsg );
489
+ }
462
490
break ;
463
491
case '?' :
464
492
exit (EXIT_FAILURE );
@@ -480,7 +508,7 @@ void optionsParse(int argc, char *argv[])
480
508
free (opt .outputFile );
481
509
opt .outputFile = getPathOfStdout ();
482
510
opt .overwrite = 1 ;
483
- opt .thumb = 0 ;
511
+ opt .thumbWorP = 0 ;
484
512
}
485
513
} else
486
514
warnx ("unrecognised option %s" , argv [optind ++ ]);
@@ -533,24 +561,30 @@ char *optionsNameThumbnail(const char *name)
533
561
void optionsParseAutoselect (char * optarg )
534
562
{
535
563
char * token ;
536
- const char tokenDelimiter [ 2 ] = "," ;
537
- int dimensions [ 4 ] ;
564
+ int * dimensions [ ] = { & opt . autoselectX , & opt . autoselectY , & opt . autoselectW ,
565
+ & opt . autoselectH , NULL /* Sentinel. */ } ;
538
566
int i = 0 ;
567
+ int min ;
568
+ const char * errmsg ;
569
+
570
+ /* Geometry dimensions must be in format x,y,w,h */
571
+ token = strtok (optarg , "," );
572
+ for (; token != NULL ; token = strtok (NULL , "," )) {
573
+ if (dimensions [i ] == NULL )
574
+ errx (EXIT_FAILURE , "option --autoselect: too many dimensions" );
575
+
576
+ min = i >= 2 ; /* X,Y offsets may be 0. Width and height may not. */
577
+ * dimensions [i ] = optionsParseNum (token , min , INT_MAX , & errmsg );
578
+ if (errmsg ) {
579
+ errx (EXIT_FAILURE , "option --autoselect: '%s' is %s" , token ,
580
+ errmsg );
581
+ }
582
+ i ++ ;
539
583
540
- if (strchr (optarg , ',' )) { /* geometry dimensions must be in format x,y,w,h */
541
- dimensions [i ++ ] = optionsParseRequiredNumber (strtok (optarg , tokenDelimiter ));
542
- while ((token = strtok (NULL , tokenDelimiter )))
543
- dimensions [i ++ ] = optionsParseRequiredNumber (token );
544
584
opt .autoselect = 1 ;
545
- opt .autoselectX = dimensions [0 ];
546
- opt .autoselectY = dimensions [1 ];
547
- opt .autoselectW = dimensions [2 ];
548
- opt .autoselectH = dimensions [3 ];
549
-
550
- if (i != 4 )
551
- errx (EXIT_FAILURE , "option 'autoselect' require 4 arguments" );
552
- } else
553
- errx (EXIT_FAILURE , "invalid format for option -- 'autoselect'" );
585
+ }
586
+ if (i < 4 )
587
+ errx (EXIT_FAILURE , "option --autoselect: too few dimensions" );
554
588
}
555
589
556
590
void optionsParseDisplay (char * optarg )
@@ -560,33 +594,33 @@ void optionsParseDisplay(char *optarg)
560
594
err (EXIT_FAILURE , "Unable to allocate display" );
561
595
}
562
596
563
- void optionsParseThumbnail (char * optarg )
597
+ static void optionsParseThumbnail (char * optarg )
564
598
{
565
- char * token ;
599
+ char * height ;
600
+ const char * errmsg ;
601
+
602
+ if ((height = strchr (optarg , 'x' )) != NULL ) { /* optarg is a resolution. */
603
+ /* optarg holds the width, height holds the height. */
604
+ * height ++ = '\0' ;
566
605
567
- if (strchr (optarg , 'x' )) { /* We want to specify the geometry */
568
- token = strtok (optarg , "x" );
569
- opt .thumbWidth = optionsParseRequiredNumber (token );
570
- token = strtok (NULL , "x" );
571
- if (token ) {
572
- opt .thumbWidth = optionsParseRequiredNumber (optarg );
573
- opt .thumbHeight = optionsParseRequiredNumber (token );
574
-
575
- if (opt .thumbWidth < 0 )
576
- opt .thumbWidth = 1 ;
577
- if (opt .thumbHeight < 0 )
578
- opt .thumbHeight = 1 ;
579
-
580
- if (!opt .thumbWidth && !opt .thumbHeight )
581
- opt .thumb = 0 ;
582
- else
583
- opt .thumb = 1 ;
606
+ opt .thumbWorP = optionsParseNum (optarg , 1 , INT_MAX , & errmsg );
607
+ if (errmsg ) {
608
+ errx (EXIT_FAILURE , "option --thumb: resolution width '%s' is %s" ,
609
+ optarg , errmsg );
584
610
}
585
- } else {
586
- opt .thumb = optionsParseRequireRange (
587
- optionsParseRequiredNumber (optarg ), 1 , 100 );
588
- }
589
611
612
+ opt .thumbH = optionsParseNum (height , 1 , INT_MAX , & errmsg );
613
+ if (errmsg ) {
614
+ errx (EXIT_FAILURE , "option --thumb: resolution height '%s' is %s" ,
615
+ height , errmsg );
616
+ }
617
+ } else { /* optarg is a percentage. */
618
+ opt .thumbWorP = optionsParseNum (optarg , 1 , INT_MAX , & errmsg );
619
+ if (errmsg ) {
620
+ errx (EXIT_FAILURE , "option --thumb: percentage '%s' is %s" , optarg ,
621
+ errmsg );
622
+ }
623
+ }
590
624
}
591
625
592
626
void optionsParseFileName (const char * optarg )
0 commit comments